[Houdini] Minutes Lyon F2F 2018-10-25 Part II: Typed OM, Layout API

=================================================
   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
--------

  - Resolving values with external information (Issue #798) needs more
      time to develop a full proposal.
  - The plan is to drop CSSPositionValue from the first level unless a
      solution is found for representing positions. (Issue #817)
  - A single value when a list is appended will be handled by silently
      dropping the single value and making it a list. (Issue #823)
  - RESOLVED: Make TypedOM compatible with future Array-like plans,
              per issue #825.

Layout API
----------
  - There were concerns that the solution for float representation and
      rounding to fractional units could be fragile and have problems,
      for example, due to differences in rounding and representation
      within the layout engine or pixel-snapping (engines try to ensure
      certain lengths, such as border widths, resolve to the same
      number of pixels given the same abstract length). Google will
      experiment with their implementation to see if there are
      problems. (Issue #830)

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

Agenda: https://github.com/w3c/css-houdini-drafts/wiki/TPAC-F2F-October-2018

Scribe: heycam

Typed OM
========

Status Update
-------------
  https://github.com/w3c/css-houdini-drafts/issues?utf8=%E2%9C%93&q=+is%3Aissue+label%3AAgenda%2B++sort%3Aupdated-desc

  TabAtkins: I want to get a bunch more work on this done before next
             meeting
  TabAtkins: More significant updates in the near future
  TabAtkins: We have some impl internally again, brought up some
             interesting issues

Resolve values to a px with external information
------------------------------------------------
  https://github.com/w3c/css-houdini-drafts/issues/798

  TabAtkins: If all of the units you have in a math value are
             compatible with pixels e.g. then you can always resolve
             them to a single px
  TabAtkins: If you have combinations of values that aren't directly
             resolvable, there's no way to resolve them
  TabAtkins: since the custom layout API works mostly in px values, it
             would be useful to give people more direct control over
             how they can simplify them down to px
  <TabAtkins> https://github.com/w3c/css-houdini-drafts/issues/798#issuecomment-415555133
  TabAtkins: So we add an options bag to the to() and toSum() functions
  TabAtkins: giving extra information for resolving the values
  TabAtkins: In particular, the element being resolved on (for ems
             etc.), the property it's on (so font-size can properly
             resolve to parent element), and what a % should resolve to
  TabAtkins: Going with this explicit % resolution thing to avoid some
             problems with some properties resolving %s against
             different sizes
  TabAtkins: This makes it more explicit with cases like the layout
             API, where you have an available space, and you can just
             pass that in rather than finding a property to pretend
             it's being resolved as
  TabAtkins: I think that lets us resolve all the units
  TabAtkins: In particular, a comment further down suggests that we
             don't have a default value, so if you don't pass an
             element, resolve like MQs do, with no context
  TabAtkins: Right now, if you call to() without this information and
             it needs it, it throws

  emilio: The layout API doesn't have access to any element, right?
  TabAtkins: Yes
  emilio: Can we instead pass what the function depends on explicitly?
          what's the expected thing if you pass an element to there, I
          assume you have to do style updates etc.?
  emilio: What happens if that element's not in the flat tree etc.?
  emilio: Would be nice to be more explicit about which data you're
          depending on
  emilio: then people can use the no context thing without having magic
  TabAtkins: There's a lot of information. which font's being used.
             it's possible to enumerate this
  emilio: Instead of getting a font and doing font matching, you may
          as well .... I guess there's no way to get ex height without
          script
  TabAtkins: A possibility is to allow you to pass an element, but
             also enumerate all the things that the various relative
             units can depend on
  TabAtkins: and if you don't pass a unit, that's the info you have to
             work with
  TabAtkins: and if you use a unit but no way to determine what that
             is, continue to throw
  emilio: If you need to download fonts to make this work...
  TabAtkins: That's what we have to do
  emilio: Normally you wait for a computed value, which triggers all
          that
  emilio: That only happens if you actually lay out the element
  TabAtkins: And the assumption here is you are doing something
             layout-y but not with an element
  emilio: Let's say you call this function from a layout worklet with
          a given font
  TabAtkins: Does the layout trigger the font load somewhere?
  TabAtkins: Good question
  TabAtkins: Possibly
  emilio: Does it do it sync? probably not
  emilio: You can't make it sync
  TabAtkins: I think then we should remove the element resolver,
             figure out what relative units we can easily resolve on,
             e.g. asking for font-size, other font relative ones are a
             bit hard for now
  TabAtkins: and then if you try to use an ex unit to px, we just throw
  TabAtkins: Then we can remove the property too
  fremy: If you still want the behavior to pass an element, you can
         have another element, and it returns the properties you would
         fill in
  TabAtkins: One of those dictionaries

  dbaron: I'm not convinced you can remove the property
  dbaron: If you're doing it from the element, you still need to know
          font-size / line-height behave differently for em and lh
          units
  TabAtkins: I'm suggesting that just like the 100% line, where you
             explicitly indicate what the resolves to, you just supply
             what them resolves to
  TabAtkins: and then also supply a function that can extract that
             from an element automatically

  fremy: CSSNumericValue -- is that calc or more stuff?
  TabAtkins: Everything
  fremy: Can just create a 1em and call to
  emilio: Layout API functions could need 1vmax, you need to expose
          viewport size to those functions
  emilio: not sure if you want to?
  <dbaron> The function to extract from an element automatically would
           need a property.
  iank: No access to viewport info
  iank: It's not necessary
  iank: The side effect of exposing that info is that when you resize
        that viewport, you'd need to call every layout function
  iank: since it's a side input
  TabAtkins: This would be an implicit dependency
  TabAtkins: unless we made it explicit somehow
  fremy: We could add a function in the worklet, before calling it
         viewport sizes are 0, and only after you can use it...
  TabAtkins: We have a case for later registrations of dependencies
  iank: Probably you want to do this in the layout registration
        dictionary
  iank: You don't know which global scope you're doing to run it in
  fremy: The other option is to create custom properties that would be
         1vmin, etc., type them to be lengths, then you'll get the
         value
  fremy: it's a bit handwavy but it does work
  iank: If this becomes a common pattern, in layout options, say that
        resolve vw/vh against the real thing
  iank: but part of the precedent for resolving to 0 is what happens
        when you set a font-size in an offscreen canvas context, it
        resolves to 0
  TabAtkins: That's different from how MQs work
  iank: Not saying that's good or bad but that's the precedent
  TabAtkins: I think that's all the feedback I need; I will come back
             with a more mature proposal in the future

Drop CSSPositionValue
---------------------
  github: https://github.com/w3c/css-houdini-drafts/issues/817

  TabAtkins: CSSPositionValue was in the first draft
  TabAtkins: Assumes every position is a horiz/vert offset
  TabAtkins: Now they can be logical
  TabAtkins: Not certain how best to represent this flexibility in the
             object shape
  TabAtkins: until I do I plan to drop it from the draft and impl
  TabAtkins: If anyone has ideas for representing these values would
             love help

  astearns: There have been a number of things we've dropped
  astearns: Are we tracking those in any way?
  astearns: Next level draft?
  TabAtkins: I shall soon
  TabAtkins: We did a publish a while ago, need to do another one soon
  TabAtkins: Before EOY I'll get ready for a new draft
  TabAtkins: and I'll have a substantial changes section to outline
             what happened

Clarify list-valued properties
------------------------------
  github: https://github.com/w3c/css-houdini-drafts/issues/823

  TabAtkins: This was brought up by one of our engineers
  TabAtkins: I was hoping at the start this issue wouldn't occur. I
             was hoping to split props into list values and single
             values
  TabAtkins: every prop would be one or the other
  TabAtkins: anders pointed out we have several props that at minimum
             have a neutral value, but sometimes have more than that
  TabAtkins: and the list value / single value split is not enforced
             in custom props either
  TabAtkins: You can always define [ length+ | number+ ]
  TabAtkins: Not handling that well
  TabAtkins: So rather than declare props to be list valued or single
             valued, declare top level productions as being list
             valued or single valued in a given prop
  TabAtkins: e.g. content
  TabAtkins: Some of the productions will be single valued, and they
             can't be used with the append() methods
  TabAtkins: Some are multi-valued, and can be used with the append()
             methods
  TabAtkins: and if you do the wrong one, and you call
             styleMap.append("counter-reset", "none")
  TabAtkins: I believe we'll throw in that case

  TabAtkins: One thing I'm not sure is how to handle the case where
             it's currently a single value, and you append a list
             production
  TabAtkins: e.g. it's currently set to none, and you want to append
             real values
  TabAtkins: The most common case is none, I'd like that to be easier
             to handle
  TabAtkins: So I'd like to make that silently drop the single value,
             and turning it into a multi-value production instead
  TabAtkins: Not sure about this, may be more consistent to throw here
  TabAtkins: I believe this design in general, is the way to go to
             handle all possible props properly
  TabAtkins: It's not a significant complexity increase
  TabAtkins: It'll be a bit more work continuing we need to make sure
             they're properly tracked

  heycam: What if we change a property from single-value length to
          multi-value length?
  TabAtkins: In that case the original length would be classified as a
             multi-value
  heycam: So in the updated property that would be defined as a
          multi-value thing
  TabAtkins: So let me know if you have opinions on this
  TabAtkins: Otherwise I'll resolve it like this and edit it in

  fremy: How you define that syntax, how would you write down your
         declaration to say this is single valued or list valued?
  TabAtkins: CSS in the specs will define this
  TabAtkins: Don't have to do that in the API side
  fremy: For custom properties
  TabAtkins: Right now, custom props are either a single value or
             there's a + in there
  TabAtkins: but once we had the # one, those will be multi-valued

Figure out Array-likes
----------------------
  github: https://github.com/w3c/css-houdini-drafts/issues/825

  TabAtkins: So Typed OM has had a handful of array-like interfaces
             since the beginning
  TabAtkins: Been clashing with Anne and Domenic on how to handle these
  TabAtkins: Last week Domenic and I sat down to look at plausible
             future plans
  TabAtkins: for array-likes in the future, it should indeed be an
             Array subclass
  TabAtkins: So Array.prototype stuff works, Array.isArray works
  TabAtkins: and then at some point, either as just a spec concept, or
             possibly as an author facing API, there'll be a way to
             hook get/set indexes
  TabAtkins: Although they look like data properties, there is still
             some magic
  TabAtkins: that intercepts a get/set so it can update length
  TabAtkins: just set
  TabAtkins: Nothing specwise that stops us using that hook
  TabAtkins: This has some implications for the array-likes in typed OM
  TabAtkins: In particular, there are 3 arrays-likes -- CSSMathArray
  TabAtkins: It's declared to descend from CSSStyleValue
  TabAtkins: It's the array that hold calc arguments
  TabAtkins: Could switch this over to a new Array subclass
  TabAtkins: The other two, CSSUnparsedValue -- for representing
             var()s -- and CSSTransformList -- which reps transform
             lists
  TabAtkins: both are array-like and you can array index them
  TabAtkins: There's are directly useful as values you can set to
             properties, so can't change them from being CSSStyleValue
  TabAtkins: Probably then we'll move the segments / functions to a
             property hanging off them, make them array-likes that
             inherit from Array
  TabAtkins: and have hooks that do type checks to ensure only
             transform functions etc. are set
  TabAtkins: In anticipation of this working later on, these will
             still have getter on them for now
  TabAtkins: When we do switch to the real model, worked out in IDL,
             we can just remove that and there'll be very little if
             any behavior change

  bz: Have you talked to JS engine implementors about this?
  TabAtkins: I talked to domenic and adam klein about this
  bz: In particular the idea of generically hooking set on arrays
      they're ok with?
  TabAtkins: We can do this right now
  bz: Sort of
  bz: It's very very special cased for length updates
  bz: In the TC39 spec handwaves it
  bz: In impls for performance ...
  TabAtkins: So they wouldn't be naked Arrays with magical behavior
  TabAtkins: Right now indexed setters get the same array-looking
             thing and they are optimized (not quite as good as naked
             arrays)
  bz: So you would be modifying Array.isArray to do something here
  TabAtkins: Yes
  bz: And deal with subclass construction bits
  TabAtkins: domenic has some feedback in TC39 for this
  fremy: One question. the segments array in CSSUnparsedValue would
         not be read only?
  TabAtkins: It's not currently read only
  TabAtkins: It only takes strings and var references
  TabAtkins: We'll continue doing type checking to make sure other
             values aren't set in there

  andrew: You don't want to make the transform components subclasses
  andrew: They have to descent from array
  andrew: What do we do for values we return?
  TabAtkins: CSSTransformList remains a CSSStyleValue
  TabAtkins: but it has a prop that is an array-like
  andrew: If you register a prop with that syntax
  TabAtkins: Transform functions are CSSStyleValues as well
  TabAtkins: The list inside it
  andrew: ok, misunderstood

  fremy: If we have the segments thing, it's better
  fremy: but there is still the case for when you upgrade
         CSSUnparsedValue to another thing
  fremy: You should not break the compat
  fremy: If right now we don't have a position thing
  TabAtkins: No, unparsed value is not the generic value class
  TabAtkins: that's CSSStyleValue
  fremy: CSSUnparsedValue is only for var?
  TabAtkins: Yes

  bz: What are the semantics of setting .length on these things?
  TabAtkins: Still a great question
  TabAtkins: Not 100% sure, but more than likely I'll decide it's fine
             to have undefined/holes
  TabAtkins: and those are ignored
  fremy: In the meanwhile can't we use a normal Array with no checking
  fremy: Well it's not compatible with you switch
  bz: Code that used to work will start throwing exceptions
  TabAtkins: This is detailed enough to know what the direction will
             be. Let me know you thoughts

  RESOLVED: Make TypedOM compatible with future Array-like plans, per
            #825.

Layout API
==========

Precision and rounding in layout API
------------------------------------
  github: https://github.com/w3c/css-houdini-drafts/issues/830

  iank: I wanted to write down the problem clearly
  iank: As specced now, with availableInlineSize: 10.13, the internal
        representations in each engine will be different
  iank: I believe Blink/WebKit will be 10.125, Gecko will be 10.133,
        Edge will be 10.13
  iank: Similarly, say that child is auto sized, the value that you
        will get out in JS will be 10.125 for Blink/WebKit, some ugly
        values near 10.33333 for the others
  iank: This is exposed right now
  <dbaron> Some of the ugliness is something that we could fix (by
           going directly from fixed-point to double rather than
           fixed-point to float to double)
  iank: If you set width:10.13px on an element, and you call
        getClientRects, you will get similar issues
  iank: Part of me thinks this is a non-issue
  iank: The thing I worry about with rounding is that you'll get
        potential tearing
  iank: Imagine if you're doing a flexbox-like thing
  iank: where you're distributing available space between children
  iank: With the current API you won't get any subpixel tear, but if
        you round inputs and outputs you will
  iank: You'll have to define the whole layout tree working on rounded
        precision mode

  eae: When you say rounding, you mean rounding to ints or to these
       values?
  iank: If you round to int values, going in and out of the engine,
        you might see tearing
  iank: If you're distributing some fractional space in a flexbox-like
        thing you can't do that
  iank: Also you'd need to specify the whole subtree works in rounded
        int precision
  smfr: What do you mean by tearing?
  iank: If you try to position the two fragments side by side, you'd
        get a gap
  iank: Similarly, the second class there is if you happen to be
        distributing some space that wouldn't get a gap, the element
        might be smaller

  bz: To be clear, the container being smaller than the elements is a
      problem anyway
  bz: Even with floats
  iank: The conversion to the JS float thing, it's still possible to
        get tears accidentally
  iank: but because the JS repr is high enough, converting from the
        internal fractional to JS to the internal fractional will
        result in the same fractional value

  chrishtr: What about pixel snapping?
  iank: We only do that at paint
  iank: We don't pixel snap any of the layout
  TabAtkins: Yes we do
  TabAtkins: border widths e.g.
  eae: Borders are very special
  emilio: Device pixels
  dbaron: There's a small class of things where you care about the
          with of the thing being the same across repeated occurrences
  dbaron: Borders, column rules, probably line heights as well
  dbaron: but for most other things we don't do internal snapping
  dbaron: You round to internal repr then snap edges to the nearest
          pixels
  iank: The pixel snapping borders we can repr that value in the
        LayoutEdges object
  bz: Presumably this is snapped to dev pixels here
  iank: Yes
  chrishtr: Blink currently snaps to CSS px
  eae: Not really
  chrishtr: In cases where the device pixel ratio is incorporated into
            zoom it's device pixels
  chrishtr: long term intention is to use device pixels

  smfr: Someone mentioned pixel snapping in border would happen at
        layout time? isn't purely a paint time operation?
  dbaron: For us it's a computed value time thing
  Rossen: For us it's also style
  Rossen: It matters for lower values, 1, 2, 3, px
  Rossen: but over 13, 14px it doesn't really matter
  dbaron: But it does matter for not getting the 1px gaps that you do
          the snapping before the layout calcs based on it
  dbaron: If you do layout calcs based on a non pixel snapped border,
          then snap it later, you'll sometimes get 1px gaps or overlaps
  Rossen: So we'll do the layout with fractional border sizes
  Rossen: then snap during paint
  dbaron: But you need to snap all the things next to that border
          during paint too or they won't line up
  Rossen: No they won't
  dbaron: Then your system is different in more complex ways from ours

  chrishtr: Is the intention to spec the rounding?
  iank: No
  chrishtr: Or just things that UAs may do?
  iank: My preference is to allow the UA to do internal rounding to
        the fractional repr and back
  iank: Main thing I want to avoid is the 1px tearing in layouts
  chrishtr: It should at least be said if the left/right edge of
            things are the same number, there should not be a tear
  eae: Can probably find a way to spec that
  chrishtr: You'd have to say the rounding and layout is the same, and
            that pixel snapping at paint time preserves that
            consistency
  iank: Yep
  Rossen: Does that work for you Simon?
  smfr: Yes I think so
  smfr: WebKit does some snapping of border widths as well
  smfr: I'm trying to understand when you're laying out fragments
        iteratively, does the browser tell you how much space is left?
        A floating point input
  smfr: with custom layout, call layoutnextfragment 3 times
  smfr: each time an input is the remaining space?
  iank: No you need to do that yourself
  iank: So you'll have some available size, layout first child,
        subtract the size of it in the JS double
  smfr: Was concerned about the conversions between JS doubles and the
        quantized values internally
  iank: Since it's handled by script it's always the JS doubles

  smfr: Trying to think of cases where the rounding would hurt you
  smfr: Maybe combining custom paint and layout
  smfr: you ask for a certain with with double precision
  smfr: then in paint ...
  iank: I think previously you've brought up trying to fit into the
        one fractional unit, and some engines will leave a tiny
        fractional unit over
  iank: In one you go into the next line, in another you'll have a
        skinny box for some reason
  iank: I think it's a super edge case
  fremy: My impression is that the only case where this could be an
         issue is where you do the data sharing between custom layout
  fremy: You could share with full precision, and on the other side of
         the API you'd get it
  fremy: but I also don't expect this to be a problem

  <TabAtkins> Found that the internal->JS->internal conversion is
              always accurate up to (10^15)px; at 10^16 there's a few
              rounding errors with Microsoft's precision.
  <TabAtkins> http://software.hixie.ch/utilities/js/live-dom-viewer/saved/6318
  <astearns> you can just add a flag in Blink to use 1/60 or 1/100
             internally
  <bkardell> astearns are you suggesting google could add that
             capability or saying that it already exists?
  <astearns> ian said he did it just now :)
  <eae> bkardell/astearns it does not exist but we could potentially
        do it as an experiment.

  smfr: As a data point, this same problem also happens in media
        timing values
  smfr: Currently they're all floating point, but there've been
        discussions about turning them into a rational type
  smfr: They're trying to solve some similar way here
  iank: I think there's been talk in JS previously about having value
        types
  TabAtkins: They're not ready yet
  iank: They've been vapourware for a while
  iank: but the intention would be to work in that fractional type
  iank: We shouldn't depend on that
  iank: Could imaging a future where JS does get that fractional type,
        see if it's compatible to change to that
  iank: or otherwise add an option to opt in to it

  fremy: This transformation is a rounding
  iank: It's a rounding to the fractional representation
  fremy: Can we floor rather than round?
  fremy: Otherwise if you divide by 7, all your 7 column objects, you
         give them avail size / 7
  fremy: then you have a layout where each gets the size they can. if
         each is rounded up, the sum will be bigger than 100% and
         you'll wrap
  fremy: Better to floor
  fremy: You make sure the sum will all fit
  TabAtkins: Flooring starts loosing this internal -> JS double ->
             internal relationship more quickly
  eae: You can have a situation where you have a lot of space at the
       end
  eae: which is also not desirable
  fremy: People writing the layout will pay attention to this

  smfr: Has anyone tried using the prototype to see if it's a problem?
  fremy: The prototype is only in one browser
  smfr: Still possible to make layouts with hairline gaps
  fremy: I've not tried but I guess it's possible
  iank: We can see if people run into it
  iank: so far surma and fremy haven't run into it
  smfr: Your masonry layout wasn't trying to fit something into
        something with a border, not obvious there's a gap
  iank: We can experiment with prototypes with borders to see if we
        can get hairline gaps
  fremy: Grid has the opportunity to have these options, but you can
         position things at particular points, no wrapping
  fremy: Even with 7 column grids, you place them before sizing, so it
         doesn't matter
  fremy: We should try the flexbox-like thing

  iank: I think the action item on us is to play with our impl to see
        if we can break
  iank: but sounds like people are relatively fine with the float
        representation and rounding to fractional units internally
  iank: We can report back to the group

  <br dur=1hr>

Received on Wednesday, 26 December 2018 22:52:28 UTC