- 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