Repository navigation
Please define how the various box tree fixups interact #1355
Copy link
Copy link
Closed
Labels
Closed Accepted as EditorialNo substantive change.No substantive change.Closed Accepted as Obvious BugfixFixed an obvious error with the obvious solution.Fixed an obvious error with the obvious solution.Closed Accepted by CSSWG ResolutionChanges backed by a CSSWG resolution.Changes backed by a CSSWG resolution.Commenter Response PendingTracked in DoCcss-display-3Current WorkCurrent Work
Description
There are now multiple CSS specs that define various computed display and box tree fixups. For example (this is probably not an exhaustive list):
- https://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level defines a fixup that happens if an inline box contains a block box; note that this does NOT create any anonymous boxes other than ones inlines create anyway.
- Various blockification fixups for flex, grid, being floated, positioned
- Fixups for mismatched writing-mode.
- Fixups for ruby as defined at https://drafts.csswg.org/css-ruby/#box-fixup
- Fixups for tables as defined at https://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
The ordering of this stuff needs to be defined. And yes, it matters, apparently. Some examples of it mattering:
- Gecko code has comments about how the ruby bits have to come after the grid bits, for certain tests to pass.
- If you have a block-inside-inline all inside a ruby container, what happens? The ruby things say they happen "after any intermediary anonymous-box fixup", but it's not clear to me whether the anonymous-block thing above counts as "intermediary anonymous-box fixup". If it does, then it seems like the output should be three inline-block boxes, the first containing the first inline, the second being the inlinized block, and the third containing the third inline. If it doesn't, then the output should be a ruby container with a block child.
For the second item, I tested what some UAs do. Testcase:
<ruby>
Start
<span style="border: 10px solid green">
<div style="background: yellow">Text</div>
</span>
End
</ruby>
Observed behavior: Chrome, Safari, and Edge don't inlinize as far as I can tell, no matter whether the <span> is there. Firefox does inlinize, but does that in some sort of deep traversal and before doing the block-inside-inline fixup, so ends up with a green box around "Text".
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Closed Accepted as EditorialNo substantive change.No substantive change.Closed Accepted as Obvious BugfixFixed an obvious error with the obvious solution.Fixed an obvious error with the obvious solution.Closed Accepted by CSSWG ResolutionChanges backed by a CSSWG resolution.Changes backed by a CSSWG resolution.Commenter Response PendingTracked in DoCcss-display-3Current WorkCurrent Work
Activity
upsuper commentedon May 12, 2017
(cc myself)
Loirooriol commentedon May 12, 2017
From CSS display, the point in inlinifications being recursive is
Presumably, this implies blocks breaking inline parents happens after inlinification. Otherwise they would have already broken the inline formatting context.
bzbarsky commentedon May 12, 2017
My point is that all this needs to be clearly defined in the relevant specs, probably by all the fixups being driven from a single spec that specifies the ordering, instead of implementors needing to guess and read tea leaves and sacrifice chickens to divine intentions.
tabatkins commentedon May 16, 2017
@fantasai and I think the ordering ends up being real simple:
Wrt to your Ruby example, only FF does it correctly - the inlinification rules were added to ruby to avoid the complication of doing block-in-inline fixup.
Is this a sufficient categorization or do you need something actually added to the spec here (if so, what)?
bzbarsky commentedon May 16, 2017
The code comments suggesting that the computed-value fixups from ruby and grid have to happen in a particular order don't make me very confident in this claim, unfortunately. It's possible those comments are wrong, of course.
Really? What happens when I have a ruby-text-container parent and a table-cell child? Which anonymous box fixups apply and in what order and why?
That's ... not what the ruby draft I linked above says, just to be clear. It explicitly says that ruby-triggered inlinization happens after anonymous box generation for tables, and that it affects computed display values.
Yes, that would be the sane thing. The specs explicitly say that's not how it works, at the moment.
Well, it would be good if the categorization given didn't disagree with existing spec text, for a start. That probably involves changing said spec text.
Just to make my position crystal clear:
display:inline", but I strongly suspect it should be an inline-table for general sanity).I would like to avoid the problems in my item 2. I think addressing item 1 is likely the best way to get there. My specific suggestion is to make this stuff very explicit: if anonymous box fixups happen after all computed style fixups, they should clearly say so (and neither one say the opposite).
So a possible start would in fact be to have the two sets of fixups, have all fixups say which set they're in, explicitly invoke the computed-value fixups at cascade time, explicitly invoke the box tree fixups from box tree construction.
Or put another way, whenever someone adds a new fixup, it should be easy for them to check whether it conflicts with existing fixups. Right now that is more or less rocket science. Even getting a list of existing fixups is rocket science.
tabatkins commentedon May 16, 2017
Those are in two different categories - ruby fixup (I presume you mean wrapping naked ruby-internal display types) is #2, while Grid blockification is #1. I agree that, previously, it wasn't completely clear that the two categories were strictly divided as they are; that's why we had to add text to Flexbox for it (because we actually discussed it and made an ordering choice, which luckily accorded with the most reasonable-in-hindsight ordering).
ruby-text-container inlinifies its contents - that's a #1 fixup of computed values. This happens before table-cell tries to wrap itself in anonymous boxes (and in fact prevents that from happening, because it turns into an inline).
Yup. :( The Ruby draft text is old and predates Display, Grid, and other things that made the ordering much clearer. It needs to be updated. (#1341 on ruby inlinification covers this issue.)
Yeah, just the Ruby spec afaik. #1341
Afaict, this is precisely what https://drafts.csswg.org/css-display/#intro says (which is normative) - it gives a precise ordering to things, with computed-values happening before box-generation (and anonymous box generation specifically). Anything missing from there?
bzbarsky commentedon May 16, 2017
No, I mean ruby-triggered inlinization. Gecko does in fact do all the computed display bits first, before we start doing anything with boxes, because sanity.
That's not what https://drafts.csswg.org/css-ruby/#anon-gen-inlinize says to do (only block-level things are inlinized, and table-cell is not block-level), and not what Firefox does. Simple testcase (using table-row instead of table-cell because it's clearer to eyeball):
that generates an anonymous inline-table around the two rows in Firefox, I'm pretty sure. But that's not defined sanely in any spec, because CSS anon box creation doesn't know about new display types either....
It claims to be dated today. ;) Anyway, clearly changes to the ruby draft are needed; we all agree on that, which is good.
That's pretty helpful, yes. It would be even better if all fixup definitions linked to it so you could go from a thing you're looking at to the thing that defines how the thing you're looking at fits into the world.
And again, adding a fixup should be a sane process, which means being able to easily determine what other fixups it needs to interact with.
[css-ruby] Move display computation above anonymous box fixup to alig…
tabatkins commentedon May 16, 2017
Then I'm not sure what you're referring to; Grid blockification and Ruby inlinification can't happen to the same elements.
The spec generator runs multiple times a day, and the CI we're using doesn't automatically inform the spec of when it was last meaningfully touched, so all the Bikeshedded drafts claim to be updated today. ^_^ (I'll bug plinss about this - I think we can do something about that.)
@fantasai and I just reviewed the Ruby spec real quick; at minimum, steps 1 and 2 of the fixup algo need to be swapped, which she's doing right now. We'll actually fix the Step 2 text (which is inconsistent with Display's model, and how Flexbox/Grid now work) in a separate issue.
Hmm. @fantasai and I will have to think a bit on how to expose an anchor reasonably in a way that reads well when used.
A given fixup is either adjusting computed styles or adding anonymous boxes, which determines where it goes automatically. And as I outlined before, I think all the fixups in each category are either compatible or mutually exclusive, so they don't need an explicit ordering yet. Right?
upsuper commentedon May 16, 2017
I think one thing which wasn't clear is what should happen when inlinification or blockification is applied to a layout-internal display type. That happens when, e.g. a ruby internal inside a grid/flex, or a table internal inside a ruby.
I just checked CSS Display again, and it seems that it has defined it clearly that, when those algorithms are applied to a layout-internal display type, they would just be converted to
inlineandblockrespectively.This isn't what Firefox currently does for ruby. We are currently trying to do smart things like avoiding doing inlinification for non-block level display type, and instead just hoping anonymous box generation would fix up everything. This is probably fragile. I think we can change it to do what the spec says about inlinification.
bzbarsky commentedon May 17, 2017
I think the relevant situation is this:
What ends up happening with the display of the
<div>?Let's posit that.
I think we're talking past each other. I'm explicitly talking through what has to happen when someone adds a new fixup. How do they make sure it's mutually exclusive or compatible with all existing ones?
But even past that, it's not clear to me that what you said is true, because nothing defines when these fixups take place. In my example above with grid and ruby-text-container, does the fixup, if any, for the
<div>take place before or after fixup, if any, for the inner<span>? Where is this ordering defined? Are all fixups applied to a given element before any fixups are applied to its descendants, or is each fixup performed as a separate pass over the tree?Seriously, this stuff needs to actually be defined. What we have now is a non-interoperable mess that's not possible to implement interoperably based on what the specs actually say. :(
Loirooriol commentedon May 17, 2017
After the edit in CSS ruby it's a bit more clear, but the ordering in #1355 (comment) should probably be explicitly explained in the Introduction of CSS Display.
However, I think it's not that simple. For example,
This is not what happens in a flex or grid context, because floating does not apply to flex or grid items, so "others" (the blockification induced by the formatting context) do "something". But floating applies in a ruby context. So I believe Tab's step number 1 in fact is something like the following:
Each formatting context should define (this is Tab's step 2):
But what if some of these anonymous boxes inserted in-between do inlinify or blockify their contents, or do something which is supposed to be handled in Tab's step 1? I don't think this is currently possible, but as Boris says, adding a new fixup could mess this up.
Anyways, in the algorithm above I'm sure I missed lots of details. I would love someone to do it properly and include it in some spec.
fantasai commentedon May 17, 2017
Filed @upsuper's question about inlinification of layout-internal types as #1390.
Wrt @bzbarsky's comment #1355 (comment)
<span style="display: grid"><span style="display: ruby-text-container"><div></div></span></span>According to CSS Display,
The question in @bzbarsky's example is that, if we resolve
ruby-text-container's computed value first, then it becomesblockand the innerdivremains ablockas well. If we resolve the innerdiv's computed value first, then it becomes aninline-block.A general principle of CSS is that constraints, insofar as we can make them so, are insensitive to a timeline and described as a steady state, and appending elements to the tree shouldn't have unexpected implicit side-effects on earlier elements. If we convert the innermost
divtoinline-blockbecause it is inside aruby-text-container, and then convert theruby-text-containerto ablock, then we notice that the innerdivhas lost its reason for being aninline-block. This isn't how we want things to work: starting with a tree without the inner div, and then inserting it later, it would be ablocknot aninline-block.Furthermore, inheritance and style computation works from the top down. We compute against the computed value of a parent's
display, not its specified or cascaded value. And if we're looking at the box tree, certainly the boxes so generated are done based on the computed value ofdisplay, not its specified or cascaded value, because the computed value is what determines the type of box to generate. So I think we can safely say that the intent is for this example to result in a grid containing a block containing a block.Computed value fixups that reference either a parent's
displayvalue or its box type by definition reference its computeddisplayvalue (which is final), not some hypothetical in-between state. This requires that all fixups are applied to a parent before its children are evaluated.tabatkins commentedon May 19, 2017
Correct. Your point on computation moving down the tree is correct - you can't compute the inner div's display until you know the span's computed display, and you can't compute that until you know the outer span's computed display. The outermost one is well-specified - it's a grid - so that dictates the inner span becomes blockified into a
block flow, which dictates that the inner div is fine and can stay its default block.Nothing implicit here or relying on appeals to general timelessness principles, it's just the way data naturally flows in the constraints CSS imposes.
@bzbarsky
Yeah, there currently isn't a way. This could definitely benefit from, as you say, an easy link that we consistently use into the Display intro, and probably a coordinating wiki page like we do for other distributed-consistency problems in CSS.
[css-display] 各種更新
[css-ruby-1] Define interaction of ruby anonymous box fixup and table…
fantasai commentedon Mar 20, 2018
Since inlinification doesn't affect layout-internal display types (expecting such boxes to be wrapped in an appropriate anonymous inline-level wrapper by the relevant box-fixup rules), I also added ec80a76 to address misparented internal table boxes inside ruby. (We were assuming they'd be considered inline-level due to fixup, but it wasn't really clear since if table box fixup hasn't run yet, they aren't yet wrapped.)
CSS Tables L3 already recognizes ruby in determining anonymous table box types, so I don't think there are any issues. there.
fantasai commentedon Mar 20, 2018
Idk what to close this as, the dependencies are all different... >_<;;