[Houdini] Minutes Seattle F2F 2017-01-10 Part III: Typed OM, Custom Properties

=================================================
   These are the official Houdini Task Force
     minutes. Unless you're correcting the
      minutes, please respond by starting
 a new thread with an appropriate subject line.
=================================================


Typed OM
--------

  - RESOLVED: CSSFooValue objects are mutable
  - RESOLVED: Move DOMMatrixReadOnly out of CSSTransformComponent to
              CSSMatrixComponent (and make mutable)
  - RESOLVED: Add an update() method to styleMap
      - TabAtkins will write a mail about if updateAll() is also
          necessary.
  - RESOLVED: Canonicalize by representing all values, and serialize
              to the most-backwards-compatible minimal
              representation to be mostly compatible with old CSSOM
              (minus some idiosyncrasies), and to gradually bring
              old CSSOM in line with new consistent serialization
              rules.

Custom Properties
-----------------

  - RESOLVED: When you register a property that makes existing value
              invalid, we throw out the invalid value.
  - RESOLVED: Typed custom properties do not trigger fallback.
  - RESOLVED: Registering types for a property therefore does not
              force reparse.
  - RESOLVED: When typed custom properties fail, they fall back to
              their initial value not to invalid.
  - RESOLVED: Invalid argument for paint function produces an
              invalid image, does not trigger fallback or anything
              like that.
  - The previous four resolutions need author feedback.

===== FULL MINUTES BELOW ======

Agenda: https://github.com/w3c/css-houdini-drafts/wiki/Seattle-F2F-Jan-10-2017

Scribe: fantasai

Typed OM
========

Mutability
----------

  shane: First topic is revisiting mutability.
  shane: We resolved in the last meeting that all of the objects in
         the typed OM would be immutable.
  shane: I've since have implementation feedback from engineers in
         Chrome who are building this that that's not great for
         performance
  shane: Specific example of where it's really bad.
  shane: Specific ex is, you've built up a complex transform. Just
         want to modify a small part of that frame by frame:
  shane writes:
        new CSSTransformValue(
            [new CSSTranslationComponent(...), ...
            new CSSRotationComponent(new CSSAngleValue(5, 'deg')),
            ...., ]);
  fantasai: wow, that is so much writing....
  shane: The problem here is that if you do this on every frame,
         you're constructing a whole bunch of objects that you then
         throw away, repeat, repeat, repeat
  shane: Doing a lot of object creation and cleanup on every frame,
         which is excessive work.
  shane: If this was mutable, though, you could then just modify the
         rotation value like: t[3].angle.value +=5;
  shane: You just mutate the one piece you're changing.
  shane: Given the whole point of this API is high performance, it
         seems important that we actually make it high performance =)

  Rossen: Why did we make it immutable?
  esprehn: For confusion reasons.
  esprehn: Today you do element.style.color = ...
  esprehn: Concern that people would do
           element.style.styleMap.transform and then call a method
           on it
  esprehn: And then authors would be confused because that's a copy,
           not the actual thing
  esprehn: But that was maybe preemptive, maybe not really a problem.
  shane: We weren't sure coming into the meeting.
  shane: We tossed it around, there weren't strong reasons on either
         side. We ended up on immutable.
  shane: I think this is a strong reason to flip back to mutable.

  smfr: Can you write down the mistake that elliot brought up?
  <esprehn> ex. element.styleMap.get("transform").rotate(32, 'deg')
  <esprehn> nothing happens to the element there
  <esprehn> you can alternate mutating the object and setting it
  <esprehn> ex. to place objects you could create a transform, and
            adjust it on the Y axis as you assign the style of each
            item in the list

  TabAtkins: The mistake is that you have to set the object back.
  smfr: Can replace the whole object, have to get it , modify it,
        and then set it?
  shane: We want to make an upgrade method to make this easier as
         well.
  <iank> document.body.styleMap.update('width', w => w.value += 5)
  surma: So want to make it mutable but not live.
  shane: Right.

  plinss: My only concern is that we see all three patterns all over
          the platform, and you never know what you're going to get.
          Would be nice if we could be consistent with ourselves.
  Rossen: Do we have a definition of what we need to be consistent
          with?
  plinss: No
  Rossen: That's another topic for the TAG then.

  smfr: Is this proposal to make all the CSSFooValue things mutable?
  shane: Yes.
  smfr: Would CSSTransformValue have a matrix?
  shane: DOMMAtrix is a view of the transform that you've set.
  smfr: You'd have to set a whole new matrix if you want to change
        it.
  smfr: Oh, you're assigning angle there.
  smfr: You can set a matrix Component, and then alter that. But
        DOMMatrix is the matrix of the whole thing.
  smfr: It inherits matrix from the base class?
  shane: We'll have DOMMatrix and then... will need to work out.
  shane: I think we need to remove the matrix component from the
         matrix superclass
  shane: I don't think I'm hearing strong disagreement...
  Rossen: So first question is whether or not to roll back decision
          to make this immutable.
  Rossen: Any objections?

  RESOLVED: CSSFooValue objects are mutable

  Rossen: Next issue is moving the DOMMatrixReadOnly out of
          CSSTransformComponent
  Rossen: to CSSMatrixComponent.
  shane: And making it mutable.

  RESOLVED: Move DOMMatrixReadOnly out of CSSTransformComponent to
            CSSMatrixComponent (and make mutable)

  Rossen: Do we need any other changes?
  fremy: Adding update()?

  RESOLVED: Add an update() method to styleMap

  shane: We might need update() and updateAll()
  Rossen: How is set() different from update()?
  shane: set() takes a value whereas update() takes a function.
  TabAtkins: This is supposed to look like a map. If you're
             mysteriously missing standard map methods, that's not
             good.
  iank: What if you try to update a thing that doesn't exist?
  shane: If you call update on a custom property that hasn't been
         set, then the input the update function is whatever the
         representation of a custom property's undefined value
  esprehn: ES map needs update function
  esprehn: In C++ you get entry in map and you can do one hash
           lookup instead of two. In JS you have to get and set, so
           it's two hash lookups.
  esprehn: It's supported by underlying platforms everywhere.
  slightlyoff: send mail?
  ACTION TabAtkins Send mail

  Rossen: There's bunch of open issues, but nothing happening.
  shane: I have to apologize, I was supposed to get some stuff done
         by now, but had some exceptional circumstances in Sydney
  shane: This year I started back up on this, been working on
         canonicalization.
  Rossen: Just wanted to see if we could help with these issues.
  shane: They're fairly mechanical afaict.

Canonicalization
----------------

  shane: Do want to go over the canonicalization, though.
  <nainar> Canonicalization is
https://github.com/w3c/css-houdini-drafts/issues/268
  shane: I made a spreadsheet of all the different CSS properties.
  shane: Trying to classify them all into different canonicalization
         approaches.
  shane: We talked about how the properties in the typed OM are
         serialized.
  shane: Complication of having values not going through string
         representation.
  shane: We decided we needed a canonical serialization for every
         property.

  fantasai: Didn't we decide we needed that a long time ago, added a
            field to the propdef table?
  dbaron: The CSSOM spec refers to canonical order a few times, but
          only for shorthands.

  shane: Also need to address e.g. colors are canonicalized into rgb.
  shane: Going through property by property
  shane: Looking at syntaxes and classifying into groups.
  shane: For the ones I've done, there's a small set of groups.
  shane: Some things are just a choice of keywords, no need to
         canonicalize
  shane: things which are set of keywords, e.g. flex-flow.
  shane: Need to fill in missing productions
  shane: e.g. flex-flow, you need a flex direction and a flex wrap.
  shane: But if you have a background, you don't want to necessarily
         fill in the missing pieces for bits of background that
         exist.
  shane: Can completely understand what background: red does without
         filling in the rest.
  shane: Some sets fill in missing pieces, others don't.

  fantasai: Do we want to have that distinction? why not follow
            shortest-output rule?
  shane: The typed OM representation should be able to read both of
         these.
  fantasai: Wouldn't that be the case for backgrounds as well?
  fantasai: There are two types of things here, e.g. flex-flow (like
            background), where an omitted value can also be present
            -- there is an explicit way to represent a missing value.
  fantasai: The there's things like text-decoration, where the a
            missing keyword represents a particular state which
            cannot be represented explicitly: the presence or
            absence of the keyword indicates state, as opposed to
            flex-flow where we have two keywords that represent the
            two states and one of them is merely the default.
  fantasai: Background is the same as flex-flow: you can explicitly
            represent the missing values.
  fantasai: Why would you treat one different than the other?
  fantasai: You're saying that the omitted values aren't
            interesting, but that's an opinion, it's not a
            structural difference as between text-decoration and
            flex-flow.
  shane: The background shorthand has two subcomponents, it's got
         background-color and then it has stack of images.
  shane: If it has no images, then the rest of it isn't interesting.
  shane: There's two parts: the color and the stack of images.
  dbaron: But that stack has a length of at least one.
  dbaron: Because originally there wasn't a stack, there was just
          one image.

  Scribe: nainar

  fantasai: We have had people suggest that we need a color on every
            layer - not an assumption you want to make.
  TabAtkins: We have that built into the image() function..
  fantasai: We don't make a special case where background is ... I'm
            not sure. You should be able to read out that
            background-image is a null value.
  zcorpan: Is it possible to have none? Images in the spec along
           with other image.
  fantasai: Yes.
  fantasai: If you want just the color, read out backgroundColor.
  shane: I'm not sure - if background is a complicated thing -
         people will default to using color.

  Scribe: fantasai

  TabAtkins: We're hitting several concepts here.
  TabAtkins: One is canonicalization of typed OM stuff.
  TabAtkins: The other thing is serialization.
  TabAtkins: We do try to have serialization obey the
             minimal-representation thing.
  TabAtkins: If you only have a background color, you get
             'background: red', 'background: repeat none 0 0 red'
             etc.
  TabAtkins: We need to define serialization.
  dbaron: There were a bunch of vague principles.
  dbaron: One is minimal representation, other is oldest form (for
          most compat) -- usually but not always the same.
  dbaron: Shortest in terms of fewest components, not fewest
          characters!
  zcorpan: CSSOM spec tries to define how serialization should work.
           I think the algorithm is broken in some ways.
  zcorpan: Tries to omit components that are the initial value, if
           it can be serialized that way.
  shane: Gets very tricky with e.g. flex shorthand.
  TabAtkins: Reason we have to do this, a bunch of properties esp.
             shorthands, have values can be easily represented in
             typed OM.
  TabAtkins: Using string access right now, want compatible way to
             expose the string value.
  TabAtkins: Without explicit guiding principles, was tasked with
             writing out serialization rules for al properties.

  shane: Flex is interesting because of 4 special values.
  shane: auto | initial | none | <integer>
  TabAtkins: It's not weird, it's pretty standard.
  TabAtkins: Auto is just a flex basis.
  TabAtkins: The expansion rules are a bit special, they don't just
             fill in the initial values always.
  TabAtkins: We have some keywords that are called out as especially
             useful.
  shane: If we have a constructed flex object, which matches one of
         these keywords that are called out
  shane: With flex-grow of 5 and flex-shrink of 1 and flex-basis of
         0.
  fantasai: That would serialize as 'flex: 5'
  TabAtkins: Being the minimal representation.
  TabAtkins: This is different from a canonical data structure.

  shane: There may be cases where the canonicalization ...
  shane: might be cases where canonicalization gets you something
         that doesn't serialize out a minimal representation.
  TabAtkins: Why can you not produce the same string again?
  shane: e.g. top 50% right 50%
  shane: When we canonicalize, it's a pair of lengths. When you
         re-serialize out...
  fantasai: You get 50% 50% -
  fantasai: minimal representation.
  shane: So we canonicalize by representing all values, and
         serialize to the minimal representation.
  TabAtkins: Yeah.
  shane: Canonicalization fills out all the missing pieces.

  gregwhitworth: What is use case where someone asks for shorthand,
                 then returns a string.
  TabAtkins: The use case right now is that typed OM object patterns
             don't currently have ability to capture all property
             patterns, esp for shorthands which are relatively
             complex.
  TabAtkins: For those, or for custom properties, we need to expose
             the values somehow. And that's a string.
  TabAtkins: We need to define what the string is.
  TabAtkins: We'd like to get this fixed so everyone is consistent.
  TabAtkins: We want typed OM to represent all properties with
             objects eventually
  TabAtkins: But right now trying to hit 80% case.
  gregwhitworth: [...]

  gregwhitworth: Should we maybe say V1 we don't handle shorthands?
  TabAtkins: We decided at TPAC we need to have shorthands defined.
  TabAtkins: That was one of dbaron's conditions.
  dbaron: I'm not sure minimal representation is web compatible.
  TabAtkins: We have ability to do it new and nice.
  <gregwhitworth> I'm primarily saying that most use cases for
                  shorthands as an author, they'll then parse out to
                  the longhands. So is it worth spending time on
                  serializing to strings or could we focus on
                  building out the shorthand typedOM models for the
                  shorthands.

  fantasai: Have to consider future compat, so will need to have a
            "most compatible form" rule going forward.
  zcorpan: If you only care about defining serialization for typed
           OM, seems like a missed opportunity to achieve interop
           for old CSSOM.
  zcorpan: Seems like roughly same amount of work to define them to
           be the same serialization.
  shane: Those incompatible serializations have been shipped already.
  zcorpan: But we should try to get interop on those too.
  zcorpan: So pick the most web-compatible solution and define that
           as the way forward for both.
  zcorpan: Instead of defining new rules and leaving old CSSOM stuff.
  shane: I think we should have a consistent set of rules, and maybe
         we can move CSSOM towards that gradually.
  fantasai: Be consistent with the old OM rules even if they are not
            the most ideal ones.
  fantasai: So that you can keep as much compat with the two systems
            as possible.
  fantasai: Just drop the really weird idiosyncracies.
  fantasai: From the new OM, and then maybe gradually reduce those
            in the old OM.
  fantasai: Is the rule about most-backwards-compatible minimal
            representation in the CSSOM spec?
  zcorpan: Minimal representation is, compatible isn't.
  fantasai: Probably should put that in the spec then, since that's
            the actual principle.

  RESOLVED: Canonicalize by representing all values, and serialize
            to the most-backwards-compatible minimal representation
            to be mostly compatible with old CSSOM (minus some
            idiosyncrasies), and to gradually bring old CSSOM in
            line with new consistent serialization rules.

  break: lunch

Custom Properties
=================

  [shane writes styleMap.set('--foo', 'invalid')]
  shane: Then register --foo as a color.
  shane: That's not a valid color.
  shane: If you register it as a color first, you get an exception.
  shane: You get a bit of raciness.
  TabAtkins: It's not racy unless you do racy things, but it's order
             dependent.
  TabAtkins: Could make it not order-dependent by making .set
             silently.
  astearns: First ordering trying to solve, setting something don't
            know what grammar applies, then using it in a context
            where grammar applies then gets thrown out.
  shane: Currently if you have a grammar and you set an invalid
         thing, an exception is raised.
  shane: If you set something and then register the grammar, don't
         get the exception.
  esprehn: Do we want it to throw an exception?
  shane: Yes.
  esprehn: That doesn't match the CSS fallback pattern.
  esprehn: Where you set multiple property declaration and the
           latest one that is valid wins.
  shane: This helps catch mishandling problems.
  shane: Much most JS-y
  shane: which makes sense because it's a JS API.

  esprehn: For unregistered case you want it to hold as a pending
           value?
  shane: It's a valid value until you register a type.
  TabAtkins: The question is, is it OK if this is order dependent?
             If not, what do we do about it?
  TabAtkins: Could make .set silently fail, probably not great.
  TabAtkins: Alternately make the registration throw.
  esprehn: That requires synchronously computing registration.
  shane: That's bad.

  shane: If you read --foo at this point what happens?
  TabAtkins: You recalc, and that results in throwing out the
             invalid value.
  shane: Other option is having explicit invalid value
         representation.
  TabAtkins: You don't get that if you set 'background' to 'invalid'
  zcorpan: What's the use case for setting a property without
           setting the grammar?
  TabAtkins: That's just a normal custom property.
  shane: This is more likely to be a conflict of two libraries than
         something intentional.
  fremy: What if it works, but did nothing?
  fremy: Set the value as an unparsed value
  fremy: when browser tries to use the declaration
  fremy: like if you did what is in your style sheet, it's still
         there in your style sheet but isn't there anymore.
  TabAtkins: It's same as 'background: invalid'--effectively thrown
             away during parsing. It's in your stylesheet, but not
             in the OM.
  ...
  TabAtkins: If you set style attribute, parsing still happens and
             we throw out the invalid stuff.

  kbabbitt: What happens if I subsequently unregister foo?
  shane: We resolved to remove unregistration at TPAC.
  shane: Not just this reason, but b/c complicated to support and
         didn't have use cases. Fortunately don't need to worry
         about that.
  fantasai: I think it's fine to not worry about this case, behavior
            seems reasonable and expected.
  fantasai: Could possibly throw an exception at registration, but
            likely to be more annoying than helpful.
  shane: It's also very expensive.

  RESOLVED: When you register a property that makes existing value
            invalid, we throw out the invalid value.

  shane: Let's say I have in a style sheet ....
         ---foo: red;
         --foo: invalid;
  shane: This is in a style sheet, and foo is registered as a color.
  shane: We don't fall back?
  TabAtkins: Of course we fall back. we know the grammar.
  TabAtkins: Once we know the grammar, can treat it as a property we
             know how to parse.
  shane: Might be some raciness.
  TabAtkins: But when you register you reparse the style sheet.
  TabAtkins: That's what needs to happen to make it consistent with
             the rest of CSS.
  TabAtkins: If we said you don't need to reparse the style sheets,
             you get the behavior where that invalid is used,
             treated as invalid, becomes
             invalid-at-computed-value-time
  ...
  esprehn: You have to reprocess the entire property set.
  shane: The behavior with not falling back is consistent with
         untyped properties.
  TabAtkins: Because it is literally impossible to know what the
             value space of a custom property is.
  esprehn: But reprocessing the style sheet on registration is very
           expensive.
  iank: What shane is proposing is that last value always wins, no
        fallback.

  shane: Even if we know that foo is typed as a color, I don't think
         we can deal with this case:
         color: red;
         color: var(--foo);
  shane: We can't fall back in that case.
  TabAtkins: At parsing time we don't know the value of --foo.
  TabAtkins: Here is where even typed custom properties behave same
             as ...
  TabAtkins: We could fall back there if we knew that --foo is typed
             as other than a color.
  shane: ...
  TabAtkins: Typed properties have an initial value that matches
             their type.
  TabAtkins: If --foo is typed as a color, you are guaranteed that
             --foo will give you a color.
  shane: No, --foo could still be invalid because of loops.
  TabAtkins: Loops just need to reset all the involved properties to
             something that is no longer referencing anything.
  TabAtkins: If you are a typed property, you have an initial value,
             we can use that. No reason to use invalid as a value
             there.
  shane: I think Tim ran into problems when he tried to do that.
  TabAtkins: There's no dependency value in falling back to initial.
  TabAtkins: Pretty sure we don't need to accept invalid as a bottom
             value.
  TabAtkins: Thus should be guaranteed that a typed property always
             resolves to given type.

  shane: So if foo in this case referenced another untyped custom
         property?
  TabAtkins: Either rando is a color, and parses correctly, or --foo
             itself will note that it's not got a color and will
             fall back.
  shane: Two scenarios on the table:
  shane: One is that when we register a custom property, we reparse
         the world.
  shane: In that scenario we can fall back on typed custom
         properties, and on typed vars in typed properties.
  shane: In other scenario we don't need to reparse the world, but
         never get fallback.
  shane: Typed custom properties behave like untyped custom
         properties.

  kbabbitt: Suppose I have property --foo registered as <length>+
            and I say margin: var(--foo);
  kbabbitt: Since I don't know the length of the list, so can't tell
            if it's valid.
  TabAtkins: It's not a valid type match: you'd need <length>{1,4}
  <TabAtkins> (The general case for type-matching is actually rather
              difficult; a --prop registered as <length>{1,2} should
              theoretically work in "margin: 1px var(--prop) 2px;",
              but hoo-boy that's making parsing more complicated.)

  iank: Once subtle thing:
  <iank> https://gist.github.com/bfgeek/460c21569776f75fb707fa8d20419dbe
  iank: Arguments for custom paint has similar problem.
  iank: Say we specify input items
  iank: Initially this property would be invalid:
        <style>
            .painting { background-image: paint(typed, 10px);
            background-image: paint(typed, color);
            }
        </style>
        <script>
            registerPaint('typed', class { static inputArguments = ['
            <length>']; // etc.
        });
  iank: What behavior do we want for input arguments?
  iank: Here we've registered custom paint argument of type length.
  iank: I'd argue that a paint function can never be invalid [unless
        it's empty paint()].
  TabAtkins: The main reason we have fallback is because we know we
             are going to make changes in the future.
  TabAtkins: With custom properties, you don't have that problem --
             you know what's supported because it's what's in the
             library.
  TabAtkins: So while consistency with CSS is nice
  TabAtkins: fallback is not so critical.
  esprehn: The use case is someone publishes a component that is
           compatible with two different versions of a library.
  TabAtkins: Wouldn't it be better to have two different style
             sheets, one per version? Avoids sending over duplicated
             data.
  TabAtkins: You can't do that with normal CSS.
  esprehn: So send different style sheet per UA version?
  TabAtkins: You can't predict that and don't have control over it.
  gregwhitworth: ...
  TabAtkins: Case is API changing out from under you, without your
             control. That's essential what browsers do.
  TabAtkins: But libraries aren't browsers. You control your
             dependencies.
  ....
  esprehn: There's an ancient spec from hixie that introduces a
           dependency attribute on scripts and styles and stuff.
  esprehn: I think simplifying this make sense. You control your
           dependencies. Don't go to /latest
  esprehn: If you link to jquery/latest you're probably broken.
  zcorpan: Are we saying that we don't want people to use /latest
           for libraries in general?
  TabAtkins: Should specify version ranges that you know work.
  <TabAtkins> (This is already best-practice in Node/etc)

  shane: What we're saying is don't reparse stylesheets when
         registering custom properties or paints.
  TabAtkins: And don't trigger parse-time fallback, in general.
  TabAtkins: When they do fail, they should fall back to their
             initial value if they're typed. Not to invalid.
  fremy: Let's say browser supports new color type.
  fremy: And you want your custom property to use the new color type
         if available in the browser.
  shane: Use @supports
  fantasai: I think conclusions here make sense, but should get some
            feedback from authors as well.

  [Following resolutions need to request author feedback...]
  RESOLVED: Typed custom properties do not trigger fallback.
  RESOLVED: Registering types for a property therefore does not
            force reparse.
  RESOLVED: When typed custom properties fail, they fall back to
            their initial value not to invalid.
  RESOLVED: Invalid argument for paint function produces an invalid
            image, does not trigger fallback or anything like that.
<div id="DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2"><br />
<table style="border-top: 1px solid #D3D4DE;">
 <tr>
        <td style="width: 55px; padding-top: 13px;"><a
href="https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=icon"
target="_blank"><img
src="https://ipmcdn.avast.com/images/icons/icon-envelope-tick-round-orange-animated-no-repeat-v1.gif"
width="46" height="29" style="width: 46px; height: 29px;" /></a></td>
  <td style="width: 470px; padding-top: 12px; color: #41424e;
font-size: 13px; font-family: Arial, Helvetica, sans-serif;
line-height: 18px;">Virus-free. <a
href="https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=link"
target="_blank" style="color: #4453ea;">www.avast.com</a>
  </td>
 </tr>
</table><a href="#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2" width="1"
height="1"></a></div>

Received on Sunday, 5 March 2017 16:01:31 UTC