In part 1, I was fighting some kind of flex-box layout problem in gecko that I put on the metaphorical back burner. The metaphorical pot is back, but rebuilt because the old pot did not support animation using CSS transitions. (I was using overflow: none in combination with scrollLeft/scrollTop to scroll the directly contained children; now I’m using overflow: none with a position:absolute child that holds the children and animates around using CSS left/top.) Thanks to the rebuild, the truly troublesome behavior is gone. But helpfully for providing closure to the previous blog post, gecko still was enlarging something and so causing the page to want to scroll.
With a few enhancements to part 1’s logic, we add a “growth” command so we can do “cfx run growth serial-13-0” and get the following results:
*** block 19 display: block tag:div id: classes:default-bugzilla-ui-bug-page-title *** inline 20 display: inline tag:span id: classes:default-bugzilla-ui-bug-page-summary inline 20 variant 1 (parent 19) first line 368 why: Reflow display: inline inputs: reflowAvailable: 960,UC reflowComputed: UC,UC reflowExtra: dirty v-resize output: reflowDims: 885,21 parent concluded: prefWidth: 885 minWidth: 100 reflowDims: 960,21 *** block 23 display: block tag:div id: classes: [elided] *** SVGOuterSVG(svg)(0) 27 display: inline tag:svg id: classes: SVGOuterSVG(svg)(0) 27 variant 1 (parent 23) first line 850 why: GetPrefWidth,GetMinWidth,Reflow display: inline inputs: reflowAvailable: 187,UC reflowComputed: 187,1725 reflowExtra: dirty v-resize output: prefWidth: 187 minWidth: 0 reflowDims: 187,1725 parent concluded: prefWidth: 187 minWidth: 0 reflowDims: 187,1730
The growth detection logic is simple and not too clever, but works. For nodes that are the sole child of their parent, we check if there is a difference between how large the child wants to be and how large the parent decides to be that cannot be accounted for by the margin of the child and the border/padding of the parent.
The first reported node is being forced wider than it wanted to be because of an explicit width assigned to a flex-box. The second reported node is the SVG node that we are rendering our timeline into (see the screenshot). We can see that its parent ends up being 5 pixels taller than the SVG node. This is concerning and very likely our problem.
The answer is the “display: inline” bit. If you are foolish enough to fire up gdb, it turns out that nsBlockFrame::ReflowLine checks whether its child line is a block or not. If it’s not, we end up in nsLineLayout::VerticalAlignLine which calls VerticalAlignFrames which decides to set mMinY=-1725px and mMaxY=5px. I am only half-foolish so I just added a “display: block”, saw that it made the problem go away, and stopped there.
To summarize, “svg” elements are not “display: block” by default and gecko appears to decide to pad the sole child out with some kind of line height logic. This does not happen in webkit/chromium in this specific situation; this is notable in this context because inconsistencies between layout engines are likely to be particularly frustrating. The gecko reflow debug logs and my tooling do not currently make the causality blindingly obvious, although I have made the output explicitly call out the “display” type more clearly so the inference is easier to make. It turns out that if I had built gecko with -DNOISY_VERTICAL_ALIGN I would have gotten additional output that would likely have provided the required information.
This looks like a re-run of the old “table of sliced images” gotcha, where (because images are inline by default) Gecko will put them into a text line which has a certain ascent and descent which will stop your images abutting each other vertically.
Yep. It wasn’t a surprise that the problem was something simple, especially for this case, since layout/CSS is made up of simple knobs; there are just a lot of them. The problem I was experiencing in part 1 was probably a bit more interesting since it implied some non-obvious semantics to the flex-box model and overflow (on gecko; again webkit/chromium worked). I spent a fairly good amount of time on that one trying to bend it to my will before moving on to the overkill option.