{"id":531,"date":"2010-07-03T21:51:24","date_gmt":"2010-07-04T05:51:24","guid":{"rendered":"http:\/\/www.visophyte.org\/blog\/?p=531"},"modified":"2010-07-03T21:57:15","modified_gmt":"2010-07-04T05:57:15","slug":"understanding-where-layout-goes-wrong-with-gecko-reflow-debug-logs-part-1","status":"publish","type":"post","link":"https:\/\/www.visophyte.org\/blog\/2010\/07\/03\/understanding-where-layout-goes-wrong-with-gecko-reflow-debug-logs-part-1\/","title":{"rendered":"understanding where layout goes wrong with gecko reflow debug logs (Part 1)"},"content":{"rendered":"<p>HTML and CSS provide awesome layout potential, but harnessing that potential to do your bidding can sometimes be problematic.\u00a0 I suspect all people who have written HTML documents have been in a situation where they have randomly permuted the document structure and CSS of something that should work in the hopes of evolving it into something that actually does work.\u00a0 Matching wits with a black box that is invulnerable to even pirate-grade profanity is generally not a pleasant experience.<\/p>\n<p>It turns out gecko has a way to let you see inside that black box.\u00a0 Well, actually, multiple ways.\u00a0 There&#8217;s a <a href=\"https:\/\/developer.mozilla.org\/Layout_Debugger\">layout debugger<\/a> (that seems broken on trunk?) that can display visual overlays for box sizes\/events\/reflow counts, dump info about paints\/invalidations\/events as they happen as well as dumping the current content tree and frames.\u00a0 Even better, <a href=\"http:\/\/www.mozilla.org\/newlayout\/doc\/frame_reflow_debug.html\">gecko&#8217;s frame reflow debugging mechanism<\/a> will dump most of the inputs and outputs of each stage of reflow calculations as they happen.\u00a0 With some comparatively minor patches[1] we can augment this information so that we can isolate reflow decisions to their origin presentation shell\/associated URL and so that we know the tag name, element id, and class information on HTML nodes subjected to reflow calculations.\u00a0 A reasonably sane person would want to do this if they were planning to be doing a lot of potentially complicated HTML layout work and would a) benefit from better understanding how layout actually works, b) not want to propagate layout cargo culting or its ilk from the code being replaced, c) not want to waste days of their lives later the next time this happens, d) help locate and fix layout bugs if bugs they be so that all might benefit.<\/p>\n<p>Of course, with logs that effectively amount to execution traces, examining them by hand is frequently intractable unless you really know what you&#8217;re looking for or are dealing with a toy example.\u00a0 My non-reduced problem case resulted in 58,107 lines, for one.\u00a0 So writing a tool is a good idea, and writing it in JS using <a href=\"https:\/\/jetpack.mozillalabs.com\/\">Jetpack<\/a> doubly so.<\/p>\n<p>In any event, the problem is I am trying to use the flexible box model to create an area of the screen that takes up as much space as possible.\u00a0 In this space I want to be able to house a virtual scrolling widget so I use &#8220;overflow: hidden&#8221;.\u00a0 Regrettably, when my logic goes to populate the div, the box ends up resizing itself and now the whole page wants to scroll.\u00a0 Very sad.\u00a0 (Things work out okay with an explicitly sized box which is why my unit tests for the virtual scrolling widget pass&#8230;)<\/p>\n<p>Let&#8217;s formulate a query on the div of interest (which I will conceal) and then see what the first little bit of output is:<\/p>\n<pre>*** Box 24 tag:div id: classes:default-bugzilla-ui-bug-page-runBox\r\n*** scroll 25 tag:div id: classes:default-bugzilla-ui-bug-page-runs\r\nscroll 25 variant 1  (parent 24) first line 406\r\n  why: GetPrefSize\r\n  inputs: boxAvail: 0,UC\r\n          boxLast: 0,0\r\n          reflowAvailable: 0,UC\r\n          reflowComputed: 0,UC\r\n          reflowExtra: dirty v-resize\r\n  output: prefWidth: 0\r\n          minWidth: 0\r\n          reflowDims: 0,0\r\n          prefSize: 0,0\r\n          minSize: 0,0\r\n          maxSize: UC,UC\r\n  parent concluded: minSize: 0,0\r\n                    maxSize: UC,UC\r\n                    prefSize: 2,0\r\nscroll 25 variant 2  (parent 24) first line 406\r\n  why: Layout\r\n  inputs: boxAvail: 771,1684\r\n          boxLast: 0,0\r\n          reflowAvailable: 771,UC\r\n          reflowComputed: 771,1684\r\n          reflowExtra: dirty dirty-children h-resize v-resize\r\n  output: prefSize: 0,0\r\n          minSize: 0,0\r\n          maxSize: UC,UC\r\n          reflowDims: 771,1684\r\n          layout: 2,0,771,1684\r\n  parent concluded: minSize: 0,0\r\n                    maxSize: UC,UC\r\n                    prefSize: 0,0\r\n                    layout: 0,0,773,1684\r\n<\/pre>\n<p>This is the general pattern we will see to the reflows.\u00a0 The parent will ask it what size it wants to be and it will usually\u00a0 respond with &#8220;ridiculously wide but not so tall&#8221;.\u00a0 (Not in this first base case, but the next one responds with a prefsize of &#8220;1960,449&#8221;, and that&#8217;s pixels.) The parent will then perform layout and say &#8220;no, you need to be taller than you want to be&#8221;, at least until I start cramming stuff in there.<\/p>\n<p>So we skim down the output to find out where things first went off the rails&#8230;<\/p>\n<pre>scroll 25 variant 16  (parent 24) first line 20548\r\n  why: GetPrefSize\r\n  inputs: boxAvail: 1960,UC\r\n          boxLast: 771,1686\r\n          reflowAvailable: 1960,UC\r\n          reflowComputed: 1960,UC\r\n          reflowExtra: dirty-children h-resize v-resize\r\n  output: prefWidth: 1960\r\n          minWidth: 352\r\n          reflowDims: 1960,1755\r\n          prefSize: 1960,1755\r\n          minSize: 352,1755\r\n          maxSize: UC,UC\r\n  parent concluded: minSize: 0,0\r\n                    maxSize: UC,UC\r\n                    prefSize: 1962,1755\r\nscroll 25 variant 17  (parent 24) first line 20548\r\n  why: Layout\r\n  inputs: boxAvail: 771,1755\r\n          boxLast: 1960,1755\r\n          reflowAvailable: 771,UC\r\n          reflowComputed: 771,1755\r\n          reflowExtra: dirty-children h-resize\r\n  output: prefSize: 0,0\r\n          minSize: 352,1755\r\n          maxSize: UC,UC\r\n          reflowDims: 771,1755\r\n          layout: 2,0,771,1755\r\n  parent concluded: minSize: 0,0\r\n                    maxSize: UC,UC\r\n                    prefSize: 0,0\r\n                    layout: 0,0,773,1755\r\n<\/pre>\n<p>Okay, that looks pretty likely to be the area of concern.\u00a0 The parent asked it for its ideal size, so it told it, but then the parent apparently decided to enlarge itself too.\u00a0 That is not what we wanted.\u00a0 We would have been cool if just the scroll #25 enlarged itself (or its block child #26 that corresponds to the same content node but which I have elided because it always says the same thing as its parent #25) since some frame needs to end up holding the overflow coordinate space.<\/p>\n<p>Thus concludes part 1 of our exciting saga.\u00a0 In part 2, we hopefully figure out what the problem is and how to fix it.\u00a0 Lest anyone suggest the root problem is that I am completely off base and am not remotely reasonably sane for choosing this as a strategy to solve the problem&#8230; it works in chrome.\u00a0 Which is not to say that my html\/css is correct and firefox&#8217;s layout is wrong; it&#8217;s quite possible for layout engines to err or deal with unspecified behaviour cases in my favor, after all.\u00a0 But it does make me want to understand what the layout engine is thinking and be able to do so with a minimum of effort in the future, since I doubt this is the last time I will not immediately understand the problem or that layout engines will differ in their behaviour.<\/p>\n<p>For those who want to play along at home: the raw <a href=\"http:\/\/www.visophyte.org\/blog\/wp-content\/uploads\/2010\/07\/framedebug.gz\">gzip&#8217;d \/tmp\/framedebug<\/a> file (gunzip to \/tmp\/framedebug) that is the entirety of the trunk firefox log with debug output, <a href=\"http:\/\/www.visophyte.org\/blog\/wp-content\/uploads\/2010\/07\/serial-12-0.gz\">the spliced output just for the one window<\/a> derived from an invocation of &#8220;cfx run splice&#8221; (it will show up under the URL in \/tmp\/framedumps), and the output of <a href=\"http:\/\/www.visophyte.org\/blog\/wp-content\/uploads\/2010\/07\/summarize-unique-22242526.txt\">the output of &#8220;cfx run summarize serial-12-0 summarize unique 22,24,25,26<\/a>.\u00a0 Those unique identifiers are deterministic but arbitrary values for the given file.\u00a0 We discovered them by using the query on the CSS class using &#8220;cfx run summarize serial-12-0 summarize class wlib-wlib-virt-wlib-virt-container-root&#8221;.\u00a0 The <a href=\"http:\/\/hg.mozilla.org\/users\/bugmail_asutherland.org\/geckgrok\/\">hg repo for the processing tool is here<\/a>, the mozilla-central patches are: <a href=\"http:\/\/hg.mozilla.org\/users\/bugmail_asutherland.org\/mozilla-central-patches\/file\/4c411d365720\/presarena-tell-allocs\">first<\/a> and <a href=\"http:\/\/hg.mozilla.org\/users\/bugmail_asutherland.org\/mozilla-central-patches\/file\/4c411d365720\/box-reflow-debug\">second<\/a>.\u00a0 A <a href=\"http:\/\/twitter.com\/asutherland\/status\/17592978792\">minor jetpack patch<\/a> is also required for the command line stuff to work.<\/p>\n<p>1: I was initially trying to avoid patching anything.\u00a0 This didn&#8217;t work out, but it did cause the initial log file splicing logic to leverage the arena allocation scheme of presentation shells to allow us to to map frames back to their URLs.\u00a0 Sadly, it turned out the arena allocation blocks requested from the upstream allocators are really small (4k or 1k) and all from the same source and so I had to instrument the allocation as well as adding debug output of the window\/docshell\/presshell linkages.\u00a0 The output adds an unacceptable additional level of noise to the default DEBUG case; the right thing to do is likely to cause the reflow log debugging to emit the document URL before each logging outburst if it is different from the last outburst.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>HTML and CSS provide awesome layout potential, but harnessing that potential to do your bidding can sometimes be problematic.\u00a0 I suspect all people who have written HTML documents have been in a situation where they have randomly permuted the document &hellip; <a href=\"https:\/\/www.visophyte.org\/blog\/2010\/07\/03\/understanding-where-layout-goes-wrong-with-gecko-reflow-debug-logs-part-1\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[3],"tags":[67,87],"class_list":["post-531","post","type-post","status-publish","format-standard","hentry","category-mozilla","tag-jetpack","tag-layout"],"_links":{"self":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/531","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/comments?post=531"}],"version-history":[{"count":8,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/531\/revisions"}],"predecessor-version":[{"id":541,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/posts\/531\/revisions\/541"}],"wp:attachment":[{"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/media?parent=531"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/categories?post=531"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.visophyte.org\/blog\/wp-json\/wp\/v2\/tags?post=531"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}