Title: [166303] trunk
Revision
166303
Author
an...@apple.com
Date
2014-03-26 11:35:55 -0700 (Wed, 26 Mar 2014)

Log Message

Render tree construction is O(N^2) in number of siblings
https://bugs.webkit.org/show_bug.cgi?id=129065

Source/WebCore: 

Reviewed by Darin Adler.
        
When adding a new renderer to the tree we would search for the correct render tree
position by traversing DOM children forward to find something that already has a renderer.
In common case there is no such renderer. This would be repeated for each inserted renderer
leading to O(n^2) in respect to child node count.
        
This patch caches the computed render tree insertion position and passes it to siblings.
Until the cached position is reached it can be used for all new renderers.

Test: perf/sibling-renderer-On2.html

* style/StyleResolveTree.cpp:
(WebCore::Style::RenderTreePosition::parent):
(WebCore::Style::RenderTreePosition::RenderTreePosition):
(WebCore::Style::RenderTreePosition::canInsert):
(WebCore::Style::RenderTreePosition::insert):
(WebCore::Style::RenderTreePosition::computeNextSibling):
(WebCore::Style::RenderTreePosition::invalidateNextSibling):
(WebCore::Style::styleForElement):
(WebCore::Style::elementInsideRegionNeedsRenderer):
(WebCore::Style::createRendererIfNeeded):
(WebCore::Style::createTextRendererIfNeeded):
(WebCore::Style::attachTextRenderer):
(WebCore::Style::updateTextRendererAfterContentChange):
(WebCore::Style::attachChildren):
(WebCore::Style::attachDistributedChildren):
(WebCore::Style::attachShadowRoot):
(WebCore::Style::attachBeforeOrAfterPseudoElementIfNeeded):
(WebCore::Style::attachRenderTree):
(WebCore::Style::resolveLocal):
(WebCore::Style::resolveTextNode):
(WebCore::Style::resolveShadowTree):
(WebCore::Style::updateBeforeOrAfterPseudoElement):
(WebCore::Style::resolveTree):

LayoutTests: 

Reviewed by Darin Adler.

* perf/sibling-renderer-On2-expected.txt: Added.
* perf/sibling-renderer-On2.html: Added.
        
    The test doesn't use magnitude-perf.js as this requires a relatively long-running test function and
    it seemed unsuitable for that.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (166302 => 166303)


--- trunk/LayoutTests/ChangeLog	2014-03-26 18:31:58 UTC (rev 166302)
+++ trunk/LayoutTests/ChangeLog	2014-03-26 18:35:55 UTC (rev 166303)
@@ -1,3 +1,16 @@
+2014-03-26  Antti Koivisto  <an...@apple.com>
+
+        Render tree construction is O(N^2) in number of siblings
+        https://bugs.webkit.org/show_bug.cgi?id=129065
+
+        Reviewed by Darin Adler.
+
+        * perf/sibling-renderer-On2-expected.txt: Added.
+        * perf/sibling-renderer-On2.html: Added.
+        
+            The test doesn't use magnitude-perf.js as this requires a relatively long-running test function and
+            it seemed unsuitable for that.
+
 2014-03-26  Zoltan Horvath  <zol...@webkit.org>
 
         [CSS Shapes] Remove shape-inside support

Added: trunk/LayoutTests/perf/sibling-renderer-On2-expected.txt (0 => 166303)


--- trunk/LayoutTests/perf/sibling-renderer-On2-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/perf/sibling-renderer-On2-expected.txt	2014-03-26 18:35:55 UTC (rev 166303)
@@ -0,0 +1,5 @@
+PASS timeRenderTreeCreation(5000) < 20 * timeRenderTreeCreation(500) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/perf/sibling-renderer-On2.html (0 => 166303)


--- trunk/LayoutTests/perf/sibling-renderer-On2.html	                        (rev 0)
+++ trunk/LayoutTests/perf/sibling-renderer-On2.html	2014-03-26 18:35:55 UTC (rev 166303)
@@ -0,0 +1,13 @@
+<script src=""
+<body>
+<script>
+function timeRenderTreeCreation(count) {
+    var t = Date.now();
+    for (var i = 0; i < count; ++i)
+        document.body.appendChild(document.createElement('div'));
+    document.body.offsetTop;
+    return Date.now() - t;
+}
+shouldBe("timeRenderTreeCreation(5000) < 20 * timeRenderTreeCreation(500)", "true");
+</script>
+<script src=""

Modified: trunk/Source/WebCore/ChangeLog (166302 => 166303)


--- trunk/Source/WebCore/ChangeLog	2014-03-26 18:31:58 UTC (rev 166302)
+++ trunk/Source/WebCore/ChangeLog	2014-03-26 18:35:55 UTC (rev 166303)
@@ -1,3 +1,44 @@
+2014-03-26  Antti Koivisto  <an...@apple.com>
+
+        Render tree construction is O(N^2) in number of siblings
+        https://bugs.webkit.org/show_bug.cgi?id=129065
+
+        Reviewed by Darin Adler.
+        
+        When adding a new renderer to the tree we would search for the correct render tree
+        position by traversing DOM children forward to find something that already has a renderer.
+        In common case there is no such renderer. This would be repeated for each inserted renderer
+        leading to O(n^2) in respect to child node count.
+        
+        This patch caches the computed render tree insertion position and passes it to siblings.
+        Until the cached position is reached it can be used for all new renderers.
+
+        Test: perf/sibling-renderer-On2.html
+
+        * style/StyleResolveTree.cpp:
+        (WebCore::Style::RenderTreePosition::parent):
+        (WebCore::Style::RenderTreePosition::RenderTreePosition):
+        (WebCore::Style::RenderTreePosition::canInsert):
+        (WebCore::Style::RenderTreePosition::insert):
+        (WebCore::Style::RenderTreePosition::computeNextSibling):
+        (WebCore::Style::RenderTreePosition::invalidateNextSibling):
+        (WebCore::Style::styleForElement):
+        (WebCore::Style::elementInsideRegionNeedsRenderer):
+        (WebCore::Style::createRendererIfNeeded):
+        (WebCore::Style::createTextRendererIfNeeded):
+        (WebCore::Style::attachTextRenderer):
+        (WebCore::Style::updateTextRendererAfterContentChange):
+        (WebCore::Style::attachChildren):
+        (WebCore::Style::attachDistributedChildren):
+        (WebCore::Style::attachShadowRoot):
+        (WebCore::Style::attachBeforeOrAfterPseudoElementIfNeeded):
+        (WebCore::Style::attachRenderTree):
+        (WebCore::Style::resolveLocal):
+        (WebCore::Style::resolveTextNode):
+        (WebCore::Style::resolveShadowTree):
+        (WebCore::Style::updateBeforeOrAfterPseudoElement):
+        (WebCore::Style::resolveTree):
+
 2014-03-26  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r166264.

Modified: trunk/Source/WebCore/style/StyleResolveTree.cpp (166302 => 166303)


--- trunk/Source/WebCore/style/StyleResolveTree.cpp	2014-03-26 18:31:58 UTC (rev 166302)
+++ trunk/Source/WebCore/style/StyleResolveTree.cpp	2014-03-26 18:35:55 UTC (rev 166303)
@@ -4,7 +4,7 @@
  *           (C) 2001 Peter Kelly (p...@post.com)
  *           (C) 2001 Dirk Mueller (muel...@kde.org)
  *           (C) 2007 David Smith (catfish....@gmail.com)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2010, 2012-2014 Apple Inc. All rights reserved.
  *           (C) 2007 Eric Seidel (e...@webkit.org)
  *
  * This library is free software; you can redistribute it and/or
@@ -61,11 +61,34 @@
 
 enum DetachType { NormalDetach, ReattachDetach };
 
-static void attachRenderTree(Element&, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle>);
-static void attachTextRenderer(Text&, ContainerNode& renderingParentNode);
+class RenderTreePosition {
+public:
+    RenderTreePosition(RenderElement* parent);
+    RenderTreePosition(RenderElement* parent, RenderObject* nextSibling);
+
+    RenderElement* parent() { return m_parent; }
+
+    void insert(RenderObject&);
+    bool canInsert(RenderElement&) const;
+    bool canInsert(RenderText&) const;
+
+    void computeNextSibling(const Node&, ContainerNode& renderingParentNode);
+    void invalidateNextSibling(const RenderObject&);
+
+private:
+    RenderElement* m_parent;
+    RenderObject* m_nextSibling;
+    bool m_hasValidNextSibling;
+#if !ASSERT_DISABLED
+    unsigned m_assertionLimitCounter;
+#endif
+};
+
+static void attachRenderTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, PassRefPtr<RenderStyle>);
+static void attachTextRenderer(Text&, ContainerNode& renderingParentNode, RenderTreePosition&);
 static void detachRenderTree(Element&, DetachType);
-static void resolveTextNode(Text&, ContainerNode& renderingParentNode);
-static void resolveTree(Element&, ContainerNode& renderingParentNode, Change);
+static void resolveTextNode(Text&, ContainerNode& renderingParentNode, RenderTreePosition&);
+static void resolveTree(Element&, ContainerNode& renderingParentNode, RenderTreePosition&, Change);
 
 Change determineChange(const RenderStyle* s1, const RenderStyle* s2)
 {
@@ -151,6 +174,67 @@
     return nullptr;
 }
 
+RenderTreePosition::RenderTreePosition(RenderElement* parent)
+    : m_parent(parent)
+    , m_nextSibling(nullptr)
+    , m_hasValidNextSibling(false)
+#if !ASSERT_DISABLED
+    , m_assertionLimitCounter(0)
+#endif
+{
+}
+
+RenderTreePosition::RenderTreePosition(RenderElement* parent, RenderObject* nextSibling)
+    : m_parent(parent)
+    , m_nextSibling(nextSibling)
+    , m_hasValidNextSibling(true)
+#if !ASSERT_DISABLED
+    , m_assertionLimitCounter(0)
+#endif
+{
+}
+
+bool RenderTreePosition::canInsert(RenderElement& renderer) const
+{
+    ASSERT(m_parent);
+    ASSERT(!renderer.parent());
+    return m_parent->isChildAllowed(renderer, renderer.style());
+}
+
+bool RenderTreePosition::canInsert(RenderText& renderer) const
+{
+    ASSERT(m_parent);
+    ASSERT(!renderer.parent());
+    return m_parent->isChildAllowed(renderer, m_parent->style());
+}
+
+void RenderTreePosition::insert(RenderObject& renderer)
+{
+    ASSERT(m_parent);
+    ASSERT(m_hasValidNextSibling);
+    m_parent->addChild(&renderer, m_nextSibling);
+}
+
+void RenderTreePosition::computeNextSibling(const Node& node, ContainerNode& renderingParentNode)
+{
+    ASSERT(!node.renderer());
+    if (m_hasValidNextSibling) {
+        // Stop validating at some point so the assert doesn't make us O(N^2) on debug builds.
+        ASSERT(++m_assertionLimitCounter > 20 || nextSiblingRenderer(node, renderingParentNode) == m_nextSibling);
+        return;
+    }
+    m_nextSibling = nextSiblingRenderer(node, renderingParentNode);
+    m_hasValidNextSibling = true;
+}
+
+void RenderTreePosition::invalidateNextSibling(const RenderObject& siblingRenderer)
+{
+    if (!m_hasValidNextSibling)
+        return;
+    if (m_nextSibling == &siblingRenderer)
+        m_hasValidNextSibling = false;
+}
+
 static bool shouldCreateRenderer(const Element& element, const ContainerNode& renderingParent)
 {
     if (!element.document().shouldCreateRenderers())
@@ -165,17 +249,18 @@
     return true;
 }
 
-static PassRef<RenderStyle> styleForElement(Element& element, RenderStyle& parentStyle)
+static PassRef<RenderStyle> styleForElement(Element& element, ContainerNode& renderingParentNode)
 {
-    if (element.hasCustomStyleResolveCallbacks()) {
-        if (RefPtr<RenderStyle> style = element.customStyleForRenderer(parentStyle))
+    RenderStyle* parentStyle = renderingParentNode.renderStyle();
+    if (element.hasCustomStyleResolveCallbacks() && parentStyle) {
+        if (RefPtr<RenderStyle> style = element.customStyleForRenderer(*parentStyle))
             return style.releaseNonNull();
     }
-    return element.document().ensureStyleResolver().styleForElement(&element, &parentStyle);
+    return element.document().ensureStyleResolver().styleForElement(&element, parentStyle);
 }
 
 // Check the specific case of elements that are children of regions but are flowed into a flow thread themselves.
-static bool elementInsideRegionNeedsRenderer(Element& element, const ContainerNode& renderingParentNode, RefPtr<RenderStyle>& style)
+static bool elementInsideRegionNeedsRenderer(Element& element, ContainerNode& renderingParentNode, RefPtr<RenderStyle>& style)
 {
 #if ENABLE(CSS_REGIONS)
     // The parent of a region should always be an element.
@@ -187,7 +272,7 @@
         return false;
 
     if (!style)
-        style = styleForElement(element, *renderingParentNode.renderStyle());
+        style = styleForElement(element, renderingParentNode);
 
     // Children of this element will only be allowed to be flowed into other flow-threads if display is NOT none.
     if (element.rendererIsNeeded(*style))
@@ -215,7 +300,7 @@
 }
 #endif
 
-static void createRendererIfNeeded(Element& element, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle> resolvedStyle)
+static void createRendererIfNeeded(Element& element, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
 {
     ASSERT(!element.renderer());
 
@@ -227,7 +312,7 @@
         return;
 
     if (!style)
-        style = styleForElement(element, *renderingParentNode.renderStyle());
+        style = styleForElement(element, renderingParentNode);
 
     RenderNamedFlowThread* parentFlowRenderer = 0;
 #if ENABLE(CSS_REGIONS)
@@ -237,28 +322,23 @@
     if (!element.rendererIsNeeded(*style))
         return;
 
-    RenderElement* parentRenderer;
-    RenderObject* nextRenderer;
-    if (parentFlowRenderer) {
-        parentRenderer = parentFlowRenderer;
-        nextRenderer = parentFlowRenderer->nextRendererForElement(element);
-    } else {
-        // FIXME: Make this path Element only, handle the root special case separately.
-        parentRenderer = renderingParentNode.renderer();
-        nextRenderer = nextSiblingRenderer(element, renderingParentNode);
-    }
+    renderTreePosition.computeNextSibling(element, renderingParentNode);
 
+    RenderTreePosition insertionPosition = parentFlowRenderer
+        ? RenderTreePosition(parentFlowRenderer, parentFlowRenderer->nextRendererForElement(element))
+        : renderTreePosition;
+
     RenderElement* newRenderer = element.createElementRenderer(style.releaseNonNull()).leakPtr();
     if (!newRenderer)
         return;
-    if (!parentRenderer->isChildAllowed(*newRenderer, newRenderer->style())) {
+    if (!insertionPosition.canInsert(*newRenderer)) {
         newRenderer->destroy();
         return;
     }
 
     // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
     // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
-    newRenderer->setFlowThreadState(parentRenderer->flowThreadState());
+    newRenderer->setFlowThreadState(insertionPosition.parent()->flowThreadState());
 
     // Code below updateAnimations() can depend on Element::renderer() already being set.
     element.setRenderer(newRenderer);
@@ -272,13 +352,13 @@
 #if ENABLE(FULLSCREEN_API)
     Document& document = element.document();
     if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) {
-        newRenderer = RenderFullScreen::wrapRenderer(newRenderer, parentRenderer, document);
+        newRenderer = RenderFullScreen::wrapRenderer(newRenderer, insertionPosition.parent(), document);
         if (!newRenderer)
             return;
     }
 #endif
     // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer.
-    parentRenderer->addChild(newRenderer, nextRenderer);
+    insertionPosition.insert(*newRenderer);
 }
 
 static RenderObject* previousSiblingRenderer(const Text& textNode)
@@ -367,34 +447,33 @@
     return true;
 }
 
-static void createTextRendererIfNeeded(Text& textNode, ContainerNode& renderingParentNode)
+static void createTextRendererIfNeeded(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
 {
     ASSERT(!textNode.renderer());
 
     if (!textRendererIsNeeded(textNode, renderingParentNode))
         return;
-    RenderElement& parentRenderer = *renderingParentNode.renderer();
-    const auto& style = parentRenderer.style();
 
-    auto newRenderer = textNode.createTextRenderer(style);
+    auto newRenderer = textNode.createTextRenderer(*renderingParentNode.renderStyle());
     ASSERT(newRenderer);
 
-    if (!parentRenderer.isChildAllowed(*newRenderer, style))
+    renderTreePosition.computeNextSibling(textNode, renderingParentNode);
+
+    if (!renderTreePosition.canInsert(*newRenderer))
         return;
 
     // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
     // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
-    newRenderer->setFlowThreadState(parentRenderer.flowThreadState());
+    newRenderer->setFlowThreadState(renderTreePosition.parent()->flowThreadState());
 
-    RenderObject* nextRenderer = nextSiblingRenderer(textNode, renderingParentNode);
     textNode.setRenderer(newRenderer.get());
     // Parent takes care of the animations, no need to call setAnimatableStyle.
-    parentRenderer.addChild(newRenderer.leakPtr(), nextRenderer);
+    renderTreePosition.insert(*newRenderer.leakPtr());
 }
 
-void attachTextRenderer(Text& textNode, ContainerNode& renderingParent)
+void attachTextRenderer(Text& textNode, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
 {
-    createTextRendererIfNeeded(textNode, renderingParent);
+    createTextRendererIfNeeded(textNode, renderingParentNode, renderTreePosition);
 
     textNode.clearNeedsStyleRecalc();
 }
@@ -413,55 +492,64 @@
         return;
 
     bool hadRenderer = textNode.renderer();
-    resolveTextNode(textNode, *renderingParentNode);
 
+    RenderTreePosition renderTreePosition(renderingParentNode->renderer());
+    resolveTextNode(textNode, *renderingParentNode, renderTreePosition);
+
     if (hadRenderer && textNode.renderer())
         textNode.renderer()->setTextWithOffset(textNode.dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
 }
 
-static void attachChildren(ContainerNode& current, ContainerNode& renderingParentNode)
+static void attachChildren(ContainerNode& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
 {
     for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
         ASSERT((!child->renderer() || child->inNamedFlow()) || current.shadowRoot());
-        if (child->renderer())
+        if (child->renderer()) {
+            renderTreePosition.invalidateNextSibling(*child->renderer());
             continue;
+        }
         if (child->isTextNode()) {
-            attachTextRenderer(*toText(child), renderingParentNode);
+            attachTextRenderer(*toText(child), renderingParentNode, renderTreePosition);
             continue;
         }
         if (child->isElementNode())
-            attachRenderTree(*toElement(child), renderingParentNode, nullptr);
+            attachRenderTree(*toElement(child), renderingParentNode, renderTreePosition, nullptr);
     }
 }
 
-static void attachDistributedChildren(InsertionPoint& insertionPoint, ContainerNode& renderingParentNode)
+static void attachDistributedChildren(InsertionPoint& insertionPoint, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
 {
     if (ShadowRoot* shadowRoot = insertionPoint.containingShadowRoot())
         ContentDistributor::ensureDistribution(shadowRoot);
 
     for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) {
+        if (current->renderer())
+            renderTreePosition.invalidateNextSibling(*current->renderer());
         if (current->isTextNode()) {
             if (current->renderer())
                 continue;
-            attachTextRenderer(*toText(current), renderingParentNode);
+            attachTextRenderer(*toText(current), renderingParentNode, renderTreePosition);
             continue;
         }
         if (current->isElementNode()) {
             Element& currentElement = toElement(*current);
             if (currentElement.renderer())
                 detachRenderTree(currentElement);
-            attachRenderTree(currentElement, renderingParentNode, nullptr);
+            attachRenderTree(currentElement, renderingParentNode, renderTreePosition, nullptr);
         }
     }
     // Use actual children as fallback content.
     if (!insertionPoint.hasDistribution())
-        attachChildren(insertionPoint, renderingParentNode);
+        attachChildren(insertionPoint, renderingParentNode, renderTreePosition);
 }
 
 static void attachShadowRoot(ShadowRoot& shadowRoot)
 {
-    attachChildren(shadowRoot, *shadowRoot.hostElement());
+    ASSERT(shadowRoot.hostElement());
 
+    RenderTreePosition renderTreePosition(shadowRoot.hostElement()->renderer());
+    attachChildren(shadowRoot, *shadowRoot.hostElement(), renderTreePosition);
+
     shadowRoot.clearNeedsStyleRecalc();
     shadowRoot.clearChildNeedsStyleRecalc();
 }
@@ -507,16 +595,16 @@
     return true;
 }
 
-static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId)
+static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
 {
     if (!needsPseudoElement(current, pseudoId))
         return;
     RefPtr<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId);
     setBeforeOrAfterPseudoElement(current, pseudoElement, pseudoId);
-    attachRenderTree(*pseudoElement, current, nullptr);
+    attachRenderTree(*pseudoElement, current, renderTreePosition, nullptr);
 }
 
-static void attachRenderTree(Element& current, ContainerNode& renderingParentNode, PassRefPtr<RenderStyle> resolvedStyle)
+static void attachRenderTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
 {
     PostResolutionCallbackDisabler callbackDisabler(current.document());
     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
@@ -524,15 +612,16 @@
     if (current.hasCustomStyleResolveCallbacks())
         current.willAttachRenderers();
 
-    createRendererIfNeeded(current, renderingParentNode, resolvedStyle);
+    createRendererIfNeeded(current, renderingParentNode, renderTreePosition, resolvedStyle);
 
     if (current.parentElement() && current.parentElement()->isInCanvasSubtree())
         current.setIsInCanvasSubtree(true);
 
-    attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE);
-
     StyleResolverParentPusher parentPusher(&current);
 
+    RenderTreePosition childRenderTreePosition(current.renderer());
+    attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE, childRenderTreePosition);
+
     if (ShadowRoot* shadowRoot = current.shadowRoot()) {
         parentPusher.push();
         attachShadowRoot(*shadowRoot);
@@ -540,9 +629,9 @@
         parentPusher.push();
 
     if (isInsertionPoint(current))
-        attachDistributedChildren(toInsertionPoint(current), renderingParentNode);
+        attachDistributedChildren(toInsertionPoint(current), renderingParentNode, renderTreePosition);
     else
-        attachChildren(current, current);
+        attachChildren(current, current, childRenderTreePosition);
 
     current.clearNeedsStyleRecalc();
     current.clearChildNeedsStyleRecalc();
@@ -550,7 +639,7 @@
     if (AXObjectCache* cache = current.document().axObjectCache())
         cache->updateCacheAfterNodeIsAttached(&current);
 
-    attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER);
+    attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER, childRenderTreePosition);
 
     current.updateFocusAppearanceAfterAttachIfNeeded();
     
@@ -652,7 +741,7 @@
     return false;
 }
 
-static Change resolveLocal(Element& current, ContainerNode& renderingParentNode, Change inheritedChange)
+static Change resolveLocal(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change inheritedChange)
 {
     Change localChange = Detach;
     RefPtr<RenderStyle> newStyle;
@@ -660,13 +749,13 @@
 
     Document& document = current.document();
     if (currentStyle && current.styleChangeType() != ReconstructRenderTree) {
-        newStyle = styleForElement(current, *renderingParentNode.renderStyle());
+        newStyle = styleForElement(current, renderingParentNode);
         localChange = determineChange(currentStyle.get(), newStyle.get());
     }
     if (localChange == Detach) {
         if (current.renderer() || current.inNamedFlow())
             detachRenderTree(current, ReattachDetach);
-        attachRenderTree(current, renderingParentNode, newStyle.release());
+        attachRenderTree(current, renderingParentNode, renderTreePosition, newStyle.release());
         invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(current);
 
         return Detach;
@@ -698,7 +787,7 @@
     return localChange;
 }
 
-void resolveTextNode(Text& text, ContainerNode& renderingParentNode)
+void resolveTextNode(Text& text, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition)
 {
     text.clearNeedsStyleRecalc();
 
@@ -713,35 +802,39 @@
     }
     if (!needsRenderer)
         return;
-    attachTextRenderer(text, renderingParentNode);
+    attachTextRenderer(text, renderingParentNode, renderTreePosition);
     invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
 }
 
-static void resolveShadowTree(ShadowRoot& shadowRoot, Style::Change change)
+static void resolveShadowTree(ShadowRoot& shadowRoot, Element& host, Style::Change change)
 {
+    ASSERT(shadowRoot.hostElement() == &host);
+    RenderTreePosition renderTreePosition(host.renderer());
     for (Node* child = shadowRoot.firstChild(); child; child = child->nextSibling()) {
+        if (child->renderer())
+            renderTreePosition.invalidateNextSibling(*child->renderer());
         if (child->isTextNode() && child->needsStyleRecalc()) {
-            resolveTextNode(*toText(child), *shadowRoot.hostElement());
+            resolveTextNode(*toText(child), host, renderTreePosition);
             continue;
         }
         if (child->isElementNode())
-            resolveTree(*toElement(child), *shadowRoot.hostElement(), change);
+            resolveTree(*toElement(child), host, renderTreePosition, change);
     }
 
     shadowRoot.clearNeedsStyleRecalc();
     shadowRoot.clearChildNeedsStyleRecalc();
 }
 
-static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId)
+static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
 {
     if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) {
         if (needsPseudoElement(current, pseudoId))
-            resolveTree(*existingPseudoElement, current, current.needsStyleRecalc() ? Force : change);
+            resolveTree(*existingPseudoElement, current, renderTreePosition, current.needsStyleRecalc() ? Force : change);
         else
             clearBeforeOrAfterPseudoElement(current, pseudoId);
         return;
     }
-    attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId);
+    attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId, renderTreePosition);
 }
 
 #if PLATFORM(IOS)
@@ -796,7 +889,7 @@
 };
 #endif // PLATFORM(IOS)
 
-void resolveTree(Element& current, ContainerNode& renderingParentNode, Change change)
+void resolveTree(Element& current, ContainerNode& renderingParentNode, RenderTreePosition& renderTreePosition, Change change)
 {
     ASSERT(change != Detach);
 
@@ -817,7 +910,7 @@
         current.resetComputedStyle();
 
     if (hasParentStyle && (change >= Inherit || current.needsStyleRecalc()))
-        change = resolveLocal(current, renderingParentNode, change);
+        change = resolveLocal(current, renderingParentNode, renderTreePosition, change);
 
     if (change != Detach) {
         StyleResolverParentPusher parentPusher(&current);
@@ -825,11 +918,12 @@
         if (ShadowRoot* shadowRoot = current.shadowRoot()) {
             if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) {
                 parentPusher.push();
-                resolveShadowTree(*shadowRoot, change);
+                resolveShadowTree(*shadowRoot, current, change);
             }
         }
 
-        updateBeforeOrAfterPseudoElement(current, change, BEFORE);
+        RenderTreePosition childRenderTreePosition(current.renderer());
+        updateBeforeOrAfterPseudoElement(current, change, BEFORE, childRenderTreePosition);
 
         // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar.
         // For now we will just worry about the common case, since it's a lot trickier to get the second case right
@@ -837,8 +931,10 @@
         bool forceCheckOfNextElementSibling = false;
         bool forceCheckOfAnyElementSibling = false;
         for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
+            if (child->renderer())
+                childRenderTreePosition.invalidateNextSibling(*child->renderer());
             if (child->isTextNode() && child->needsStyleRecalc()) {
-                resolveTextNode(*toText(child), current);
+                resolveTextNode(*toText(child), current, childRenderTreePosition);
                 continue;
             }
             if (!child->isElementNode())
@@ -849,13 +945,13 @@
                 childElement->setNeedsStyleRecalc();
             if (change >= Inherit || childElement->childNeedsStyleRecalc() || childElement->needsStyleRecalc()) {
                 parentPusher.push();
-                resolveTree(*childElement, current, change);
+                resolveTree(*childElement, current, childRenderTreePosition, change);
             }
             forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules;
             forceCheckOfAnyElementSibling = forceCheckOfAnyElementSibling || (childRulesChanged && hasIndirectAdjacentRules);
         }
 
-        updateBeforeOrAfterPseudoElement(current, change, AFTER);
+        updateBeforeOrAfterPseudoElement(current, change, AFTER, childRenderTreePosition);
     }
 
     current.clearNeedsStyleRecalc();
@@ -890,7 +986,8 @@
         return;
     if (change < Inherit && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
         return;
-    resolveTree(*documentElement, document, change);
+    RenderTreePosition renderTreePosition(document.renderView());
+    resolveTree(*documentElement, document, renderTreePosition, change);
 }
 
 void detachRenderTree(Element& element)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to