Resolve ::before and ::after pseudo elements during style resolution
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Oct 2017 13:41:04 +0000 (13:41 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Oct 2017 13:41:04 +0000 (13:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178339
Source/WebCore:

Reviewed by Ryosuke Niwa.

They are currently resolved during render tree building which creates problems with display:contents and animations.

* dom/PseudoElement.cpp:
(WebCore::PseudoElement::PseudoElement):

    Call InspectorInstrumentation from constructor.

* style/RenderTreeUpdater.cpp:
(WebCore::RenderTreeUpdater::Parent::Parent):
(WebCore::RenderTreeUpdater::updateRenderTree):
(WebCore::RenderTreeUpdater::pushParent):

    Push the full update to the parent stack.

(WebCore::RenderTreeUpdater::popParent):
(WebCore::RenderTreeUpdater::updateBeforeDescendants):
(WebCore::RenderTreeUpdater::updateAfterDescendants):
(WebCore::RenderTreeUpdater::invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded):
* style/RenderTreeUpdater.h:
* style/RenderTreeUpdaterGeneratedContent.cpp:
(WebCore::RenderTreeUpdater::GeneratedContent::updatePseudoElement):

    No need to resolve pseudo style, we have it already.

(WebCore::RenderTreeUpdater::GeneratedContent::needsPseudoElement):
(WebCore::RenderTreeUpdater::GeneratedContent::updateBeforePseudoElement): Deleted.
(WebCore::RenderTreeUpdater::GeneratedContent::updateAfterPseudoElement): Deleted.
* style/RenderTreeUpdaterGeneratedContent.h:
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::resolveElement):
(WebCore::Style::TreeResolver::resolvePseudoStyle):

    Resolve pseudos.

(WebCore::Style::TreeResolver::createAnimatedElementUpdate):

    Make a private member function.

(WebCore::Style::TreeResolver::resolveComposedTree):
* style/StyleTreeResolver.h:
* style/StyleUpdate.cpp:
(WebCore::Style::Update::elementUpdates const):
(WebCore::Style::Update::elementUpdates):

    Bundle the style update for an element and the associated before/after pseudos.

(WebCore::Style::Update::elementStyle const):
(WebCore::Style::Update::elementStyle):
(WebCore::Style::Update::addElement):
(WebCore::Style::Update::elementUpdate const): Deleted.
(WebCore::Style::Update::elementUpdate): Deleted.
* style/StyleUpdate.h:

LayoutTests:

<rdar://problem/35025601>

Reviewed by Ryosuke Niwa.

Add a test verifying a crash noticed with earlier version of this patch.

* fast/css-generated-content/svg-use-crash-expected.html: Added.
* fast/css-generated-content/svg-use-crash.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css-generated-content/svg-use-crash-expected.html [new file with mode: 0644]
LayoutTests/fast/css-generated-content/svg-use-crash.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/PseudoElement.cpp
Source/WebCore/dom/PseudoElement.h
Source/WebCore/style/RenderTreeUpdater.cpp
Source/WebCore/style/RenderTreeUpdater.h
Source/WebCore/style/RenderTreeUpdaterGeneratedContent.cpp
Source/WebCore/style/RenderTreeUpdaterGeneratedContent.h
Source/WebCore/style/StyleTreeResolver.cpp
Source/WebCore/style/StyleTreeResolver.h
Source/WebCore/style/StyleUpdate.cpp
Source/WebCore/style/StyleUpdate.h

index ac6d756..f9269b8 100644 (file)
@@ -1,3 +1,16 @@
+2017-10-18  Antti Koivisto  <antti@apple.com>
+
+        Resolve ::before and ::after pseudo elements during style resolution
+        https://bugs.webkit.org/show_bug.cgi?id=178339
+        <rdar://problem/35025601>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add a test verifying a crash noticed with earlier version of this patch.
+
+        * fast/css-generated-content/svg-use-crash-expected.html: Added.
+        * fast/css-generated-content/svg-use-crash.html: Added.
+
 2017-10-18  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] __proto__ getter should be fast
diff --git a/LayoutTests/fast/css-generated-content/svg-use-crash-expected.html b/LayoutTests/fast/css-generated-content/svg-use-crash-expected.html
new file mode 100644 (file)
index 0000000..88197ba
--- /dev/null
@@ -0,0 +1,8 @@
+This test passes if it doesn't crash.
+<svg width="100" height="100">
+<style>
+circle::before { content: "foo" }
+circle::after { content: "bar" }
+</style>
+<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
+</svg>
diff --git a/LayoutTests/fast/css-generated-content/svg-use-crash.html b/LayoutTests/fast/css-generated-content/svg-use-crash.html
new file mode 100644 (file)
index 0000000..fbcdb9c
--- /dev/null
@@ -0,0 +1,13 @@
+This test passes if it doesn't crash.
+<svg width="100" height="100">
+<style>
+circle::before { content: "foo" }
+circle::after { content: "bar" }
+</style>
+<defs>
+<g id="c">
+<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
+</g>
+</defs>
+<use xlink:href="#c"/>
+</svg>
index 6477bb0..0cca670 100644 (file)
@@ -1,3 +1,63 @@
+2017-10-18  Antti Koivisto  <antti@apple.com>
+
+        Resolve ::before and ::after pseudo elements during style resolution
+        https://bugs.webkit.org/show_bug.cgi?id=178339
+
+        Reviewed by Ryosuke Niwa.
+
+        They are currently resolved during render tree building which creates problems with display:contents and animations.
+
+        * dom/PseudoElement.cpp:
+        (WebCore::PseudoElement::PseudoElement):
+
+            Call InspectorInstrumentation from constructor.
+
+        * style/RenderTreeUpdater.cpp:
+        (WebCore::RenderTreeUpdater::Parent::Parent):
+        (WebCore::RenderTreeUpdater::updateRenderTree):
+        (WebCore::RenderTreeUpdater::pushParent):
+
+            Push the full update to the parent stack.
+
+        (WebCore::RenderTreeUpdater::popParent):
+        (WebCore::RenderTreeUpdater::updateBeforeDescendants):
+        (WebCore::RenderTreeUpdater::updateAfterDescendants):
+        (WebCore::RenderTreeUpdater::invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded):
+        * style/RenderTreeUpdater.h:
+        * style/RenderTreeUpdaterGeneratedContent.cpp:
+        (WebCore::RenderTreeUpdater::GeneratedContent::updatePseudoElement):
+
+            No need to resolve pseudo style, we have it already.
+
+        (WebCore::RenderTreeUpdater::GeneratedContent::needsPseudoElement):
+        (WebCore::RenderTreeUpdater::GeneratedContent::updateBeforePseudoElement): Deleted.
+        (WebCore::RenderTreeUpdater::GeneratedContent::updateAfterPseudoElement): Deleted.
+        * style/RenderTreeUpdaterGeneratedContent.h:
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::resolveElement):
+        (WebCore::Style::TreeResolver::resolvePseudoStyle):
+
+            Resolve pseudos.
+
+        (WebCore::Style::TreeResolver::createAnimatedElementUpdate):
+
+            Make a private member function.
+
+        (WebCore::Style::TreeResolver::resolveComposedTree):
+        * style/StyleTreeResolver.h:
+        * style/StyleUpdate.cpp:
+        (WebCore::Style::Update::elementUpdates const):
+        (WebCore::Style::Update::elementUpdates):
+
+            Bundle the style update for an element and the associated before/after pseudos.
+
+        (WebCore::Style::Update::elementStyle const):
+        (WebCore::Style::Update::elementStyle):
+        (WebCore::Style::Update::addElement):
+        (WebCore::Style::Update::elementUpdate const): Deleted.
+        (WebCore::Style::Update::elementUpdate): Deleted.
+        * style/StyleUpdate.h:
+
 2017-10-18  Ms2ger  <Ms2ger@igalia.com>
 
         Update the signatures of texSubImage3D.
index 111f60b..2a59b71 100644 (file)
@@ -28,6 +28,7 @@
 #include "config.h"
 #include "PseudoElement.h"
 
+#include "CSSAnimationController.h"
 #include "ContentData.h"
 #include "InspectorInstrumentation.h"
 #include "RenderElement.h"
@@ -71,10 +72,22 @@ PseudoElement::~PseudoElement()
     ASSERT(!m_hostElement);
 }
 
+Ref<PseudoElement> PseudoElement::create(Element& host, PseudoId pseudoId)
+{
+    auto pseudoElement = adoptRef(*new PseudoElement(host, pseudoId));
+
+    InspectorInstrumentation::pseudoElementCreated(host.document().page(), pseudoElement.get());
+
+    return pseudoElement;
+}
+
 void PseudoElement::clearHostElement()
 {
     InspectorInstrumentation::pseudoElementDestroyed(document().page(), *this);
 
+    if (auto* frame = document().frame())
+        frame->animation().cancelAnimations(*this);
+
     m_hostElement = nullptr;
 }
 
index 47a9474..280950c 100644 (file)
@@ -33,10 +33,7 @@ namespace WebCore {
 
 class PseudoElement final : public Element {
 public:
-    static Ref<PseudoElement> create(Element& host, PseudoId pseudoId)
-    {
-        return adoptRef(*new PseudoElement(host, pseudoId));
-    }
+    static Ref<PseudoElement> create(Element& host, PseudoId);
     virtual ~PseudoElement();
 
     Element* hostElement() const { return m_hostElement; }
index 6157b1e..cd7916e 100644 (file)
@@ -75,9 +75,9 @@ RenderTreeUpdater::Parent::Parent(ContainerNode& root)
 {
 }
 
-RenderTreeUpdater::Parent::Parent(Element& element, Style::Change styleChange)
+RenderTreeUpdater::Parent::Parent(Element& element, const Style::ElementUpdates* updates)
     : element(&element)
-    , styleChange(styleChange)
+    , updates(updates)
     , renderTreePosition(element.renderer() ? std::make_optional(RenderTreePosition(*element.renderer())) : std::nullopt)
 {
 }
@@ -176,7 +176,7 @@ void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
         if (is<Text>(node)) {
             auto& text = downcast<Text>(node);
             auto* textUpdate = m_styleUpdate->textUpdate(text);
-            if (parent().styleChange == Style::Detach || textUpdate || m_invalidatedWhitespaceOnlyTextSiblings.contains(&text))
+            if ((parent().updates && parent().updates->update.change == Style::Detach) || textUpdate || m_invalidatedWhitespaceOnlyTextSiblings.contains(&text))
                 updateTextRenderer(text, textUpdate);
 
             it.traverseNextSkippingChildren();
@@ -185,17 +185,17 @@ void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
 
         auto& element = downcast<Element>(node);
 
-        auto* elementUpdate = m_styleUpdate->elementUpdate(element);
+        auto* elementUpdates = m_styleUpdate->elementUpdates(element);
 
         // We hop through display: contents elements in findRenderingRoot, so
         // there may be other updates down the tree.
-        if (!elementUpdate && !element.hasDisplayContents()) {
+        if (!elementUpdates && !element.hasDisplayContents()) {
             it.traverseNextSkippingChildren();
             continue;
         }
 
-        if (elementUpdate)
-            updateElementRenderer(element, *elementUpdate);
+        if (elementUpdates)
+            updateElementRenderer(element, elementUpdates->update);
 
         bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent()));
         if (!mayHaveRenderedDescendants) {
@@ -203,7 +203,7 @@ void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
             continue;
         }
 
-        pushParent(element, elementUpdate ? elementUpdate->change : Style::NoChange);
+        pushParent(element, elementUpdates);
 
         it.traverseNext();
     }
@@ -223,18 +223,18 @@ RenderTreePosition& RenderTreeUpdater::renderTreePosition()
     return *m_parentStack.last().renderTreePosition;
 }
 
-void RenderTreeUpdater::pushParent(Element& element, Style::Change changeType)
+void RenderTreeUpdater::pushParent(Element& element, const Style::ElementUpdates* updates)
 {
-    m_parentStack.append(Parent(element, changeType));
+    m_parentStack.append(Parent(element, updates));
 
-    updateBeforeDescendants(element);
+    updateBeforeDescendants(element, updates);
 }
 
 void RenderTreeUpdater::popParent()
 {
     auto& parent = m_parentStack.last();
     if (parent.element)
-        updateAfterDescendants(*parent.element, parent.styleChange);
+        updateAfterDescendants(*parent.element, parent.updates);
 
     m_parentStack.removeLast();
 }
@@ -247,14 +247,16 @@ void RenderTreeUpdater::popParentsToDepth(unsigned depth)
         popParent();
 }
 
-void RenderTreeUpdater::updateBeforeDescendants(Element& element)
+void RenderTreeUpdater::updateBeforeDescendants(Element& element, const Style::ElementUpdates* updates)
 {
-    generatedContent().updateBeforePseudoElement(element);
+    if (updates)
+        generatedContent().updatePseudoElement(element, updates->beforePseudoElementUpdate, BEFORE);
 }
 
-void RenderTreeUpdater::updateAfterDescendants(Element& element, Style::Change styleChange)
+void RenderTreeUpdater::updateAfterDescendants(Element& element, const Style::ElementUpdates* updates)
 {
-    generatedContent().updateAfterPseudoElement(element);
+    if (updates)
+        generatedContent().updatePseudoElement(element, updates->afterPseudoElementUpdate, AFTER);
 
     auto* renderer = element.renderer();
     if (!renderer)
@@ -268,7 +270,7 @@ void RenderTreeUpdater::updateAfterDescendants(Element& element, Style::Change s
     if (is<RenderBlockFlow>(*renderer))
         MultiColumn::update(downcast<RenderBlockFlow>(*renderer));
 
-    if (element.hasCustomStyleResolveCallbacks() && styleChange == Style::Detach)
+    if (element.hasCustomStyleResolveCallbacks() && updates && updates->update.change == Style::Detach)
         element.didAttachRenderers();
 }
 
@@ -493,7 +495,7 @@ void RenderTreeUpdater::invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(
     // the current node gaining or losing the renderer. This can only affect white space text nodes.
     for (Node* sibling = current.nextSibling(); sibling; sibling = sibling->nextSibling()) {
         if (is<Element>(*sibling)) {
-            if (m_styleUpdate->elementUpdate(downcast<Element>(*sibling)))
+            if (m_styleUpdate->elementUpdates(downcast<Element>(*sibling)))
                 return;
             // Text renderers beyond rendered elements can't be affected.
             if (sibling->renderer())
index b3fb7b8..feef054 100644 (file)
@@ -62,23 +62,23 @@ private:
     void updateElementRenderer(Element&, const Style::ElementUpdate&);
     void createRenderer(Element&, RenderStyle&&);
     void invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node&);
-    void updateBeforeDescendants(Element&);
-    void updateAfterDescendants(Element&, Style::Change);
+    void updateBeforeDescendants(Element&, const Style::ElementUpdates*);
+    void updateAfterDescendants(Element&, const Style::ElementUpdates*);
 
     struct Parent {
         Element* element { nullptr };
-        Style::Change styleChange { Style::NoChange };
+        const Style::ElementUpdates* updates { nullptr };
         std::optional<RenderTreePosition> renderTreePosition;
 
         Parent(ContainerNode& root);
-        Parent(Element&, Style::Change);
+        Parent(Element&, const Style::ElementUpdates*);
     };
     Parent& parent() { return m_parentStack.last(); }
     RenderTreePosition& renderTreePosition();
 
     GeneratedContent& generatedContent() { return *m_generatedContent; }
 
-    void pushParent(Element&, Style::Change);
+    void pushParent(Element&, const Style::ElementUpdates*);
     void popParent();
     void popParentsToDepth(unsigned depth);
 
index 0f5840a..86d8818 100644 (file)
@@ -44,16 +44,6 @@ RenderTreeUpdater::GeneratedContent::GeneratedContent(RenderTreeUpdater& updater
 {
 }
 
-void RenderTreeUpdater::GeneratedContent::updateBeforePseudoElement(Element& element)
-{
-    updatePseudoElement(element, BEFORE);
-}
-
-void RenderTreeUpdater::GeneratedContent::updateAfterPseudoElement(Element& element)
-{
-    updatePseudoElement(element, AFTER);
-}
-
 void RenderTreeUpdater::GeneratedContent::updateRemainingQuotes()
 {
     if (!m_updater.renderView().hasQuotesNeedingUpdate())
@@ -102,14 +92,14 @@ static void updateStyleForContentRenderers(RenderElement& renderer)
     }
 }
 
-void RenderTreeUpdater::GeneratedContent::updatePseudoElement(Element& current, PseudoId pseudoId)
+void RenderTreeUpdater::GeneratedContent::updatePseudoElement(Element& current, const std::optional<Style::ElementUpdate>& update, PseudoId pseudoId)
 {
     PseudoElement* pseudoElement = pseudoId == BEFORE ? current.beforePseudoElement() : current.afterPseudoElement();
 
     if (auto* renderer = pseudoElement ? pseudoElement->renderer() : nullptr)
         m_updater.renderTreePosition().invalidateNextSibling(*renderer);
 
-    if (!needsPseudoElement(current, pseudoId)) {
+    if (!needsPseudoElement(current, update)) {
         if (pseudoElement) {
             if (pseudoId == BEFORE)
                 current.clearBeforePseudoElement();
@@ -125,28 +115,23 @@ void RenderTreeUpdater::GeneratedContent::updatePseudoElement(Element& current,
         pseudoElement = newPseudoElement.get();
     }
 
-    auto newStyle = RenderStyle::clonePtr(*current.renderer()->getCachedPseudoStyle(pseudoId, &current.renderer()->style()));
-
-    auto elementUpdate = Style::TreeResolver::createAnimatedElementUpdate(WTFMove(newStyle), *pseudoElement, Style::NoChange);
-
-    if (elementUpdate.change == Style::NoChange)
+    if (update->change == Style::NoChange)
         return;
 
     if (newPseudoElement) {
-        InspectorInstrumentation::pseudoElementCreated(m_updater.m_document.page(), *newPseudoElement);
         if (pseudoId == BEFORE)
             current.setBeforePseudoElement(newPseudoElement.releaseNonNull());
         else
             current.setAfterPseudoElement(newPseudoElement.releaseNonNull());
     }
 
-    m_updater.updateElementRenderer(*pseudoElement, elementUpdate);
+    m_updater.updateElementRenderer(*pseudoElement, *update);
 
     auto* pseudoRenderer = pseudoElement->renderer();
     if (!pseudoRenderer)
         return;
 
-    if (elementUpdate.change == Style::Detach)
+    if (update->change == Style::Detach)
         createContentRenderers(*pseudoRenderer);
     else
         updateStyleForContentRenderers(*pseudoRenderer);
@@ -159,13 +144,14 @@ void RenderTreeUpdater::GeneratedContent::updatePseudoElement(Element& current,
         ListItem::updateMarker(downcast<RenderListItem>(*pseudoRenderer));
 }
 
-bool RenderTreeUpdater::GeneratedContent::needsPseudoElement(Element& current, PseudoId pseudoId)
+bool RenderTreeUpdater::GeneratedContent::needsPseudoElement(Element& current, const std::optional<Style::ElementUpdate>& update)
 {
-    if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren())
+    ASSERT(!current.isPseudoElement());
+    if (!update)
         return false;
-    if (current.isPseudoElement())
+    if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren())
         return false;
-    if (!pseudoElementRendererIsNeeded(current.renderer()->getCachedPseudoStyle(pseudoId)))
+    if (!pseudoElementRendererIsNeeded(update->style.get()))
         return false;
     return true;
 }
index 9a376b1..aefa183 100644 (file)
@@ -37,15 +37,13 @@ class RenderTreeUpdater::GeneratedContent {
 public:
     GeneratedContent(RenderTreeUpdater&);
 
-    void updateBeforePseudoElement(Element&);
-    void updateAfterPseudoElement(Element&);
+    void updatePseudoElement(Element&, const std::optional<Style::ElementUpdate>&, PseudoId);
     void updateRemainingQuotes();
 
 private:
-    void updatePseudoElement(Element&, PseudoId);
     void updateQuotesUpTo(RenderQuote*);
     
-    static bool needsPseudoElement(Element&, PseudoId);
+    static bool needsPseudoElement(Element&, const std::optional<Style::ElementUpdate>&);
 
     RenderTreeUpdater& m_updater;
     RenderQuote* m_previousUpdatedQuote { nullptr };
index a6042b5..94ea2df 100644 (file)
@@ -174,7 +174,7 @@ static const RenderStyle* renderOrDisplayContentsStyle(const Element& element)
     return nullptr;
 }
 
-ElementUpdate TreeResolver::resolveElement(Element& element)
+ElementUpdates TreeResolver::resolveElement(Element& element)
 {
     if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {
         m_document.setHasNodesWithMissingStyle();
@@ -218,7 +218,32 @@ ElementUpdate TreeResolver::resolveElement(Element& element)
             update.change = Detach;
     }
 
-    return update;
+    auto beforeUpdate = resolvePseudoStyle(element, update, BEFORE);
+    auto afterUpdate = resolvePseudoStyle(element, update, AFTER);
+
+    return { WTFMove(update), WTFMove(beforeUpdate), WTFMove(afterUpdate) };
+}
+
+ElementUpdate TreeResolver::resolvePseudoStyle(Element& element, const ElementUpdate& elementUpdate, PseudoId pseudoId)
+{
+    if (!elementUpdate.style->hasPseudoStyle(pseudoId))
+        return { };
+
+    auto pseudoStyle = scope().styleResolver.pseudoStyleForElement(element, { pseudoId }, *elementUpdate.style);
+    if (!pseudoStyle)
+        return { };
+
+    PseudoElement* pseudoElement = pseudoId == BEFORE ? element.beforePseudoElement() : element.afterPseudoElement();
+    if (!pseudoElement) {
+        auto newPseudoElement = PseudoElement::create(element, pseudoId);
+        pseudoElement = newPseudoElement.ptr();
+        if (pseudoId == BEFORE)
+            element.setBeforePseudoElement(WTFMove(newPseudoElement));
+        else
+            element.setAfterPseudoElement(WTFMove(newPseudoElement));
+    }
+
+    return createAnimatedElementUpdate(WTFMove(pseudoStyle), *pseudoElement, elementUpdate.change);
 }
 
 const RenderStyle* TreeResolver::parentBoxStyle() const
@@ -237,7 +262,7 @@ const RenderStyle* TreeResolver::parentBoxStyle() const
 
 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange)
 {
-    auto& animationController = element.document().frame()->animation();
+    auto& animationController = m_document.frame()->animation();
 
     auto* oldStyle = renderOrDisplayContentsStyle(element);
     auto animationUpdate = animationController.updateAnimations(element, *newStyle, oldStyle);
@@ -425,19 +450,19 @@ void TreeResolver::resolveComposedTree()
             if (element.hasCustomStyleResolveCallbacks())
                 element.willRecalcStyle(parent.change);
 
-            auto elementUpdate = resolveElement(element);
+            auto elementUpdates = resolveElement(element);
 
             if (element.hasCustomStyleResolveCallbacks())
-                element.didRecalcStyle(elementUpdate.change);
+                element.didRecalcStyle(elementUpdates.update.change);
 
-            style = elementUpdate.style.get();
-            change = elementUpdate.change;
+            style = elementUpdates.update.style.get();
+            change = elementUpdates.update.change;
 
             if (affectedByPreviousSibling && change != Detach)
                 change = Force;
 
-            if (elementUpdate.style)
-                m_update->addElement(element, parent.element, WTFMove(elementUpdate));
+            if (elementUpdates.update.style)
+                m_update->addElement(element, parent.element, WTFMove(elementUpdates));
 
             clearNeedsStyleResolution(element);
         }
index 2b873ce..eb0d18e 100644 (file)
@@ -51,13 +51,13 @@ public:
 
     std::unique_ptr<Update> resolve();
 
-    static ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, Element&, Change parentChange);
-
 private:
     std::unique_ptr<RenderStyle> styleForElement(Element&, const RenderStyle& inheritedStyle);
 
     void resolveComposedTree();
-    ElementUpdate resolveElement(Element&);
+    ElementUpdates resolveElement(Element&);
+    ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, Element&, Change parentChange);
+    ElementUpdate resolvePseudoStyle(Element&, const ElementUpdate&, PseudoId);
 
     struct Scope : RefCounted<Scope> {
         StyleResolver& styleResolver;
index bbb3c0f..08592a2 100644 (file)
@@ -41,7 +41,7 @@ Update::Update(Document& document)
 {
 }
 
-const ElementUpdate* Update::elementUpdate(const Element& element) const
+const ElementUpdates* Update::elementUpdates(const Element& element) const
 {
     auto it = m_elements.find(&element);
     if (it == m_elements.end())
@@ -49,7 +49,7 @@ const ElementUpdate* Update::elementUpdate(const Element& element) const
     return &it->value;
 }
 
-ElementUpdate* Update::elementUpdate(const Element& element)
+ElementUpdates* Update::elementUpdates(const Element& element)
 {
     auto it = m_elements.find(&element);
     if (it == m_elements.end())
@@ -67,8 +67,8 @@ const TextUpdate* Update::textUpdate(const Text& text) const
 
 const RenderStyle* Update::elementStyle(const Element& element) const
 {
-    if (auto* update = elementUpdate(element))
-        return update->style.get();
+    if (auto* updates = elementUpdates(element))
+        return updates->update.style.get();
     auto* renderer = element.renderer();
     if (!renderer)
         return nullptr;
@@ -77,21 +77,21 @@ const RenderStyle* Update::elementStyle(const Element& element) const
 
 RenderStyle* Update::elementStyle(const Element& element)
 {
-    if (auto* update = elementUpdate(element))
-        return update->style.get();
+    if (auto* updates = elementUpdates(element))
+        return updates->update.style.get();
     auto* renderer = element.renderer();
     if (!renderer)
         return nullptr;
     return &renderer->mutableStyle();
 }
 
-void Update::addElement(Element& element, Element* parent, ElementUpdate&& elementUpdate)
+void Update::addElement(Element& element, Element* parent, ElementUpdates&& elementUpdates)
 {
     ASSERT(!m_elements.contains(&element));
     ASSERT(composedTreeAncestors(element).first() == parent);
 
     addPossibleRoot(parent);
-    m_elements.add(&element, WTFMove(elementUpdate));
+    m_elements.add(&element, WTFMove(elementUpdates));
 }
 
 void Update::addText(Text& text, Element* parent, TextUpdate&& textUpdate)
index f6d83e0..aa96511 100644 (file)
@@ -57,6 +57,21 @@ struct ElementUpdate {
     bool recompositeLayer { false };
 };
 
+struct ElementUpdates {
+#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
+    ElementUpdates() = default;
+    ElementUpdates(ElementUpdate update, std::optional<ElementUpdate> beforePseudoElementUpdate, std::optional<ElementUpdate> afterPseudoElementUpdate)
+        : update { WTFMove(update) }
+        , beforePseudoElementUpdate { WTFMove(beforePseudoElementUpdate) }
+        , afterPseudoElementUpdate { WTFMove(afterPseudoElementUpdate) }
+    {
+    }
+#endif
+    ElementUpdate update;
+    std::optional<ElementUpdate> beforePseudoElementUpdate;
+    std::optional<ElementUpdate> afterPseudoElementUpdate;
+};
+
 struct TextUpdate {
 #if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
     TextUpdate() = default;
@@ -80,8 +95,8 @@ public:
 
     const ListHashSet<ContainerNode*>& roots() const { return m_roots; }
 
-    const ElementUpdate* elementUpdate(const Element&) const;
-    ElementUpdate* elementUpdate(const Element&);
+    const ElementUpdates* elementUpdates(const Element&) const;
+    ElementUpdates* elementUpdates(const Element&);
 
     const TextUpdate* textUpdate(const Text&) const;
 
@@ -92,7 +107,7 @@ public:
 
     unsigned size() const { return m_elements.size() + m_texts.size(); }
 
-    void addElement(Element&, Element* parent, ElementUpdate&&);
+    void addElement(Element&, Element* parent, ElementUpdates&&);
     void addText(Text&, Element* parent, TextUpdate&&);
     void addText(Text&, TextUpdate&&);
 
@@ -101,7 +116,7 @@ private:
 
     Document& m_document;
     ListHashSet<ContainerNode*> m_roots;
-    HashMap<const Element*, ElementUpdate> m_elements;
+    HashMap<const Element*, ElementUpdates> m_elements;
     HashMap<const Text*, TextUpdate> m_texts;
 };