RESOLVED CONFIGURATION CHANGED 111494
innerHTML should use lazyAttach to avoid creating a render tree until needed
https://bugs.webkit.org/show_bug.cgi?id=111494
Summary innerHTML should use lazyAttach to avoid creating a render tree until needed
Eric Seidel (no email)
Reported 2013-03-05 16:30:19 PST
innerHTML should use lazyAttach to avoid creating a render tree until needed
Attachments
Patch (2.67 KB, patch)
2013-03-05 16:33 PST, Eric Seidel (no email)
no flags
updated to tot (2.74 KB, patch)
2013-03-13 17:14 PDT, Eric Seidel (no email)
no flags
Eric Seidel (no email)
Comment 1 2013-03-05 16:33:36 PST
Eric Seidel (no email)
Comment 2 2013-03-05 16:34:17 PST
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.
Kentaro Hara
Comment 3 2013-03-05 16:35:35 PST
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!
Adam Barth
Comment 4 2013-03-05 16:39:28 PST
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).
Kentaro Hara
Comment 5 2013-03-05 16:40:45 PST
The 2400% regression bug is here: https://bugs.webkit.org/show_bug.cgi?id=94733
Eric Seidel (no email)
Comment 6 2013-03-05 16:41:07 PST
(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).
Eric Seidel (no email)
Comment 7 2013-03-05 17:43:41 PST
(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.
Eric Seidel (no email)
Comment 8 2013-03-05 17:54:43 PST
If bug 111503 lands before this one, this will need a trivial rebase.
WebKit Review Bot
Comment 9 2013-03-05 19:46:52 PST
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
Build Bot
Comment 10 2013-03-06 00:42:49 PST
Build Bot
Comment 11 2013-03-06 03:09:20 PST
Build Bot
Comment 12 2013-03-06 07:04:32 PST
Eric Seidel (no email)
Comment 13 2013-03-06 12:07:59 PST
--- /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. :)
Eric Seidel (no email)
Comment 14 2013-03-06 12:41:22 PST
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.)
Eric Seidel (no email)
Comment 15 2013-03-06 12:45:52 PST
(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.
Eric Seidel (no email)
Comment 16 2013-03-06 12:52:39 PST
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.
Eric Seidel (no email)
Comment 17 2013-03-06 14:41:48 PST
(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.
Antti Koivisto
Comment 18 2013-03-07 05:41:57 PST
Comment on attachment 191596 [details] Patch We should really do all attaching lazily (including that triggered by the parser).
Eric Seidel (no email)
Comment 19 2013-03-07 13:03:50 PST
(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.
Antti Koivisto
Comment 20 2013-03-08 00:42:38 PST
> Agreed. Do you have any theories for the failures mentioned in comment 13? Not without debugging no.
Eric Seidel (no email)
Comment 22 2013-03-13 17:14:15 PDT
Created attachment 193025 [details] updated to tot
Eric Seidel (no email)
Comment 23 2013-05-10 14:41:56 PDT
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.
Eric Seidel (no email)
Comment 24 2013-05-10 15:27:36 PDT
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.
Eric Seidel (no email)
Comment 25 2013-05-10 17:24:44 PDT
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.
Ryosuke Niwa
Comment 26 2013-05-10 17:40:56 PDT
(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 ?
Eric Seidel (no email)
Comment 27 2013-05-10 18:31:53 PDT
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.
Anne van Kesteren
Comment 28 2023-04-01 00:55:11 PDT
This has all been substantially changed.
Note You need to log in before you can comment on or make changes to this bug.