innerHTML should use lazyAttach to avoid creating a render tree until needed
Created attachment 191596 [details] Patch
This may need a couple more tweeks. I saw two editing-related failures on my local machine, but am not yet sure if those are from this patch or not.
Comment on attachment 191596 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=191596&action=review > Source/WebCore/ChangeLog:15 > + In any case, this brings our inner-html-setter number from 93 runs/sec to 507 runs/sec :) Really!
Comment on attachment 191596 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=191596&action=review > Source/WebCore/editing/markup.cpp:1100 > - containerNode->replaceChild(fragment, containerNode->firstChild(), ec); > + containerNode->replaceChild(fragment, containerNode->firstChild(), ec, true); I wish the last parameter had more readable value than "true" (but that's obviously not something you need to solve in this patch).
The 2400% regression bug is here: https://bugs.webkit.org/show_bug.cgi?id=94733
(In reply to comment #4) > (From update of attachment 191596 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=191596&action=review > > > Source/WebCore/editing/markup.cpp:1100 > > - containerNode->replaceChild(fragment, containerNode->firstChild(), ec); > > + containerNode->replaceChild(fragment, containerNode->firstChild(), ec, true); > > I wish the last parameter had more readable value than "true" (but that's obviously not something you need to solve in this patch). I tried shaving that Yak first, actually. :) But that will wait for a second patch. I'm going to also change the default from false to true, since I suspect any place which depends on implicit "false" behavior is probably wrong. There are some cases where it might make performance-sense to explicitly require a synchronous attach (like for editing I could imagine).
(In reply to comment #4) > (From update of attachment 191596 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=191596&action=review > > > Source/WebCore/editing/markup.cpp:1100 > > - containerNode->replaceChild(fragment, containerNode->firstChild(), ec); > > + containerNode->replaceChild(fragment, containerNode->firstChild(), ec, true); > > I wish the last parameter had more readable value than "true" (but that's obviously not something you need to solve in this patch). Bug 111503.
If bug 111503 lands before this one, this will need a trivial rebase.
Comment on attachment 191596 [details] Patch Attachment 191596 [details] did not pass chromium-ews (chromium-xvfb): Output: http://webkit-commit-queue.appspot.com/results/17082077 New failing tests: fast/forms/basic-buttons.html editing/execCommand/change-font.html
Comment on attachment 191596 [details] Patch Attachment 191596 [details] did not pass mac-wk2-ews (mac-wk2): Output: http://webkit-commit-queue.appspot.com/results/17081088
Comment on attachment 191596 [details] Patch Attachment 191596 [details] did not pass win-ews (win): Output: http://webkit-commit-queue.appspot.com/results/17021095
Comment on attachment 191596 [details] Patch Attachment 191596 [details] did not pass mac-ews (mac): Output: http://webkit-commit-queue.appspot.com/results/17065088
--- /src/WebKit/Source/WebKit/chromium/webkit/Release/layout-test-results/fast/forms/basic-buttons-expected.txt +++ /src/WebKit/Source/WebKit/chromium/webkit/Release/layout-test-results/fast/forms/basic-buttons-actual.txt @@ -14,6 +14,7 @@ text run at (0,40) width 617: "with text (\"foo\") and then uses six different paddings to make sure each of the buttons render properly. " RenderBR {BR} at (617,40) size 0x19 RenderBR {BR} at (0,60) size 0x19 + RenderText {#text} at (0,0) size 0x0 RenderTable {TABLE} at (0,80) size 657x285 RenderTableSection {TBODY} at (0,0) size 657x285 RenderTableRow {TR} at (0,0) size 657x22 And: --- /src/WebKit/Source/WebKit/chromium/webkit/Release/layout-test-results/editing/execCommand/change-font-expected.txt +++ /src/WebKit/Source/WebKit/chromium/webkit/Release/layout-test-results/editing/execCommand/change-font-actual.txt @@ -1,2 +1,5 @@ There should only be one font tag. SUCCESS + + Are the failures. Both come from uses of innerHTML on the body element. I'm not sure about either. Maybe there is some white-space collapsing that we would normally do during synchronous attach() which we somehow miss in the async case? The rendering of both pages is correct, this is a DRT-only issue. Not yet sure if we care. :)
These failures sound very related to the code Haraken touched in bug 110786. (I'm not suggesting that his change broke anything here. Just that he's touched that code and it's probably not lazy-attach aware.)
(In reply to comment #14) > These failures sound very related to the code Haraken touched in bug 110786. (I'm not suggesting that his change broke anything here. Just that he's touched that code and it's probably not lazy-attach aware.) I suspect that we should move the "don't ignore text renderers in all cases" code in Node::attach() to style recalc and that would fix this issue. Is suspect we're currently broken wrt ignoring text nodes and display/visibility changes of renderers.
This FIXME in Element::recalcStyle *terrifies* me: if (ch == Detach || !currentStyle) { // FIXME: The style gets computed twice by calling attach. We could do better if we passed the style along. reattach(); If we're really recalc-ing style twice for intial attach of a lazy-attached node, that's absolutely horrible.
(In reply to comment #16) > This FIXME in Element::recalcStyle *terrifies* me: > if (ch == Detach || !currentStyle) { > // FIXME: The style gets computed twice by calling attach. We could do better if we passed the style along. > reattach(); > > If we're really recalc-ing style twice for intial attach of a lazy-attached node, that's absolutely horrible. Filed bug 111627.
Comment on attachment 191596 [details] Patch We should really do all attaching lazily (including that triggered by the parser).
(In reply to comment #18) > (From update of attachment 191596 [details]) > We should really do all attaching lazily (including that triggered by the parser). Agreed. Do you have any theories for the failures mentioned in comment 13? Debugging those is blocking this change.
> Agreed. Do you have any theories for the failures mentioned in comment 13? Not without debugging no.
http://trac.webkit.org/browser/trunk/LayoutTests/fast/forms/basic-buttons.html and http://trac.webkit.org/browser/trunk/LayoutTests/editing/execCommand/change-font.html are the failing tests.
Created attachment 193025 [details] updated to tot
With the patch applied, this test: <!DOCTYPE html> <html> <body> text <br> <script> document.body.innerHTML += '<div></div>'; </script> </body> </html> Fails with this diff: @@ -7,4 +7,7 @@ RenderText {#text} at (0,0) size 26x19 text run at (0,0) width 26: "text " RenderBR {BR} at (26,0) size 0x19 + RenderText {#text} at (0,0) size 0x0 RenderBlock {DIV} at (0,20) size 784x0 + RenderBlock (anonymous) at (0,20) size 784x0 + RenderText {#text} at (0,0) size 0x0 Still debuggin.
Interestingly, removing this block in Node::attach() makes the behavior change go away. :) (At least in the test in question.) // If this node got a renderer it may be the previousRenderer() of sibling text nodes and thus affect the // result of Text::textRendererIsNeeded() for those nodes. if (renderer()) { for (Node* next = nextSibling(); next; next = next->nextSibling()) { if (next->renderer()) break; if (!next->attached()) break; // Assume this means none of the following siblings are attached. if (!next->isTextNode()) continue; ASSERT(!next->renderer()); toText(next)->createTextRendererIfNeeded(); // If we again decided not to create a renderer for next, we can bail out the loop, // because it won't affect the result of Text::textRendererIsNeeded() for the rest // of sibling nodes. if (!next->renderer()) break; } } I'm not yet entirely sure what the "correct" behavior for these tests are, or if that block is doing what it really means to.
The following printf-logging on the above test: @@ -221,8 +221,11 @@ bool Text::textRendererIsNeeded(const NodeRenderingContext& context) return true; RenderObject* prev = context.previousRenderer(); - if (prev && prev->isBR()) // <span><br/> <br/></span> + printf("prev: %p, br: %i\n", prev, prev && prev->isBR()); + if (prev && prev->isBR()) { + // <span><br/> <br/></span> return false; + } if (parent->isRenderInline()) { // <span><div/> <div/></span> @@ -236,12 +239,15 @@ bool Text::textRendererIsNeeded(const NodeRenderingContext& context) while (first && first->isFloatingOrOutOfFlowPositioned()) first = first->nextSibling(); RenderObject* next = context.nextRenderer(); - if (!first || next == first) + if (!first || next == first) { + printf("magic!\n"); // Whitespace at the start of a block just goes away. Don't even // make a render object for this text. return false; } + } + printf("Default true: '%s'\n", wholeText().utf8().data()); return true; } Produces the following change: @@ -1,8 +1,19 @@ -prev: 0x3c7cbc586038, br: 1 -prev: 0x3c7cbc3594b8, br: 1 -prev: 0x3c7cbc6dc138, br: 0 -prev: 0x3c7cbc6dc138, br: 0 -prev: 0x3c7cbc6dc138, br: 0 +prev: 0x1af39d059578, br: 1 +prev: (nil), br: 0 +magic! +prev: (nil), br: 0 +magic! +prev: (nil), br: 0 +magic! +prev: 0x1af39d4ee0f8, br: 0 +Default true: ' +' +prev: 0x1af39d512cf8, br: 0 +Default true: ' + + + +' layer at (0,0) size 800x600 RenderView at (0,0) size 800x600 layer at (0,0) size 800x36 @@ -12,4 +23,7 @@ RenderText {#text} at (0,0) size 26x19 text run at (0,0) width 26: "text " RenderBR {BR} at (26,0) size 0x19 + RenderText {#text} at (0,0) size 0x0 RenderBlock {DIV} at (0,20) size 784x0 + RenderBlock (anonymous) at (0,20) size 784x0 + RenderText {#text} at (0,0) size 0x0 Clearly we're taking a very different path for text node creation during lazyAttach. Still investigating.
(In reply to comment #24) > Interestingly, removing this block in Node::attach() makes the behavior change go away. :) (At least in the test in question.) How about reverting http://trac.webkit.org/changeset/144526 ?
I believe the problem here is simply the code in Node::attach(). When lazy-attaching, nothing has a renderer yet. But the first thing we attach, then walks through its siblings, creating a text renderers. (See comment 24 for the context.) lazyAttach marks you as "attached", which confuses this code.
This has all been substantially changed.