Resolve style iteratively
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Feb 2016 18:29:40 +0000 (18:29 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Feb 2016 18:29:40 +0000 (18:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154355

Reviewed by Andreas Kling.

Instead of a set of recursive functions use ComposedTreeIterator for traversing the DOM
tree in composed tree order.

This, along with maintaining explicit parent stack makes style resolve code more tractable
for future work.

It also makes the ComposedTreeIterator the definite authority for the shape of the composed tree
instead of duplicating it as a set of recursive style resolve functions. This eliminates
a significant source of bugs and confusion.

The render tree building code path remains recursive for now.

* css/StyleInvalidationAnalysis.cpp:
(WebCore::StyleInvalidationAnalysis::invalidateIfNeeded):

    Invalidate the host element instead of the shadow root. This reduces need for special handling for shadow roots.

* dom/ComposedTreeIterator.cpp:
(WebCore::ComposedTreeIterator::initializeContextStack):
(WebCore::ComposedTreeIterator::dropAssertions):

    Add support for dropping DOM mutation assertions.

(WebCore::ComposedTreeIterator::traverseShadowRoot):
* dom/ComposedTreeIterator.h:
(WebCore::ComposedTreeIterator::context):
(WebCore::ComposedTreeIterator::current):
* dom/PseudoElement.h:
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::TreeResolver):
(WebCore::Style::TreeResolver::Scope::Scope):
(WebCore::Style::TreeResolver::Parent::Parent):
(WebCore::Style::TreeResolver::pushScope):
(WebCore::Style::resetStyleForNonRenderedDescendants):
(WebCore::Style::pseudoStyleCacheIsInvalid):
(WebCore::Style::TreeResolver::resolveElement):
(WebCore::Style::resolveTextNode):
(WebCore::Style::TreeResolver::resolveBeforeOrAfterPseudoElement):
(WebCore::Style::TreeResolver::pushParent):
(WebCore::Style::TreeResolver::popParent):
(WebCore::Style::TreeResolver::popParentsToDepth):

    Maintain explicit parent stack.

(WebCore::Style::TreeResolver::resolveComposedTree):

    The main loop that iterates over the composed tree and computes style for dirty elements.

(WebCore::Style::TreeResolver::resolve):
(WebCore::Style::detachRenderTree):
(WebCore::Style::TreeResolver::resolveLocally): Deleted.
(WebCore::Style::TreeResolver::resolveChildAtShadowBoundary): Deleted.
(WebCore::Style::TreeResolver::resolveShadowTree): Deleted.
(WebCore::Style::TreeResolver::resolveChildren): Deleted.
(WebCore::Style::TreeResolver::resolveSlotAssignees): Deleted.
(WebCore::Style::TreeResolver::resolveRecursively): Deleted.

    Recursive functions go away.

* style/StyleTreeResolver.h:
(WebCore::Style::TreeResolver::scope):
(WebCore::Style::TreeResolver::parent):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196864 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/css/StyleInvalidationAnalysis.cpp
Source/WebCore/dom/ComposedTreeIterator.cpp
Source/WebCore/dom/ComposedTreeIterator.h
Source/WebCore/dom/PseudoElement.h
Source/WebCore/style/StyleTreeResolver.cpp
Source/WebCore/style/StyleTreeResolver.h

index 19504c9..f8a4782 100644 (file)
@@ -1,3 +1,73 @@
+2016-02-20  Antti Koivisto  <antti@apple.com>
+
+        Resolve style iteratively
+        https://bugs.webkit.org/show_bug.cgi?id=154355
+
+        Reviewed by Andreas Kling.
+
+        Instead of a set of recursive functions use ComposedTreeIterator for traversing the DOM
+        tree in composed tree order.
+
+        This, along with maintaining explicit parent stack makes style resolve code more tractable
+        for future work.
+
+        It also makes the ComposedTreeIterator the definite authority for the shape of the composed tree
+        instead of duplicating it as a set of recursive style resolve functions. This eliminates
+        a significant source of bugs and confusion.
+
+        The render tree building code path remains recursive for now.
+
+        * css/StyleInvalidationAnalysis.cpp:
+        (WebCore::StyleInvalidationAnalysis::invalidateIfNeeded):
+
+            Invalidate the host element instead of the shadow root. This reduces need for special handling for shadow roots.
+
+        * dom/ComposedTreeIterator.cpp:
+        (WebCore::ComposedTreeIterator::initializeContextStack):
+        (WebCore::ComposedTreeIterator::dropAssertions):
+
+            Add support for dropping DOM mutation assertions.
+
+        (WebCore::ComposedTreeIterator::traverseShadowRoot):
+        * dom/ComposedTreeIterator.h:
+        (WebCore::ComposedTreeIterator::context):
+        (WebCore::ComposedTreeIterator::current):
+        * dom/PseudoElement.h:
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::TreeResolver):
+        (WebCore::Style::TreeResolver::Scope::Scope):
+        (WebCore::Style::TreeResolver::Parent::Parent):
+        (WebCore::Style::TreeResolver::pushScope):
+        (WebCore::Style::resetStyleForNonRenderedDescendants):
+        (WebCore::Style::pseudoStyleCacheIsInvalid):
+        (WebCore::Style::TreeResolver::resolveElement):
+        (WebCore::Style::resolveTextNode):
+        (WebCore::Style::TreeResolver::resolveBeforeOrAfterPseudoElement):
+        (WebCore::Style::TreeResolver::pushParent):
+        (WebCore::Style::TreeResolver::popParent):
+        (WebCore::Style::TreeResolver::popParentsToDepth):
+
+            Maintain explicit parent stack.
+
+        (WebCore::Style::TreeResolver::resolveComposedTree):
+
+            The main loop that iterates over the composed tree and computes style for dirty elements.
+
+        (WebCore::Style::TreeResolver::resolve):
+        (WebCore::Style::detachRenderTree):
+        (WebCore::Style::TreeResolver::resolveLocally): Deleted.
+        (WebCore::Style::TreeResolver::resolveChildAtShadowBoundary): Deleted.
+        (WebCore::Style::TreeResolver::resolveShadowTree): Deleted.
+        (WebCore::Style::TreeResolver::resolveChildren): Deleted.
+        (WebCore::Style::TreeResolver::resolveSlotAssignees): Deleted.
+        (WebCore::Style::TreeResolver::resolveRecursively): Deleted.
+
+            Recursive functions go away.
+
+        * style/StyleTreeResolver.h:
+        (WebCore::Style::TreeResolver::scope):
+        (WebCore::Style::TreeResolver::parent):
+
 2016-02-20  Andreas Kling  <akling@apple.com>
 
         REGRESSION(r196780): Fake memory handler takes too long to run now.
index c6c39d8..8b058a8 100644 (file)
@@ -99,8 +99,8 @@ StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidat
 {
     if (m_hasShadowPseudoElementRulesInAuthorSheet) {
         // FIXME: This could do actual rule matching too.
-        if (auto* shadowRoot = element.shadowRoot())
-            shadowRoot->setNeedsStyleRecalc();
+        if (element.shadowRoot())
+            element.setNeedsStyleRecalc();
     }
 
     switch (element.styleChangeType()) {
index e7721d6..feccd35 100644 (file)
@@ -106,6 +106,13 @@ void ComposedTreeIterator::initializeContextStack(ContainerNode& root, Node& cur
     m_contextStack.reverse();
 }
 
+void ComposedTreeIterator::dropAssertions()
+{
+    for (auto& context : m_contextStack)
+        context.iterator.dropAssertions();
+    m_didDropAssertions = true;
+}
+
 void ComposedTreeIterator::traverseShadowRoot(ShadowRoot& shadowRoot)
 {
     Context shadowContext(shadowRoot);
@@ -115,6 +122,9 @@ void ComposedTreeIterator::traverseShadowRoot(ShadowRoot& shadowRoot)
         return;
     }
 
+    if (m_didDropAssertions)
+        shadowContext.iterator.dropAssertions();
+
     m_contextStack.append(WTFMove(shadowContext));
 }
 
index 06a9b33..f3d4650 100644 (file)
@@ -54,6 +54,8 @@ public:
 
     unsigned depth() const;
 
+    void dropAssertions();
+
 private:
     void initializeContextStack(ContainerNode& root, Node& current);
     void traverseNextInShadowTree();
@@ -81,6 +83,7 @@ private:
     const Context& context() const { return m_contextStack.last(); }
     Node& current() { return *context().iterator; }
 
+    bool m_didDropAssertions { false };
     Vector<Context, 4> m_contextStack;
 };
 
index 581e127..0e23464 100644 (file)
@@ -47,6 +47,7 @@ public:
 
     virtual RefPtr<RenderStyle> customStyleForRenderer(RenderStyle& parentStyle) override;
     virtual void didAttachRenderers() override;
+    virtual void didRecalcStyle(Style::Change) override;
     virtual bool rendererIsNeeded(const RenderStyle&) override;
 
     // As per http://dev.w3.org/csswg/css3-regions/#flow-into, pseudo-elements such as ::first-line, ::first-letter, ::before or ::after
@@ -63,7 +64,6 @@ public:
 private:
     PseudoElement(Element&, PseudoId);
 
-    virtual void didRecalcStyle(Style::Change) override;
     virtual PseudoId customPseudoId() const override { return m_pseudoId; }
 
     Element* m_hostElement;
index d67aa87..2743dee 100644 (file)
@@ -112,8 +112,6 @@ TreeResolver::TreeResolver(Document& document)
     : m_document(document)
 {
     ensurePlaceholderStyle(document);
-
-    m_scopeStack.append(adoptRef(*new Scope(document)));
 }
 
 TreeResolver::Scope::Scope(Document& document)
@@ -130,6 +128,22 @@ TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
 {
 }
 
+TreeResolver::Parent::Parent(Document& document, Change change)
+    : element(nullptr)
+    , style(*document.renderStyle())
+    , renderTreePosition(*document.renderView())
+    , change(change)
+{
+}
+
+TreeResolver::Parent::Parent(Element& element, RenderStyle& style, RenderTreePosition renderTreePosition, Change change)
+    : element(&element)
+    , style(style)
+    , renderTreePosition(renderTreePosition)
+    , change(change)
+{
+}
+
 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
 {
     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
@@ -425,20 +439,18 @@ static void clearBeforeOrAfterPseudoElement(Element& current, PseudoId pseudoId)
 
 static void resetStyleForNonRenderedDescendants(Element& current)
 {
+    // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator.
     ASSERT(!current.renderer());
     bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
     for (auto& child : childrenOfType<Element>(current)) {
         ASSERT(!child.renderer());
-        if (elementNeedingStyleRecalcAffectsNextSiblingElementStyle) {
-            if (child.styleIsAffectedByPreviousSibling())
-                child.setNeedsStyleRecalc();
+        bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
+        if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
             elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
-        }
 
-        if (child.needsStyleRecalc()) {
+        if (child.needsStyleRecalc() || affectedByPreviousSibling) {
             child.resetComputedStyle();
             child.clearNeedsStyleRecalc();
-            elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
         }
 
         if (child.childNeedsStyleRecalc()) {
@@ -652,28 +664,32 @@ static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newS
     return false;
 }
 
-Change TreeResolver::resolveLocally(Element& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Change inheritedChange)
+Change TreeResolver::resolveElement(Element& current)
 {
     Change localChange = Detach;
     RefPtr<RenderStyle> newStyle;
     RefPtr<RenderStyle> currentStyle = current.renderStyle();
 
     if (currentStyle && current.styleChangeType() != ReconstructRenderTree) {
-        Ref<RenderStyle> style(styleForElement(current, inheritedStyle));
+        Ref<RenderStyle> style(styleForElement(current, parent().style));
         newStyle = style.ptr();
         localChange = determineChange(*currentStyle, style);
     }
     if (localChange == Detach) {
         if (current.renderer() || current.isNamedFlowContentNode())
             detachRenderTree(current, ReattachDetach);
-        createRenderTreeRecursively(current, inheritedStyle, renderTreePosition, newStyle.release());
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+        else if (is<HTMLSlotElement>(current))
+            detachRenderTree(current, ReattachDetach);
+#endif
+        createRenderTreeRecursively(current, parent().style, parent().renderTreePosition, newStyle.release());
         invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(current);
 
         return Detach;
     }
 
     if (RenderElement* renderer = current.renderer()) {
-        if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (inheritedChange == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange)
+        if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (parent().change == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange)
             renderer->setAnimatableStyle(*newStyle, current.styleChangeType() == SyntheticStyleChange ? StyleDifferenceRecompositeLayer : StyleDifferenceEqual);
         else if (current.needsStyleRecalc()) {
             // Although no change occurred, we use the new style so that the cousin style sharing code won't get
@@ -689,9 +705,7 @@ Change TreeResolver::resolveLocally(Element& current, RenderStyle& inheritedStyl
         scope().styleResolver.invalidateMatchedPropertiesCache();
         return Force;
     }
-    if (inheritedChange == Force)
-        return Force;
-    if (current.styleChangeType() >= FullStyleChange)
+    if (parent().change == Force || current.styleChangeType() >= FullStyleChange)
         return Force;
 
     return localChange;
@@ -716,48 +730,28 @@ void resolveTextNode(Text& text, RenderTreePosition& renderTreePosition)
     invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
 }
 
-void TreeResolver::resolveChildAtShadowBoundary(Node& child, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Style::Change change)
+void TreeResolver::resolveBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
 {
-    if (auto* renderer = child.renderer())
-        renderTreePosition.invalidateNextSibling(*renderer);
-
-    if (is<Text>(child) && child.needsStyleRecalc()) {
-        resolveTextNode(downcast<Text>(child), renderTreePosition);
+    if (!current.renderer())
+        return;
+    PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId);
+    if (!existingPseudoElement) {
+        createRenderTreeForBeforeOrAfterPseudoElement(current, pseudoId, renderTreePosition);
         return;
     }
-    if (is<Element>(child))
-        resolveRecursively(downcast<Element>(child), inheritedStyle, renderTreePosition, change);
-}
-
-void TreeResolver::resolveShadowTree(Style::Change change, RenderStyle& inheritedStyle)
-{
-    ASSERT(scope().shadowRoot);
-    auto& host = *scope().shadowRoot->host();
-    ASSERT(host.renderer());
-    if (scope().shadowRoot->styleChangeType() >= FullStyleChange)
-        change = Force;
-    RenderTreePosition renderTreePosition(*host.renderer());
-    for (auto* child = scope().shadowRoot->firstChild(); child; child = child->nextSibling())
-        resolveChildAtShadowBoundary(*child, inheritedStyle, renderTreePosition, change);
-
-    scope().shadowRoot->clearNeedsStyleRecalc();
-    scope().shadowRoot->clearChildNeedsStyleRecalc();
-}
 
-void TreeResolver::resolveBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
-{
-    ASSERT(current.renderer());
-    if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) {
-        if (existingPseudoElement->renderer())
-            renderTreePosition.invalidateNextSibling(*existingPseudoElement->renderer());
+    if (existingPseudoElement->renderer())
+        renderTreePosition.invalidateNextSibling(*existingPseudoElement->renderer());
 
-        if (needsPseudoElement(current, pseudoId))
-            resolveRecursively(*existingPseudoElement, current.renderer()->style(), renderTreePosition, current.needsStyleRecalc() ? Force : change);
-        else
-            clearBeforeOrAfterPseudoElement(current, pseudoId);
+    if (change == NoChange && !existingPseudoElement->needsStyleRecalc())
         return;
-    }
-    createRenderTreeForBeforeOrAfterPseudoElement(current, pseudoId, renderTreePosition);
+
+    if (needsPseudoElement(current, pseudoId)) {
+        auto change = resolveElement(*existingPseudoElement);
+        existingPseudoElement->didRecalcStyle(change);
+        existingPseudoElement->clearNeedsStyleRecalc();
+    } else
+        clearBeforeOrAfterPseudoElement(current, pseudoId);
 }
 
 #if PLATFORM(IOS)
@@ -814,112 +808,155 @@ private:
 };
 #endif // PLATFORM(IOS)
 
-void TreeResolver::resolveChildren(Element& current, RenderStyle& inheritedStyle, Change change, RenderTreePosition& childRenderTreePosition)
+void TreeResolver::pushParent(Element& element, RenderStyle& style, RenderTreePosition renderTreePosition, Change change)
 {
-    SelectorFilterPusher selectorFilterPusher(scope().selectorFilter, current, SelectorFilterPusher::NoPush);
+    scope().selectorFilter.pushParent(&element);
 
-    bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
-    for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
-        if (RenderObject* childRenderer = child->renderer())
-            childRenderTreePosition.invalidateNextSibling(*childRenderer);
-        if (is<Text>(*child) && child->needsStyleRecalc()) {
-            resolveTextNode(downcast<Text>(*child), childRenderTreePosition);
-            continue;
-        }
-        if (!is<Element>(*child))
-            continue;
+    Parent parent(element, style, renderTreePosition, change);
 
-        Element& childElement = downcast<Element>(*child);
-        if (elementNeedingStyleRecalcAffectsNextSiblingElementStyle) {
-            if (childElement.styleIsAffectedByPreviousSibling())
-                childElement.setNeedsStyleRecalc();
-            elementNeedingStyleRecalcAffectsNextSiblingElementStyle = childElement.affectsNextSiblingElementStyle();
-        } else if (childElement.needsStyleRecalc())
-            elementNeedingStyleRecalcAffectsNextSiblingElementStyle = childElement.affectsNextSiblingElementStyle();
-        if (change >= Inherit || childElement.childNeedsStyleRecalc() || childElement.needsStyleRecalc()) {
-            selectorFilterPusher.push();
-            resolveRecursively(childElement, inheritedStyle, childRenderTreePosition, change);
-        }
+    if (auto* shadowRoot = element.shadowRoot()) {
+        pushScope(*shadowRoot);
+        parent.didPushScope = true;
+    }
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+    else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
+        pushEnclosingScope();
+        parent.didPushScope = true;
     }
+#endif
+
+    m_parentStack.append(WTFMove(parent));
+
+    resolveBeforeOrAfterPseudoElement(element, change, BEFORE, renderTreePosition);
 }
 
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
-void TreeResolver::resolveSlotAssignees(HTMLSlotElement& slot, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Change change)
+void TreeResolver::popParent()
 {
-    if (auto* assignedNodes = slot.assignedNodes()) {
-        pushEnclosingScope();
-        for (auto* child : *assignedNodes)
-            resolveChildAtShadowBoundary(*child, inheritedStyle, renderTreePosition, change);
+    auto& parentElement = *parent().element;
+
+    resolveBeforeOrAfterPseudoElement(parentElement, parent().change, AFTER, parent().renderTreePosition);
+
+    parentElement.clearNeedsStyleRecalc();
+    parentElement.clearChildNeedsStyleRecalc();
+
+    if (parent().didPushScope)
         popScope();
-    } else
-        resolveChildren(slot, inheritedStyle, change, renderTreePosition);
 
-    slot.clearNeedsStyleRecalc();
-    slot.clearChildNeedsStyleRecalc();
+    scope().selectorFilter.popParent();
+
+    m_parentStack.removeLast();
 }
-#endif
 
-void TreeResolver::resolveRecursively(Element& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Change change)
+void TreeResolver::popParentsToDepth(unsigned depth)
 {
-    ASSERT(change != Detach);
+    ASSERT(depth);
+    ASSERT(m_parentStack.size() >= depth);
 
-#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
-    if (is<HTMLSlotElement>(current)) {
-        resolveSlotAssignees(downcast<HTMLSlotElement>(current), inheritedStyle, renderTreePosition, change);
-        return;
-    }
-#endif
+    while (m_parentStack.size() > depth)
+        popParent();
+}
 
-    if (current.hasCustomStyleResolveCallbacks()) {
-        if (!current.willRecalcStyle(change))
-            return;
-    }
+void TreeResolver::resolveComposedTree()
+{
+    ASSERT(m_parentStack.size() == 1);
+    ASSERT(m_scopeStack.size() == 1);
+
+    auto descendants = composedTreeDescendants(m_document);
+    auto it = descendants.begin();
+    auto end = descendants.end();
+
+    // FIXME: SVG <use> element may cause tree mutations during style recalc.
+    it.dropAssertions();
+
+    while (it != end) {
+        popParentsToDepth(it.depth());
+
+        auto& node = *it;
+        auto& parent = this->parent();
+
+        ASSERT(node.containingShadowRoot() == scope().shadowRoot);
+        ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
+
+        if (auto* existingRenderer = node.renderer())
+            parent.renderTreePosition.invalidateNextSibling(*existingRenderer);
+
+        if (is<Text>(node)) {
+            if (node.needsStyleRecalc())
+                resolveTextNode(downcast<Text>(node), parent.renderTreePosition);
+            it.traverseNextSkippingChildren();
+            continue;
+        }
+
+        auto& element = downcast<Element>(node);
+
+        // FIXME: We should deal with this during style invalidation.
+        bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
+        if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
+            parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle();
+
+        Change change = NoChange;
 
+        bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || affectedByPreviousSibling;
+        if (shouldResolve) {
 #if PLATFORM(IOS)
-    CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&current, current.renderStyle());
+            CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle());
 #endif
+            element.resetComputedStyle();
 
-    if (change > NoChange || current.needsStyleRecalc())
-        current.resetComputedStyle();
+            if (element.hasCustomStyleResolveCallbacks()) {
+                if (!element.willRecalcStyle(parent.change)) {
+                    it.traverseNextSkippingChildren();
+                    continue;
+                }
+            }
+            change = resolveElement(element);
 
-    if (change >= Inherit || current.needsStyleRecalc())
-        change = resolveLocally(current, inheritedStyle, renderTreePosition, change);
+            element.clearNeedsStyleRecalc();
 
-    auto* renderer = current.renderer();
+            if (element.hasCustomStyleResolveCallbacks())
+                element.didRecalcStyle(change);
 
-    if (change != Detach && renderer) {
-        auto* shadowRoot = current.shadowRoot();
-        if (shadowRoot && (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc())) {
-            SelectorFilterPusher selectorFilterPusher(scope().selectorFilter, current);
+            if (change == Detach) {
+                it.traverseNextSkippingChildren();
+                continue;
+            }
 
-            pushScope(*shadowRoot);
-            resolveShadowTree(change, renderer->style());
-            popScope();
+            if (affectedByPreviousSibling)
+                change = Force;
         }
 
-        RenderTreePosition childRenderTreePosition(*renderer);
-        resolveBeforeOrAfterPseudoElement(current, change, BEFORE, childRenderTreePosition);
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+        if (is<HTMLSlotElement>(element)) {
+            // FIXME: We should compute style for the slot and use it as parent style.
+            // FIXME: This should be display:contents check.
+            // Duplicate the style and render tree position from the current context.
+            pushParent(element, parent.style.get(), parent.renderTreePosition, change);
+            it.traverseNext();
+            continue;
+        }
+#endif
+        auto* renderer = element.renderer();
+        if (!renderer) {
+            resetStyleForNonRenderedDescendants(element);
+            element.clearChildNeedsStyleRecalc();
+        }
 
-        bool skipChildren = shadowRoot;
-        if (!skipChildren)
-            resolveChildren(current, renderer->style(), change, childRenderTreePosition);
+        bool shouldIterateChildren = renderer && (element.childNeedsStyleRecalc() || change != NoChange);
+        if (!shouldIterateChildren) {
+            it.traverseNextSkippingChildren();
+            continue;
+        }
+
+        pushParent(element, renderer->style(), RenderTreePosition(*renderer), change);
 
-        resolveBeforeOrAfterPseudoElement(current, change, AFTER, childRenderTreePosition);
+        it.traverseNext();
     }
-    if (change != Detach && !renderer)
-        resetStyleForNonRenderedDescendants(current);
 
-    current.clearNeedsStyleRecalc();
-    current.clearChildNeedsStyleRecalc();
-    
-    if (current.hasCustomStyleResolveCallbacks())
-        current.didRecalcStyle(change);
+    popParentsToDepth(1);
 }
 
 void TreeResolver::resolve(Change change)
 {
-    ASSERT(!scope().shadowRoot);
-
     auto& renderView = *m_document.renderView();
 
     Element* documentElement = m_document.documentElement();
@@ -928,15 +965,21 @@ void TreeResolver::resolve(Change change)
     if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
         return;
 
+    m_scopeStack.append(adoptRef(*new Scope(m_document)));
+
     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
 
-    RenderTreePosition renderTreePosition(renderView);
-    resolveRecursively(*documentElement, *m_document.renderStyle(), renderTreePosition, change);
+    m_parentStack.append(Parent(m_document, change));
+
+    resolveComposedTree();
 
     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
+
+    m_parentStack.clear();
+    m_scopeStack.clear();
 }
 
 void detachRenderTree(Element& element)
index 5aa560f..b5f7c9a 100644 (file)
@@ -27,6 +27,7 @@
 #define StyleTreeResolver_h
 
 #include "RenderStyleConstants.h"
+#include "RenderTreePosition.h"
 #include "SelectorFilter.h"
 #include "StyleChange.h"
 #include "StyleSharingResolver.h"
@@ -41,7 +42,6 @@ class Element;
 class HTMLSlotElement;
 class Node;
 class RenderStyle;
-class RenderTreePosition;
 class Settings;
 class ShadowRoot;
 class StyleResolver;
@@ -56,14 +56,10 @@ public:
     void resolve(Change);
 
 private:
-    void resolveShadowTree(Change, RenderStyle& inheritedStyle);
-
     Ref<RenderStyle> styleForElement(Element&, RenderStyle& inheritedStyle);
 
-    void resolveRecursively(Element&, RenderStyle& inheritedStyle, RenderTreePosition&, Change);
-    Change resolveLocally(Element&, RenderStyle& inheritedStyle, RenderTreePosition&, Change inheritedChange);
-    void resolveChildren(Element&, RenderStyle&, Change, RenderTreePosition&);
-    void resolveChildAtShadowBoundary(Node&, RenderStyle& inheritedStyle, RenderTreePosition&, Change);
+    void resolveComposedTree();
+    Change resolveElement(Element&);
     void resolveBeforeOrAfterPseudoElement(Element&, Change, PseudoId, RenderTreePosition&);
 
     void createRenderTreeRecursively(Element&, RenderStyle&, RenderTreePosition&, RefPtr<RenderStyle>&& resolvedStyle);
@@ -73,7 +69,6 @@ private:
     void createRenderTreeForShadowRoot(ShadowRoot&);
 
 #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
-    void resolveSlotAssignees(HTMLSlotElement&, RenderStyle& inheritedStyle, RenderTreePosition&, Change);
     void createRenderTreeForSlotAssignees(HTMLSlotElement&, RenderStyle& inheritedStyle, RenderTreePosition&);
 #endif
 
@@ -87,13 +82,33 @@ private:
         Scope(Document&);
         Scope(ShadowRoot&, Scope& enclosingScope);
     };
+
+    struct Parent {
+        Element* element;
+        Ref<RenderStyle> style;
+        RenderTreePosition renderTreePosition;
+        Change change;
+        bool didPushScope { false };
+        bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle { false };
+
+        Parent(Document&, Change);
+        Parent(Element&, RenderStyle&, RenderTreePosition, Change);
+    };
+
     Scope& scope() { return m_scopeStack.last(); }
+    Parent& parent() { return m_parentStack.last(); }
+
     void pushScope(ShadowRoot&);
     void pushEnclosingScope();
     void popScope();
 
+    void pushParent(Element&, RenderStyle&, RenderTreePosition, Change);
+    void popParent();
+    void popParentsToDepth(unsigned depth);
+
     Document& m_document;
     Vector<Ref<Scope>, 4> m_scopeStack;
+    Vector<Parent, 32> m_parentStack;
 };
 
 void detachRenderTree(Element&);