- From: Dael Jackson <daelcss@gmail.com>
- Date: Sun, 21 Feb 2016 15:02:57 -0500
- To: public-houdini@w3.org
- Cc: www-style@w3.org
=======================================================
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.
=======================================================
Layout API
----------
- iank introduced the group to the work he's done on developing
the spec (text at the meeting was here:
http://bfgeek.com/css-houdini-drafts/css-layout-api/Overview.html
It has since been made public:
https://drafts.css-houdini.org/css-layout-api/)
- registerLayout gets a class, is instantiated one per box,
and have two callbacks; one for layout and one for
computedStyle
- inputProperties gets a breakToken and a constraintSpace.
There's also childInputProperties that function similarly,
but just on the children.
- The layout function is similar but for ContentObject and
returns the traits { width, height, List<Area> children }.
Children is the list of areas you've produced as your
content objects.
- When passed a breakToken in layout you start producing
fragments from that breakToken and the breakToken should
give you all the information you need to do so.
- constraintSpace takes the values of width (inline), height
(block), and exclusions. Width and height can be either
definite or null (infinite). Exclusions are a list of
places where you shouldn't layout.
- Concern was expressed about how to handle min/max content and
will be marked as a large issue in the spec.
- Handling character direction was a gap in the spec that
concerned some, but there were several opinions that bidi
resolution should be out of scope for the first version of
the spec, especially since the information in doLayout isn't
the only information you receive in layout.
- doLayout will return a promise in order to allow for parallel
layout, though it will require more investigation to ensure
it is performant.
- constraintSpace received some concerns about scrolling and
fragmentation for this property. Describing scrolling was
decided to be out of scope for this spec. Fragmentation
should be handled in the text and could be with some adjustments.
- An additional concern about constraintSpace is that it was
written without display: inside/outside. Though the intention
was to roll back unintentional behavior, several people felt
that the behavior was needed.
===== FULL MINUTES BELOW =======
Agenda: https://github.com/w3c/css-houdini-drafts/wiki/Sydney-F2F-January-2016
Scribe: ojan
Layout API
----------
<iank> http://bfgeek.com/css-houdini-drafts/css-layout-api/Overview.html
iank: I wrote up rough spec,
iank: just on personal website,
iank: but don't read it...it's confusing.
iank: I will talk through it now.
iank: The concept of layout is that we have a registerLayout
function.
iank: It's similar to paint,
iank: give it a class,
iank: gets instantiated once per area/box/thing,
iank: you get two callbacks.
iank: First one is layout.
iank: In layout what you get given is a list of your children,
iank: you get given your own computedStyle.
iank: inputProperties you said you'd invalidate on appear in the
computedStyle stylemap
iank: e.g. flex.
iank: You get given a breakToken, which I'll explain in a minute,
iank: and a constraintSpace.
iank: layout(List<Area> children, styleMap, breakToken,
constraintSpace).
iank: In addition to inputProps, you list childInputProps,
iank: e.g. in flexbox I require flex-shrink on the children.
iank: inputProperties show up in the styleMap,
iank: the childProperties show up in the child Area's styleMap.
iank: An Area can perform doLayout, which takes a constraintSpace
and an optional breakToken.
iank: Area {
iank: styleMap;
iank: doLayout(constraintSpace, breakToken);
iank: }
<dbaron> My biggest concern so far is intrinsic size computation
and layout being in the same callback
<dbaron> well, maybe the biggest concern is that this draft should
get in the official repository rather than being in Ian's
own repo...
iank: For layout function s/Area/ContentObject/
iank: for the list of children.
iank: Layout returns traits that consist of { width, height,
List<Area> children }.
iank: Children is the list of areas you've produced as your
content objects.
Florian: Are you going to do line breaking?
iank: Yes, will get to that.
iank: This is all in the spec, but just explaining it here.
iank: layout(child, constraintSpace, breakToken, styleMap)
iank: In layout you iterate over your children, create areas from
them and then you return the generated areas,
iank: this is basically fragmentation.
iank: for (child in children) {
iank: var area = child.doLayout(...);
iank: }
iank: Return list of areas.
Florian: I think I see where this is going in regard to
fragmenting children, but how about fragmenting yourself
and re-layouting children then?
iank: This gets into breakTokens
iank: In layout you get given a breakToken that you can pass to
your children that says "continue doing layout starting at
the breakToken"
iank: <drawing example>
iank: First doLayout call gives you your first line with an area
and a breakToken saying when it last broke.
iank: Then you feed that breakToken back into doLayout so that you
can do the next line.
Florian: Does this mean fragmentation has to be greedy and you
can't do balancing?
Rossen: You can do whatever algorithm you want with backtracking.
Florian: If you want to do balancing you have to do multiple
passes?
iank, Rossen: yeah
shane: Yes, fundamentally this is why balancing is expensive
because you have to do multiple passes. not fundamental to
the API.
iank: The only constraint is that the children areas you return
must be generated from a contiguous set of breakTokens.
SteveZ: So a break token is a point in the source string?
iank: It doesn't have to be in the source string.
iank: Here's my naive sense of what a breakToken should contain;
iank: There's one that's completely opaque. it's just for feeding
back into doLayout.
iank: The other breakToken (passed into layout) has two things
ContentObject and has the opaque breakToken from the
doLayout methods.
Rossen: There needs to be some kind of trait there as well.
Rossen: For example if your content object was an element then you
need to know what kind of trait the element produced.
Rossen: If you have a type properties that was specified and you
took x amount of that property.
Rossen: That should be expressed as a trait.
iank: It probably needs to be flexible to other data in the
breakToken.
dbaron: You're doing in place switch to the area terminology.
dbaron: But what you have in the draft uses box and fragment, and
does so correctly.
dbaron: And it actually needs to be that way and it's doing it
right,
dbaron: can we stick to box and fragments.
Florian: This isn't entirely on the right side.
dbaron: Boxes and fragments are
iank: Lets not bikeshed this now.
<general outrage>
iank: breakToken contains a box.
iank: When you get given a breakToken in layout you have to start
producing fragments from that breakToken and the breakToken
should give you all the information you need to do so.
<dbaron> (I don't follow the break token stuff yet.)
Florian: If you're thinking about doing flexbox for this, is it in
the flexbox or flex items that you consider things like
the min-content size?
Florian: min-content size needs to be taken into account when
you're laying out the entire thing.
iank: Pretend a fragment has a min-content/max-content associated
with it
iank: In the spec see all the things fragment has on it. width,
height, min/max content, ignore un-positioned fragments for
now, opaque breakToken and ignore baseline for the moment.
<dbaron> Also, I think min-content and max-content are per-box
rather than per-fragment... at least for many types of
fragmentation
astearns: Should fragments also have a notion of the content they
contain?
Rossen: Each box is associated with a content element. If you want
to know what's inside of you you can go to your collections.
astearns: In this example where you've broken the line I'd like to
find the first line and find the range of content in the
line.
iank: You want to get the string out.
astearns: Yes. might be more than a string.
iank: I haven't added it in because I don't know how it would work.
Rossen: Why would you need it at this level?
astearns: I was thinking of pagination case where you have content
fragmented across pages.
astearns: And you have footnotes that need to be laid out on that
page.
astearns: It's the reason we have range sequence on the regions API.
Florian: Wouldn't the footnote be one of the boxes in the API?
dbaron: Depends how we define out of flow things in the box tree.
iank: Let's avoid this detail for now.
astearns: Also, as you're laying out do you need to make
distinguish between fragments that have laid out and
fragments that are in overflow.
SteveZ: Related to astearns's question, how would I do first line?
SteveZ: I need to know what content after I've done capital first
letter first on first line.
Rossen: That's in your output breakToken. It tells you where you
ended.
Rossen: I don't need to know how far it got, just need to know
that when you're inside the layout.
dbaron: I'm concerned about a few things related to min/max
content sizes.
dbaron: One is that I don't like the way it's doing them in the
same layout callback as the actual layout computations for
two reasons.
dbaron: 1. Implementations currently implement it as a pass
separate and before layout.
dbaron: 2. In most breaking situations min-content/max-content are
per-box instead of per fragment.
<general discussion about how blink/webkit/gecko do as separate
pass and edge does not>
Florian: For #2 you can ask the entire thing for it's min-content
and then fragment as you move along.
iank: Min and max content are computed when you give a null
constraint space saying you have infinite size for layout so
don't fragment
iank: The min/max content here is from that calculation.
iank: Lets leave that as a large issue.
Florian: Adding to it, in rare cases, min/max content depend on
the position.
Florian: I'm referring to line snapping.
Rossen: e.g. when you have floaters and have to take them into
account?
Florian: No, I meant line grid.
dbaron: We haven't even defined how min/max content work in those
cases and would try to avoid having them affected by
position.
iank: I will leave how we compute min/max content as an larger
open issue.
iank: Fragments don't position themselves, the parent can position
them in it's space.
SteveZ: Can you ask where the baseline is?
iank: Yes. It's a read only attribute on the fragment.
iank: That was obvious and added it for the moment.
Florian: Can you ask where the baselines are?
iank: Just make it a list?
SteveZ: It's not so simple.
Florian: If you get dominant baseline, that's enough for a start.
<astearns> needs to be much better defined, but it's nice that
it's there in the first draft :)
iank: Back to what you return from the layout method...
iank: Width and height of fragment, list of children fragments you
contain (and you've positioned them)
iank: They need to be from a contiguous set of breakTokens.
iank: The give back a list of unpositioned fragments,
iank: and also where your baseline is.
iank: This is how you can propagate baseline up into parents.
iank: Unpositioned fragments is how you do abspos, fixed, floats
and stuff like that
iank: Your layout says I don't know how to position this fragment,
I just pass it up to my parent.
iank: You get this eight from the fragment you generated from
doLayout or the fragment has a list of
unpositionedFragements you can pass up until it hits
something that can position them.
Florian: Depends how we define boxes/fragments and whether they
are your children.
iank: It's useful if you want to be able to flatten your layout.
iank: A complex constraint based layout can artificially flatten
by passing fragments up without positioning them.
Bert: I'm interested about breakToken.
iank: It's up to your parent to either return a breakToken saying
I still have stuff to layout or call layout on you again to
get the next fragment.
Florian: Is it possible for the parent to tell when you need to
get a different break point because if you need to go
beyond it you will overflow or fragment?
iank: Yes, that's in the constraint space.
Bert: min and max content are in inline direction?
Florian: Not both?
iank: No.
Bert: Not sure it's needed here
Rossen: Your min content in the block direction is the result of
the max content of the page.
Rossen: Assuming you're using your layout engine to compute the
max,
Rossen: block direction you need to actually do layout to compute
min/max.
Rossen: The other dimension is you run layout with 0 width and
read the height.
Florian: Depends on if we go with min/max being separate from
layout.
iank: That's only for inline min/max
iank: Final thing you get is...this is a super dumb API...
iank: The constraintSpace
iank: Conceptually has 3 things on it;
iank: width (inline), height (block)
iank: can be definite or null.
iank: null == infinite space in that direction.
iank: It can have definite in one direction and infinite in the
other.
iank: Lastly have a list of exclusions.
iank: It's super dumb at the moment; there's probably better way
to describe this.
iank: Exclusions is areas you shouldn't position fragments in,
iank: floats for example.
iank: When you layout and position something it becomes part of
your exclusion area so that when you layout the next thing
it's in your exclusions space.
iank: It doesn't prevent doing something less general.
iank: child uses constraintSpace just to figure out width/height.
iank: Parent uses it to say where fragments should be positioned
Florian: Have you thought about how shapes work with that?
iank: No.
iank: Could be an exclusion...not sure if good idea.
Rossen: Why not?
iank: Sure. exclusionArea and exclusionRect is a subclass.
iank: That is it.
iank: I have no idea how to do overflow.
dbaron: What do you mean by overflow?
iank: If you should scroll or not.
Rossen: We're doing layout right now not scrolling.
<rossen uses magic fingers>
Rossen: When we are going through layout and taking off chunks of
space for various reasons, the one question that came up
in different contexts...
Rossen: The first one is that I might have special lines that when
I'm crossing I might have to take action.
Rossen: One of them is for fragmentation.
Rossen: If this is meant for fragmentation there will be a set of
commands you would have to take into account whatever your
layout logic is for that.
Rossen: In the case of overflow, you have a different type of
constraint.
Rossen: That says "if I'm crossing this boundary, you don't have
to stop. It's up to you, but I will be requiring a
scrollbar."
Rossen: It's up to the layout logic of the owner of that
constraint space to keep going or not.
jet: Such things aren't breakTokens introduced in the middle of
layout.
dbaron: Scrolling and fragmentation are different things.
Rossen: Yes.
dbaron: Scrolling is something we don't need information in this
API to handle,
dbaron: or not in V1.
dbaron: As long as the thing you're trying to write with the API
isn't overflow:scroll it seems there is default handling
already in the implementation.
dbaron: Maybe we don't want to explain that in V1.
dbaron: For fragmentation there might need to be more information
about how you're trying to fragment.
dbaron: You could be in a space where you want to fragment stuff
that doesn't fit vertically,
dbaron: or that doesn't fit horizontally,
dbaron: and you need to pass that to descendants so they know to
fragment.
Rossen: One of the points of confusion about constraintSpace,
Rossen: there are two types, unbound and bound.
Rossen: That the space is bound doesn't mean you have to fragment.
Rossen: When you do layout we are going to the descendants to
figure out the overall union so you can figure out your
scroll content area and propagate that out.
Rossen: And your constraintSpace can say a given line is special
for fragmenting,
Rossen: and they will nest and be affected that way.
Rossen: The unboundness of space is on attribute and fragmentation
vs non-fragmentation is a different thing.
Florian: Fragmentation might not just be a boolean, e.g. break
after column or break after page.
SteveZ: You said you were doing it in x and y, don't you need to
pass the layout directions.
iank: It should be handled by the engine.
Rossen: You'll always use logical coordinates.
SteveZ: I want to switch at some point.
Rossen: Do it on the ConstraintSpace.
SteveZ: relying on the contentObject to say what direction it's in?
Rossen: Yes.
Rossen: Space has a direction. It's always left to right.
Florian: How do you inform the children that base directionality
is left-to-right or right-to-left?
dbaron: One thing here might be that this is not attempting to
explain inline layout.
Rossen: Why not?
iank: I didn't get into text runs here.
iank: I don't get into positioning characters so bidi handled
magically.
Florian: Can you separate the two?
Rossen: If you're breaking a bunch of divs, do you care what the
layout inside of the divs are?
Florian: When you call doLayout on the linebox, this is not one
you can override?
ojan: Not in V1 for doing character by character layout, but
that's a thing you can add to this general structure of API.
dbaron: Part of what Florian was bringing up is that if you're
doing inline layout, how that works deeply within the line
is a function of the bidi parameters getting passed through.
dbaron: That stuff is reasonably complicated, I'm not sure we want
to try to attack all of that in the first version.
dbaron: Last time we talked about how a custom layout would always
be a block formatting context.
dbaron: So that we don't do inline layout, float interactions,
margin collapsing.
ojan: My perspective: this attempts to have an overarching thing
that we could later add line layout and float exclusions into.
iank: But for v1 we could blockify everything.
ojan: What I was going to say -- there's a strict subset of this
API that does the block part of layout -- it's not a
different API, but a subset.
ojan: This lets us have a single API for block and line layout,
but we ship the block subset in v1.
dbaron: I don't understand the breaking model. My gut feeling is
that it's too simple to possibly be able to work, but
haven't worked out the details.
Florian: To have this API work in level one with explaining
everything don't we need to add more for the bidi stuff?
iank: These will all be additions to the constraints?
ojan: dbaron, is the constraintSpace you pass in and a result from
layout you pass up not conceptually enough?
dbaron: bidi resolution and reordering are two separate steps.
dbaron: Resolution goes before fragmentation.
dbaron: Reordering is after fragmentation and very simple.
dbaron: I don't see how you explain just that in this model.
dbaron: If you have the resolution results, doing the reordering
doesn't seem hard.
dbaron: The next question is what an extension of that is. Do
people want to extend it? If so, in what way/direction and
do we want to make it open or hackable or whatever.
eae: Some of these things can happen before the calls into these
layout methods. whether they need to be exposed is up for
debate. Many use cases you can satisfy without overriding
bidi behavior.
Florian: There's a distinction between the things we expose and
pass through.
Florian: Authors call the outer loop and need to pass the
information into the inner loop.
iank: This is why we have both layout and doLayout.
iank: That way the engine can pass more information down.
ojan: Author script doesn't call layout, it calls doLayout.
Florian: That makes a big difference.
Florian: So the constraintSpace you pass when you call doLayout
isn't necessarily the one your receive in layout.
eae: The browser can pass special information down in the
constraintSpace.
ojan: doLayout/layout also lets the browser to caching in terms of
when to skip re-layout of a subtree because nothing has
changed.
Florian: In fact, might want to tell authors explicitly not to do
caching since the browser has opaque information the
author doesn't know about.
iank: This is not all the information available to the engine,
just a view into the box exposed.
ojan: I've been thinking that bidi resolution phase is not part of
this API at all. but I don't know anything about this space,
so I might not be right.
dbaron: Makes sense.
plinss: What about parallel layout?
iank: I want to make doLayout return a promise.
ojan: I don't want to do that.
plinss: Putting entire layout into author space and precluding
parallelization, you need to leave the loop in engine code.
iank: I don't want two phases.
iank/Rossen: We can do it with a promise.
plinss: Then it need a single promise for all children.
iank: Some things are inherently sequential.
Florian: What's wrong with doing the loop at the browser level?
Florian: So it can run serially or in parallel?
iank: That's what promises are for.
iank: You can do "let frag = away doLayout" or await*[doLayout,
doLayout].
tantek: There are things that are optimistically parallel because
they might not depend on each other and after the fact you
fix up if they did.
iank: With a promised based API, authors can do that.
ojan: My only concern with promises is performance overhead.
Promises in engines today have way more overhead than a
function callback. I don't know if that's fundamental.
tantek: Not all things are parallel or sequential, you can
optimistically layout in parallel.
tantek: Or some traits can be figured out in parallel (e.g.
widths, but not heights).
iank: Yes, to go down that path we'd need to make it promised based.
iank: We need to experiment with performance of promises.
Florian: For what tantek is saying, need promise per trait. That's
way worse overhead.
iank: We don't want to do that.
iank: One way to solve that is to doLayout on a bunch of your
children and then you can call doLayout again with different
constraints.
tantek: Yes, that's more what I'm talking about.
iank: I think we can do that.
Bert: Doesn't that require that the layout function has the
possibility to fail?
Bert: If there were dependencies we didn't know about before you
have to return failure.
tantek: Not failure, just return a result that says some aspects
of this result have a dependency (e.g. vertical
coordinates).
Rossen: Nothing precluding doing multiple passes of doLayouts.
Rossen: That's the error handling.
Rossen: It's not really errors.
Rossen: For all practical purposes the layout is done. It's a
result. You just might not like it.
Florian: How do you attach an implementation of your layout to a
thing?
iank: It's really dumb at the moment. It just uses display.
<ojan> display: layout('foo')
<ojan> display: inline-layout('foo')
<astearns> http://bfgeek.com/css-houdini-drafts/css-layout-api/Overview.html#layout-notation
Bert: Do the built-ins have names?
iank: No.
iank: Similar to paint functions how you cant refer to linear-
gradient.
Florian: How do you get the right thing called on flex items?
iank: They still get coerced to block.
Rossen: Scenario: Have an element that has a custom layout display
value: spiral-flexbox. Only I know how the layout works
and what the rules on the items inside are.
Rossen: For my purposes I don't care what the items do. So you are
taking over all the display outside of your children.
ojan: Let's say you have a flexbox with a display:inline-layout
('foo') in it. Does it get changed to layout('foo')?
ojan: The point is that layout() vs. inline-layout() call the same
code, just different display-outside. In a flexbox display-
outside is coerced to block.
iank: Writing this ignoring display inside/outside.
Rossen: If I have a table, and it's inside grid or flex, ....
ojan: Don't do table.
Rossen: Lets do table!
ojan: Lets not talk tag names, you put a display: layout into an
inline.
dbaron: It triggers the block splitting from CSS 2.1.
Rossen: I think we should do that.
Rossen: Overriding your display-outside doesn't make sense, you
don't know where you get used.
dbaron: In some contexts it does make sense.
ojan: There's a world where, if we redesigned the platform from
scratch, display-outside comes from parent and is not
settable.
ojan: Can we do that here instead of having layout and
inline-layout?
dbaron: I disagree that the parent should always decide.
dbaron: That's saying no one ever needs to distinguish between
inline block and block.
dbaron: People do want that though.
ojan: I agree people do that, if we had a platform where you
couldn't I don't think it's hard to work around.
Rossen: Kind of, I think that view is very block oriented.
Rossen: If you have a thing in a flex or grid, what about those?
dbaron: Yes, those override the display on their children.
Rossen: Your layout of the parent will layout in whatever context
it's operating.
Rossen: For example table row can only handle cells, flex can only
handle cells.
ojan: I think a distinct case where developers do something
different is a block inside an inline.
ojan: For example, a block or inline block inside an inline,
developers might want to decide, but if we designed the
platform from scratch we wouldn't allow that.
dbaron: The sequence of block or inlines inside a block, that's
also different.
dbaron: You could have different display type.
ojan: Yeah.
Rossen: We have "flow layout", basing all our decisions on this
document centric layout ideal seems bad.
dbaron: When we have display types like this we have an inline and
a block variant, so you can use them inside blocks.
dbaron: It doesn't matter outside block, but we still do it.
Rossen: yes.
ojan: There's an alternative, we could add a block that contains
block, and block that contains inlines.
tantek: We have block that contains inlines, that's block.
ojan: No, that one is magic.
dbaron: If we wanted those we need two new types, they're
explicitly incompatible with blocks today.
ojan: If we only had layout, what do we lose?
dbaron: You lose the ability to put it in an inline, you end up
adding wrappers.
ojan: ...
Florian: Yeah.. unpleasant but it's been that way for a while.
dbaron: I'm hesitant that we should litigate this again, once we
have inline-flex, grid table etc.
ojan: This gets to the original thing that Rossen didn't like with
an inline thing in a block only context it get coerced.
Rossen: Let's step back.
Rossen: Why does this matter for your callback?
ojan: It should call the same code.
Rossen: It will execute the same code, you just get a
constraintSpace (layout vs inline layout), depending on
the parent you might get a different constraintSpace,
otherwise the callback is the same.
Florian: Assuming an information heavy constraint space.
Rossen: Well, you have exclusions, etc.
Florian: Yeah, baselines, etc.
Scribe: ojan
esprehn: I think you need them both and it just controls what
contraintSpace you get. If you put a custom layout thing
in a paragraph, might want it inline-block-like...
shouldn't need to add a wrapper span. In a block, you
should get a block constraintSpace.
ojan: What's different bout the constraining space?
esprehn: In a block, parent gives you your width.
esprehn: In a inline-block, the contstratingSpace tells you to
shrink-to-fit.
dbaron: It also applies shrink-to-fit to blocks as well.
esprehn: Yes, just a property of your constraintSpace.
dbaron: For the next steps, can we take this document off your
personal website?
dbaron: So that ten years from now when someone reads these
minutes someone can map the minutes to the document.
iank: I will do so after lunch.
iank: Any other next steps?
Florian: Ship an implementation!
<everyone is loopy from low blood sugar>
<br type=LUNCH>
Received on Sunday, 21 February 2016 20:03:56 UTC