keyframes do not work when defined inside a style in a shadowRoot
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 Dec 2016 22:42:28 +0000 (22:42 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 5 Dec 2016 22:42:28 +0000 (22:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164608
<rdar://problem/29210251>

Reviewed by Darin Adler.

Source/WebCore:

With :host and ::slotted rules a keyframe animation affecting an element can be
defined in a style scope different from the element's own scope. Style resolver
loses the scope information when building the RenderStyle so there is no way
to find out the correct scope.

Fix by passing style scope through to style builder and including a scope association
with the animation name. Find the correct scope when resolving keyframes.

Test: fast/shadow-dom/shadow-host-animation.html

* css/CSSToStyleMap.cpp:
(WebCore::CSSToStyleMap::mapAnimationName):

    Include scope with the name.

* css/ElementRuleCollector.cpp:
(WebCore::MatchRequest::MatchRequest):
(WebCore::ElementRuleCollector::addMatchedRule):
(WebCore::ElementRuleCollector::sortAndTransferMatchedRules):
(WebCore::ElementRuleCollector::matchAuthorRules):
(WebCore::ElementRuleCollector::matchAuthorShadowPseudoElementRules):
(WebCore::ElementRuleCollector::matchHostPseudoClassRules):
(WebCore::ElementRuleCollector::matchSlottedPseudoElementRules):
(WebCore::ElementRuleCollector::collectMatchingRulesForList):

    Replace treeContextOrdinal int with Style::ScopeOrdinal enum carrying the same information.
    Simplify the code removing unnecessary use of MatchRequest struct.

(WebCore::compareRules):
* css/ElementRuleCollector.h:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::MatchResult::addMatchedProperties):
(WebCore::StyleResolver::CascadedProperties::setPropertyInternal):
(WebCore::StyleResolver::CascadedProperties::set):
(WebCore::StyleResolver::CascadedProperties::setDeferred):

    Pass styleScopeOrdinal through the cascade mechanism

(WebCore::cascadeLevelForIndex):
(WebCore::StyleResolver::CascadedProperties::addMatch):
(WebCore::StyleResolver::CascadedProperties::addImportantMatches):
(WebCore::StyleResolver::CascadedProperties::Property::apply):

    Set styleScopeOrdinal in State when applying style.

(WebCore::StyleResolver::CascadedProperties::addStyleProperties): Deleted.

    Move the code to the only caller.

* css/StyleResolver.h:
(WebCore::StyleResolver::State::styleScopeOrdinal):
(WebCore::StyleResolver::State::setStyleScopeOrdinal):
* page/animation/CompositeAnimation.cpp:
(WebCore::KeyframeAnimation::KeyframeAnimation):
(WebCore::KeyframeAnimation::resolveKeyframeStyles):

    Find the correct scope for resolving keyframes based on the scope ordinal.

* platform/animation/Animation.cpp:
* platform/animation/Animation.h:

    Add m_nameStyleScopeOrdinal that tells the scope where the name is defined.

* style/StyleScope.cpp:
(WebCore::Style::Scope::forOrdinal):

    Find the scope for ordinal.

* style/StyleScope.h:

    Define ScopeOrdinal types.

(WebCore::Style::operator++):

LayoutTests:

* fast/shadow-dom/shadow-host-animation-expected.html: Added.
* fast/shadow-dom/shadow-host-animation.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/shadow-dom/shadow-host-animation-expected.html [new file with mode: 0644]
LayoutTests/fast/shadow-dom/shadow-host-animation.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSToStyleMap.cpp
Source/WebCore/css/ElementRuleCollector.cpp
Source/WebCore/css/ElementRuleCollector.h
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h
Source/WebCore/page/animation/KeyframeAnimation.cpp
Source/WebCore/platform/animation/Animation.cpp
Source/WebCore/platform/animation/Animation.h
Source/WebCore/style/StyleScope.cpp
Source/WebCore/style/StyleScope.h

index cb1fa65..ff8a5f9 100644 (file)
@@ -1,3 +1,14 @@
+2016-12-05  Antti Koivisto  <antti@apple.com>
+
+        keyframes do not work when defined inside a style in a shadowRoot
+        https://bugs.webkit.org/show_bug.cgi?id=164608
+        <rdar://problem/29210251>
+
+        Reviewed by Darin Adler.
+
+        * fast/shadow-dom/shadow-host-animation-expected.html: Added.
+        * fast/shadow-dom/shadow-host-animation.html: Added.
+
 2016-12-05  Ryan Haddad  <ryanhaddad@apple.com>
 
         Skip three media/modern-media-controls tests.
diff --git a/LayoutTests/fast/shadow-dom/shadow-host-animation-expected.html b/LayoutTests/fast/shadow-dom/shadow-host-animation-expected.html
new file mode 100644 (file)
index 0000000..e704d24
--- /dev/null
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <p>Test passes if you see a single 100px by 100px green box below.</p>
+    <div style="width: 100px; height: 100px; background: green;"></div>
+</body>
+</html>
diff --git a/LayoutTests/fast/shadow-dom/shadow-host-animation.html b/LayoutTests/fast/shadow-dom/shadow-host-animation.html
new file mode 100644 (file)
index 0000000..4ad12de
--- /dev/null
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@keyframes top-keyframes {
+    0% { background: red; }
+    100% { background: green; }
+}
+@keyframes shadow-keyframes {
+    0% { background: green; }
+    100% { background: red; }
+}
+@keyframes slotted-keyframes {
+    0% { background: green; }
+    100% { background: red; }
+}
+@keyframes deep-slotted-keyframes {
+    0% { background: green; }
+    100% { background: red; }
+}
+.test {
+    width: 100px;
+    height: 25px;
+    background: red;
+    color: green;
+}
+
+#host1.green {
+    animation-duration: 0.1s;
+    animation-iteration-count: 1;
+    animation-name: top-keyframes;
+    animation-fill-mode: forwards;
+}
+
+</style>
+</head>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host1" class="test"><div>text</div></div>
+<div id="host2" class="test"><div>text</div></div>
+<div id="host3" class="test"><div>text</div></div>
+<div id="host4" class="test"><div>text</div></div>
+<script>
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+let expectedEventCount = 0;
+let eventCount = 0;
+
+function animationEndEvent()
+{
+    ++eventCount;
+    if (eventCount == expectedEventCount) {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+}
+
+{
+    const host = document.getElementById('host1');
+    host.attachShadow({mode: 'closed'}).innerHTML = `
+        <slot></slot>
+    `;
+
+    getComputedStyle(host.firstChild).backgroundColor;
+    ++expectedEventCount;
+    document.addEventListener('animationend', animationEndEvent);
+    host.classList.toggle('green');
+}
+
+{
+    const host = document.getElementById('host2');
+    host.attachShadow({mode: 'closed'}).innerHTML = `
+    <style>
+    @keyframes shadow-keyframes {
+        0% { background: red; }
+        100% { background: green; }
+    }
+    :host(.green) {
+        animation-duration: 0.1s;
+        animation-iteration-count: 1;
+        animation-name: shadow-keyframes;
+        animation-fill-mode: forwards;
+    }
+    </style>
+    <slot></slot>
+    `;
+
+    getComputedStyle(host.firstChild).backgroundColor;
+    ++expectedEventCount;
+    document.addEventListener('animationend', animationEndEvent);
+    host.classList.toggle('green');
+}
+
+{
+    const host = document.getElementById('host3');
+    const shadow = host.attachShadow({mode: 'closed'});
+    shadow.innerHTML = `
+    <style>
+    @keyframes slotted-keyframes {
+        0% { background: red; }
+        100% { background: green; }
+    }
+    ::slotted(.green) {
+        animation-duration: 0.1s;
+        animation-iteration-count: 1;
+        animation-name: slotted-keyframes;
+        animation-fill-mode: forwards;
+        height: 100%;
+    }
+    div { height: 100%; }
+    </style>
+    <div><slot></slot></div>
+    `;
+
+    shadow.querySelector('div').attachShadow({mode: 'closed'}).innerHTML = `
+    <style>
+    @keyframes slotted-keyframes {
+        0% { background: green; }
+        100% { background: red; }
+    }
+    </style>
+    <slot></slot>
+    `;
+
+    getComputedStyle(host.firstChild).backgroundColor;
+    ++expectedEventCount;
+    document.addEventListener('animationend', animationEndEvent);
+    host.firstChild.classList.toggle('green');
+}
+
+{
+    const host = document.getElementById('host4');
+    const shadow = host.attachShadow({mode: 'closed'});
+    shadow.innerHTML = `
+    <style>
+    @keyframes deep-slotted-keyframes {
+        0% { background: green; }
+        100% { background: red; }
+    }
+    div { height: 100%; }
+    </style>
+    <div><slot></slot></div>
+    `;
+
+    shadow.querySelector('div').attachShadow({mode: 'closed'}).innerHTML = `
+    <style>
+    @keyframes deep-slotted-keyframes {
+        0% { background: red; }
+        100% { background: green; }
+    }
+    ::slotted(.green) {
+        animation-duration: 0.1s;
+        animation-iteration-count: 1;
+        animation-name: deep-slotted-keyframes;
+        animation-fill-mode: forwards;
+        height: 100%;
+    }
+    </style>
+    <slot></slot>
+    `;
+
+    getComputedStyle(host.firstChild).backgroundColor;
+    ++expectedEventCount;
+    document.addEventListener('animationend', animationEndEvent);
+    host.firstChild.classList.toggle('green');
+}
+
+</script>
+</body>
+</html>
index 13a19c0..86588a9 100644 (file)
@@ -1,3 +1,85 @@
+2016-12-05  Antti Koivisto  <antti@apple.com>
+
+        keyframes do not work when defined inside a style in a shadowRoot
+        https://bugs.webkit.org/show_bug.cgi?id=164608
+        <rdar://problem/29210251>
+
+        Reviewed by Darin Adler.
+
+        With :host and ::slotted rules a keyframe animation affecting an element can be
+        defined in a style scope different from the element's own scope. Style resolver
+        loses the scope information when building the RenderStyle so there is no way
+        to find out the correct scope.
+
+        Fix by passing style scope through to style builder and including a scope association
+        with the animation name. Find the correct scope when resolving keyframes.
+
+        Test: fast/shadow-dom/shadow-host-animation.html
+
+        * css/CSSToStyleMap.cpp:
+        (WebCore::CSSToStyleMap::mapAnimationName):
+
+            Include scope with the name.
+
+        * css/ElementRuleCollector.cpp:
+        (WebCore::MatchRequest::MatchRequest):
+        (WebCore::ElementRuleCollector::addMatchedRule):
+        (WebCore::ElementRuleCollector::sortAndTransferMatchedRules):
+        (WebCore::ElementRuleCollector::matchAuthorRules):
+        (WebCore::ElementRuleCollector::matchAuthorShadowPseudoElementRules):
+        (WebCore::ElementRuleCollector::matchHostPseudoClassRules):
+        (WebCore::ElementRuleCollector::matchSlottedPseudoElementRules):
+        (WebCore::ElementRuleCollector::collectMatchingRulesForList):
+
+            Replace treeContextOrdinal int with Style::ScopeOrdinal enum carrying the same information.
+            Simplify the code removing unnecessary use of MatchRequest struct.
+
+        (WebCore::compareRules):
+        * css/ElementRuleCollector.h:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::MatchResult::addMatchedProperties):
+        (WebCore::StyleResolver::CascadedProperties::setPropertyInternal):
+        (WebCore::StyleResolver::CascadedProperties::set):
+        (WebCore::StyleResolver::CascadedProperties::setDeferred):
+
+            Pass styleScopeOrdinal through the cascade mechanism
+
+        (WebCore::cascadeLevelForIndex):
+        (WebCore::StyleResolver::CascadedProperties::addMatch):
+        (WebCore::StyleResolver::CascadedProperties::addImportantMatches):
+        (WebCore::StyleResolver::CascadedProperties::Property::apply):
+
+            Set styleScopeOrdinal in State when applying style.
+
+        (WebCore::StyleResolver::CascadedProperties::addStyleProperties): Deleted.
+
+            Move the code to the only caller.
+
+        * css/StyleResolver.h:
+        (WebCore::StyleResolver::State::styleScopeOrdinal):
+        (WebCore::StyleResolver::State::setStyleScopeOrdinal):
+        * page/animation/CompositeAnimation.cpp:
+        (WebCore::KeyframeAnimation::KeyframeAnimation):
+        (WebCore::KeyframeAnimation::resolveKeyframeStyles):
+
+            Find the correct scope for resolving keyframes based on the scope ordinal.
+
+        * platform/animation/Animation.cpp:
+        * platform/animation/Animation.h:
+
+            Add m_nameStyleScopeOrdinal that tells the scope where the name is defined.
+
+        * style/StyleScope.cpp:
+        (WebCore::Style::Scope::forOrdinal):
+
+            Find the scope for ordinal.
+
+        * style/StyleScope.h:
+
+            Define ScopeOrdinal types.
+
+        (WebCore::Style::operator++):
+
 2016-12-05  Dave Hyatt  <hyatt@apple.com>
 
         [CSS Parser] Support glyph-orientation-horizontal and glyph-orientation-vertical
index 7c9fa98..bddc68f 100644 (file)
@@ -412,7 +412,7 @@ void CSSToStyleMap::mapAnimationName(Animation& layer, const CSSValue& value)
     if (primitiveValue.valueID() == CSSValueNone)
         layer.setIsNoneAnimation(true);
     else
-        layer.setName(primitiveValue.stringValue());
+        layer.setName(primitiveValue.stringValue(), m_resolver->state().styleScopeOrdinal());
 }
 
 void CSSToStyleMap::mapAnimationPlayState(Animation& layer, const CSSValue& value)
index cfb9d91..f0fc6ee 100644 (file)
@@ -68,15 +68,15 @@ static const StyleProperties& rightToLeftDeclaration()
 
 class MatchRequest {
 public:
-    MatchRequest(const RuleSet* ruleSet, bool includeEmptyRules = false, int treeContextOrdinal = 0)
+    MatchRequest(const RuleSet* ruleSet, bool includeEmptyRules = false, Style::ScopeOrdinal styleScopeOrdinal = Style::ScopeOrdinal::Element)
         : ruleSet(ruleSet)
         , includeEmptyRules(includeEmptyRules)
-        , treeContextOrdinal(treeContextOrdinal)
+        , styleScopeOrdinal(styleScopeOrdinal)
     {
     }
     const RuleSet* ruleSet;
     const bool includeEmptyRules;
-    int treeContextOrdinal;
+    Style::ScopeOrdinal styleScopeOrdinal;
 };
 
 ElementRuleCollector::ElementRuleCollector(const Element& element, const DocumentRuleSets& ruleSets, const SelectorFilter* selectorFilter)
@@ -108,14 +108,14 @@ const Vector<RefPtr<StyleRule>>& ElementRuleCollector::matchedRuleList() const
     return m_matchedRuleList;
 }
 
-inline void ElementRuleCollector::addMatchedRule(const RuleData& ruleData, unsigned specificity, int treeContextOrdinal, StyleResolver::RuleRange& ruleRange)
+inline void ElementRuleCollector::addMatchedRule(const RuleData& ruleData, unsigned specificity, Style::ScopeOrdinal styleScopeOrdinal, StyleResolver::RuleRange& ruleRange)
 {
     // Update our first/last rule indices in the matched rules array.
     ++ruleRange.lastRuleIndex;
     if (ruleRange.firstRuleIndex == -1)
         ruleRange.firstRuleIndex = ruleRange.lastRuleIndex;
 
-    m_matchedRules.append({ &ruleData, specificity, treeContextOrdinal });
+    m_matchedRules.append({ &ruleData, specificity, styleScopeOrdinal });
 }
 
 void ElementRuleCollector::clearMatchedRules()
@@ -193,7 +193,7 @@ void ElementRuleCollector::sortAndTransferMatchedRules()
     }
 
     for (const MatchedRule& matchedRule : m_matchedRules) {
-        m_result.addMatchedProperties(matchedRule.ruleData->rule()->properties(), matchedRule.ruleData->rule(), matchedRule.ruleData->linkMatchType(), matchedRule.ruleData->propertyWhitelistType(), matchedRule.treeContextOrdinal);
+        m_result.addMatchedProperties(matchedRule.ruleData->rule()->properties(), matchedRule.ruleData->rule(), matchedRule.ruleData->linkMatchType(), matchedRule.ruleData->propertyWhitelistType(), matchedRule.styleScopeOrdinal);
     }
 }
 
@@ -204,25 +204,26 @@ void ElementRuleCollector::matchAuthorRules(bool includeEmptyRules)
     m_result.ranges.lastAuthorRule = m_result.matchedProperties().size() - 1;
     StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange();
 
-    // Match global author rules.
-    MatchRequest matchRequest(&m_authorStyle, includeEmptyRules);
-    collectMatchingRules(matchRequest, ruleRange);
-    collectMatchingRulesForRegion(matchRequest, ruleRange);
+    {
+        MatchRequest matchRequest(&m_authorStyle, includeEmptyRules);
+        collectMatchingRules(matchRequest, ruleRange);
+        collectMatchingRulesForRegion(matchRequest, ruleRange);
+    }
 
     auto* parent = m_element.parentElement();
     if (parent && parent->shadowRoot())
-        matchSlottedPseudoElementRules(matchRequest, ruleRange);
+        matchSlottedPseudoElementRules(includeEmptyRules, ruleRange);
 
     if (m_element.shadowRoot() && m_pseudoStyleRequest.pseudoId == NOPSEUDO)
-        matchHostPseudoClassRules(matchRequest, ruleRange);
+        matchHostPseudoClassRules(includeEmptyRules, ruleRange);
 
     if (m_element.isInShadowTree())
-        matchAuthorShadowPseudoElementRules(matchRequest, ruleRange);
+        matchAuthorShadowPseudoElementRules(includeEmptyRules, ruleRange);
 
     sortAndTransferMatchedRules();
 }
 
-void ElementRuleCollector::matchAuthorShadowPseudoElementRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
+void ElementRuleCollector::matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
 {
     ASSERT(m_element.isInShadowTree());
     auto& shadowRoot = *m_element.containingShadowRoot();
@@ -230,16 +231,14 @@ void ElementRuleCollector::matchAuthorShadowPseudoElementRules(const MatchReques
         return;
     // Look up shadow pseudo elements also from the host scope author style as they are web-exposed.
     auto& hostAuthorRules = Style::Scope::forNode(*shadowRoot.host()).resolver().ruleSets().authorStyle();
-    MatchRequest hostAuthorRequest { &hostAuthorRules, matchRequest.includeEmptyRules, matchRequest.treeContextOrdinal - 1 };
+    MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost };
     collectMatchingShadowPseudoElementRules(hostAuthorRequest, ruleRange);
 }
 
-void ElementRuleCollector::matchHostPseudoClassRules(MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
+void ElementRuleCollector::matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
 {
     ASSERT(m_element.shadowRoot());
 
-    matchRequest.treeContextOrdinal++;
-
     auto& shadowAuthorStyle = m_element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle();
     auto& shadowHostRules = shadowAuthorStyle.hostPseudoClassRules();
     if (shadowHostRules.isEmpty())
@@ -249,40 +248,35 @@ void ElementRuleCollector::matchHostPseudoClassRules(MatchRequest& matchRequest,
     SelectorChecker selectorChecker(m_element.document());
 
     for (auto& ruleData : shadowHostRules) {
-        if (ruleData.rule()->properties().isEmpty() && !matchRequest.includeEmptyRules)
+        if (ruleData.rule()->properties().isEmpty() && !includeEmptyRules)
             continue;
         auto& selector = *ruleData.selector();
         unsigned specificity = 0;
         if (!selectorChecker.matchHostPseudoClass(selector, m_element, context, specificity))
             continue;
-        addMatchedRule(ruleData, specificity, matchRequest.treeContextOrdinal, ruleRange);
+        addMatchedRule(ruleData, specificity, Style::ScopeOrdinal::Shadow, ruleRange);
     }
 }
 
-void ElementRuleCollector::matchSlottedPseudoElementRules(MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
+void ElementRuleCollector::matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
 {
-    auto* maybeSlotted = &m_element;
-    for (auto* hostShadowRoot = m_element.parentNode()->shadowRoot(); hostShadowRoot; hostShadowRoot = maybeSlotted->parentNode()->shadowRoot()) {
-        auto* slot = hostShadowRoot->findAssignedSlot(*maybeSlotted);
-        if (!slot)
-            return;
-
-        matchRequest.treeContextOrdinal++;
+    auto* slot = m_element.assignedSlot();
+    auto styleScopeOrdinal = Style::ScopeOrdinal::FirstSlot;
 
-        // In nested case the slot may itself be assigned to a slot. Collect ::slotted rules from all the nested trees.
-        maybeSlotted = slot;
-        if (!hostShadowRoot->styleScope().resolver().ruleSets().isAuthorStyleDefined())
+    for (; slot; slot = slot->assignedSlot(), ++styleScopeOrdinal) {
+        auto& styleScope = Style::Scope::forNode(*slot);
+        if (!styleScope.resolver().ruleSets().isAuthorStyleDefined())
             continue;
         // Find out if there are any ::slotted rules in the shadow tree matching the current slot.
         // FIXME: This is really part of the slot style and could be cached when resolving it.
-        ElementRuleCollector collector(*slot, hostShadowRoot->styleScope().resolver().ruleSets().authorStyle(), nullptr);
-        auto slottedPseudoElementRules = collector.collectSlottedPseudoElementRulesForSlot(matchRequest.includeEmptyRules);
+        ElementRuleCollector collector(*slot, styleScope.resolver().ruleSets().authorStyle(), nullptr);
+        auto slottedPseudoElementRules = collector.collectSlottedPseudoElementRulesForSlot(includeEmptyRules);
         if (!slottedPseudoElementRules)
             continue;
         // Match in the current scope.
         SetForScope<bool> change(m_isMatchingSlottedPseudoElements, true);
 
-        MatchRequest scopeMatchRequest(nullptr, matchRequest.includeEmptyRules, matchRequest.treeContextOrdinal);
+        MatchRequest scopeMatchRequest(nullptr, includeEmptyRules, styleScopeOrdinal);
         collectMatchingRulesForList(slottedPseudoElementRules.get(), scopeMatchRequest, ruleRange);
 
         m_keepAliveSlottedPseudoElementRules.append(WTFMove(slottedPseudoElementRules));
@@ -507,15 +501,15 @@ void ElementRuleCollector::collectMatchingRulesForList(const RuleSet::RuleDataVe
 
         unsigned specificity;
         if (ruleMatches(ruleData, specificity))
-            addMatchedRule(ruleData, specificity, matchRequest.treeContextOrdinal, ruleRange);
+            addMatchedRule(ruleData, specificity, matchRequest.styleScopeOrdinal, ruleRange);
     }
 }
 
 static inline bool compareRules(MatchedRule r1, MatchedRule r2)
 {
-    // For normal properties the earlier tree wins. This may be reversed by !important which is handled when resolving cascade.
-    if (r1.treeContextOrdinal != r2.treeContextOrdinal)
-        return r1.treeContextOrdinal > r2.treeContextOrdinal;
+    // For normal properties the earlier scope wins. This may be reversed by !important which is handled when resolving cascade.
+    if (r1.styleScopeOrdinal != r2.styleScopeOrdinal)
+        return r1.styleScopeOrdinal > r2.styleScopeOrdinal;
 
     if (r1.specificity != r2.specificity)
         return r1.specificity < r2.specificity;
index d438be8..3c381b5 100644 (file)
@@ -40,7 +40,7 @@ class SelectorFilter;
 struct MatchedRule {
     const RuleData* ruleData;
     unsigned specificity;   
-    int treeContextOrdinal;
+    Style::ScopeOrdinal styleScopeOrdinal;
 };
 
 class ElementRuleCollector {
@@ -75,9 +75,9 @@ private:
     void addElementStyleProperties(const StyleProperties*, bool isCacheable = true);
 
     void matchUARules(RuleSet*);
-    void matchAuthorShadowPseudoElementRules(const MatchRequest&, StyleResolver::RuleRange&);
-    void matchHostPseudoClassRules(MatchRequest&, StyleResolver::RuleRange&);
-    void matchSlottedPseudoElementRules(MatchRequest&, StyleResolver::RuleRange&);
+    void matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange&);
+    void matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange&);
+    void matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange&);
 
     void collectMatchingShadowPseudoElementRules(const MatchRequest&, StyleResolver::RuleRange&);
     std::unique_ptr<RuleSet::RuleDataVector> collectSlottedPseudoElementRulesForSlot(bool includeEmptyRules);
@@ -90,7 +90,7 @@ private:
     void sortMatchedRules();
     void sortAndTransferMatchedRules();
 
-    void addMatchedRule(const RuleData&, unsigned specificity, int treeContextOrdinal, StyleResolver::RuleRange&);
+    void addMatchedRule(const RuleData&, unsigned specificity, Style::ScopeOrdinal, StyleResolver::RuleRange&);
 
     const Element& m_element;
     const RuleSet& m_authorStyle;
index 10f65c3..b6055cc 100644 (file)
@@ -187,18 +187,17 @@ inline void StyleResolver::State::clear()
     m_cssToLengthConversionData = CSSToLengthConversionData();
 }
 
-void StyleResolver::MatchResult::addMatchedProperties(const StyleProperties& properties, StyleRule* rule, unsigned linkMatchType, PropertyWhitelistType propertyWhitelistType, int treeContextOrdinal)
+void StyleResolver::MatchResult::addMatchedProperties(const StyleProperties& properties, StyleRule* rule, unsigned linkMatchType, PropertyWhitelistType propertyWhitelistType, Style::ScopeOrdinal styleScopeOrdinal)
 {
     m_matchedProperties.grow(m_matchedProperties.size() + 1);
     StyleResolver::MatchedProperties& newProperties = m_matchedProperties.last();
     newProperties.properties = const_cast<StyleProperties*>(&properties);
     newProperties.linkMatchType = linkMatchType;
     newProperties.whitelistType = propertyWhitelistType;
-    newProperties.treeContextOrdinal = treeContextOrdinal;
+    newProperties.styleScopeOrdinal = styleScopeOrdinal;
     matchedRules.append(rule);
 
-    // Ordinal is relative to the currently matched element
-    if (treeContextOrdinal)
+    if (styleScopeOrdinal != Style::ScopeOrdinal::Element)
         isCacheable = false;
 
     if (isCacheable) {
@@ -2060,11 +2059,12 @@ inline StyleResolver::CascadedProperties::Property StyleResolver::CascadedProper
     return m_customProperties.get(name);
 }
 
-void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel)
+void StyleResolver::CascadedProperties::setPropertyInternal(Property& property, CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
 {
     ASSERT(linkMatchType <= SelectorChecker::MatchAll);
     property.id = id;
     property.level = cascadeLevel;
+    property.styleScopeOrdinal = styleScopeOrdinal;
     if (linkMatchType == SelectorChecker::MatchAll) {
         property.cssValue[0] = &cssValue;
         property.cssValue[SelectorChecker::MatchLink] = &cssValue;
@@ -2073,7 +2073,7 @@ void StyleResolver::CascadedProperties::setPropertyInternal(Property& property,
         property.cssValue[linkMatchType] = &cssValue;
 }
 
-void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel)
+void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
 {
     if (CSSProperty::isDirectionAwareProperty(id))
         id = CSSProperty::resolveDirectionAwareProperty(id, m_direction, m_writingMode);
@@ -2090,11 +2090,11 @@ void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue
             Property property;
             property.id = id;
             memset(property.cssValue, 0, sizeof(property.cssValue));
-            setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel);
+            setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
             customProperties().set(customValue.name(), property);
         } else {
             Property property = customProperties().get(customValue.name());
-            setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel);
+            setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
             customProperties().set(customValue.name(), property);
         }
         return;
@@ -2103,24 +2103,39 @@ void StyleResolver::CascadedProperties::set(CSSPropertyID id, CSSValue& cssValue
     if (!m_propertyIsPresent[id])
         memset(property.cssValue, 0, sizeof(property.cssValue));
     m_propertyIsPresent.set(id);
-    setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel);
+    setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
 }
 
-void StyleResolver::CascadedProperties::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel)
+void StyleResolver::CascadedProperties::setDeferred(CSSPropertyID id, CSSValue& cssValue, unsigned linkMatchType, CascadeLevel cascadeLevel, Style::ScopeOrdinal styleScopeOrdinal)
 {
     ASSERT(!CSSProperty::isDirectionAwareProperty(id));
     ASSERT(shouldApplyPropertyInParseOrder(id));
 
     Property property;
     memset(property.cssValue, 0, sizeof(property.cssValue));
-    setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel);
+    setPropertyInternal(property, id, cssValue, linkMatchType, cascadeLevel, styleScopeOrdinal);
     m_deferredProperties.append(property);
 }
 
-void StyleResolver::CascadedProperties::addStyleProperties(const StyleProperties& properties, bool isImportant, bool inheritedOnly, PropertyWhitelistType propertyWhitelistType, unsigned linkMatchType, CascadeLevel cascadeLevel)
+static CascadeLevel cascadeLevelForIndex(const StyleResolver::MatchResult& matchResult, int index)
 {
-    for (unsigned i = 0, count = properties.propertyCount(); i < count; ++i) {
-        auto current = properties.propertyAt(i);
+    if (index >= matchResult.ranges.firstUARule && index <= matchResult.ranges.lastUARule)
+        return UserAgentLevel;
+    if (index >= matchResult.ranges.firstUserRule && index <= matchResult.ranges.lastUserRule)
+        return UserLevel;
+    return AuthorLevel;
+}
+
+void StyleResolver::CascadedProperties::addMatch(const MatchResult& matchResult, unsigned index, bool isImportant, bool inheritedOnly)
+{
+    auto& matchedProperties = matchResult.matchedProperties()[index];
+    auto& styleProperties = *matchedProperties.properties;
+
+    auto propertyWhitelistType = static_cast<PropertyWhitelistType>(matchedProperties.whitelistType);
+    auto cascadeLevel = cascadeLevelForIndex(matchResult, index);
+
+    for (unsigned i = 0, count = styleProperties.propertyCount(); i < count; ++i) {
+        auto current = styleProperties.propertyAt(i);
         if (isImportant != current.isImportant())
             continue;
         if (inheritedOnly && !current.isInherited()) {
@@ -2139,31 +2154,12 @@ void StyleResolver::CascadedProperties::addStyleProperties(const StyleProperties
 #endif
 
         if (shouldApplyPropertyInParseOrder(propertyID))
-            setDeferred(propertyID, *current.value(), linkMatchType, cascadeLevel);
+            setDeferred(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
         else
-            set(propertyID, *current.value(), linkMatchType, cascadeLevel);
+            set(propertyID, *current.value(), matchedProperties.linkMatchType, cascadeLevel, matchedProperties.styleScopeOrdinal);
     }
 }
 
-static CascadeLevel cascadeLevelForIndex(const StyleResolver::MatchResult& matchResult, int index)
-{
-    if (index >= matchResult.ranges.firstUARule && index <= matchResult.ranges.lastUARule)
-        return UserAgentLevel;
-    if (index >= matchResult.ranges.firstUserRule && index <= matchResult.ranges.lastUserRule)
-        return UserLevel;
-    return AuthorLevel;
-}
-
-void StyleResolver::CascadedProperties::addMatch(const MatchResult& matchResult, unsigned index, bool isImportant, bool inheritedOnly)
-{
-    const MatchedProperties& matchedProperties = matchResult.matchedProperties()[index];
-
-    auto propertyWhitelistType = static_cast<PropertyWhitelistType>(matchedProperties.whitelistType);
-    auto cascadeLevel = cascadeLevelForIndex(matchResult, index);
-
-    addStyleProperties(*matchedProperties.properties, isImportant, inheritedOnly, propertyWhitelistType, matchedProperties.linkMatchType, cascadeLevel);
-}
-
 void StyleResolver::CascadedProperties::addNormalMatches(const MatchResult& matchResult, int startIndex, int endIndex, bool inheritedOnly)
 {
     if (startIndex == -1)
@@ -2189,7 +2185,7 @@ void StyleResolver::CascadedProperties::addImportantMatches(const MatchResult& m
 
     struct IndexAndOrdinal {
         int index;
-        int ordinal;
+        Style::ScopeOrdinal ordinal;
     };
     Vector<IndexAndOrdinal> shadowTreeMatches;
 
@@ -2199,8 +2195,8 @@ void StyleResolver::CascadedProperties::addImportantMatches(const MatchResult& m
         if (!hasImportantProperties(*matchedProperties.properties))
             continue;
 
-        if (matchedProperties.treeContextOrdinal) {
-            shadowTreeMatches.append({ i, matchedProperties.treeContextOrdinal });
+        if (matchedProperties.styleScopeOrdinal != Style::ScopeOrdinal::Element) {
+            shadowTreeMatches.append({ i, matchedProperties.styleScopeOrdinal });
             continue;
         }
 
@@ -2230,6 +2226,7 @@ void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver,
 {
     State& state = resolver.state();
     state.setCascadeLevel(level);
+    state.setStyleScopeOrdinal(styleScopeOrdinal);
 
     if (cssValue[SelectorChecker::MatchDefault]) {
         state.setApplyPropertyToRegularStyle(true);
index e0d1362..bf1a59c 100644 (file)
@@ -32,6 +32,7 @@
 #include "RuleSet.h"
 #include "SelectorChecker.h"
 #include "StylePendingResources.h"
+#include "StyleScope.h"
 #include <bitset>
 #include <memory>
 #include <wtf/HashMap.h>
@@ -250,7 +251,7 @@ public:
         RefPtr<StyleProperties> properties;
         uint16_t linkMatchType;
         uint16_t whitelistType;
-        int treeContextOrdinal;
+        Style::ScopeOrdinal styleScopeOrdinal;
     };
 
     struct MatchResult {
@@ -261,7 +262,7 @@ public:
 
         const Vector<MatchedProperties, 64>& matchedProperties() const { return m_matchedProperties; }
 
-        void addMatchedProperties(const StyleProperties&, StyleRule* = nullptr, unsigned linkMatchType = SelectorChecker::MatchAll, PropertyWhitelistType = PropertyWhitelistNone, int treeContextOrdinal = 0);
+        void addMatchedProperties(const StyleProperties&, StyleRule* = nullptr, unsigned linkMatchType = SelectorChecker::MatchAll, PropertyWhitelistType = PropertyWhitelistNone, Style::ScopeOrdinal = Style::ScopeOrdinal::Element);
     private:
         Vector<MatchedProperties, 64> m_matchedProperties;
     };
@@ -276,6 +277,7 @@ public:
 
             CSSPropertyID id;
             CascadeLevel level;
+            Style::ScopeOrdinal styleScopeOrdinal;
             CSSValue* cssValue[3];
         };
 
@@ -285,9 +287,6 @@ public:
         void addNormalMatches(const MatchResult&, int startIndex, int endIndex, bool inheritedOnly = false);
         void addImportantMatches(const MatchResult&, int startIndex, int endIndex, bool inheritedOnly = false);
 
-        void set(CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel);
-        void setDeferred(CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel);
-
         void applyDeferredProperties(StyleResolver&, const MatchResult*);
 
         HashMap<AtomicString, Property>& customProperties() { return m_customProperties; }
@@ -296,8 +295,9 @@ public:
         
     private:
         void addMatch(const MatchResult&, unsigned index, bool isImportant, bool inheritedOnly);
-        void addStyleProperties(const StyleProperties&, bool isImportant, bool inheritedOnly, PropertyWhitelistType, unsigned linkMatchType, CascadeLevel);
-        static void setPropertyInternal(Property&, CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel);
+        void set(CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel, Style::ScopeOrdinal);
+        void setDeferred(CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel, Style::ScopeOrdinal);
+        static void setPropertyInternal(Property&, CSSPropertyID, CSSValue&, unsigned linkMatchType, CascadeLevel, Style::ScopeOrdinal);
 
         Property m_properties[numCSSProperties + 2];
         std::bitset<numCSSProperties + 2> m_propertyIsPresent;
@@ -404,7 +404,9 @@ public:
 
         CascadeLevel cascadeLevel() const { return m_cascadeLevel; }
         void setCascadeLevel(CascadeLevel level) { m_cascadeLevel = level; }
-        
+        Style::ScopeOrdinal styleScopeOrdinal() const { return m_styleScopeOrdinal; }
+        void setStyleScopeOrdinal(Style::ScopeOrdinal styleScopeOrdinal) { m_styleScopeOrdinal = styleScopeOrdinal; }
+
         CascadedProperties* authorRollback() const { return m_authorRollback.get(); }
         CascadedProperties* userRollback() const { return m_userRollback.get(); }
         
@@ -439,6 +441,7 @@ public:
         CSSToLengthConversionData m_cssToLengthConversionData;
         
         CascadeLevel m_cascadeLevel { UserAgentLevel };
+        Style::ScopeOrdinal m_styleScopeOrdinal { Style::ScopeOrdinal::Element };
         std::unique_ptr<CascadedProperties> m_authorRollback;
         std::unique_ptr<CascadedProperties> m_userRollback;
 
index d314c3b..471bacf 100644 (file)
@@ -39,6 +39,7 @@
 #include "RenderStyle.h"
 #include "StylePendingResources.h"
 #include "StyleResolver.h"
+#include "StyleScope.h"
 #include "WillChangeData.h"
 
 namespace WebCore {
@@ -363,7 +364,8 @@ void KeyframeAnimation::resolveKeyframeStyles()
         return;
     auto& element = *m_object->element();
 
-    element.styleResolver().keyframeStylesForAnimation(*m_object->element(), m_unanimatedStyle.get(), m_keyframes);
+    if (auto* styleScope = Style::Scope::forOrdinal(element, m_animation->nameStyleScopeOrdinal()))
+        styleScope->resolver().keyframeStylesForAnimation(*m_object->element(), m_unanimatedStyle.get(), m_keyframes);
 
     // Ensure resource loads for all the frames.
     for (auto& keyframe : m_keyframes.keyframes()) {
index 1206168..1affdd8 100644 (file)
@@ -59,6 +59,7 @@ Animation::Animation()
 Animation::Animation(const Animation& o)
     : RefCounted<Animation>()
     , m_name(o.m_name)
+    , m_nameStyleScopeOrdinal(o.m_nameStyleScopeOrdinal)
     , m_property(o.m_property)
     , m_mode(o.m_mode)
     , m_iterationCount(o.m_iterationCount)
@@ -90,6 +91,7 @@ Animation::Animation(const Animation& o)
 Animation& Animation::operator=(const Animation& o)
 {
     m_name = o.m_name;
+    m_nameStyleScopeOrdinal = o.m_nameStyleScopeOrdinal;
     m_property = o.m_property;
     m_mode = o.m_mode;
     m_iterationCount = o.m_iterationCount;
@@ -130,6 +132,7 @@ Animation::~Animation()
 bool Animation::animationsMatch(const Animation& other, bool matchPlayStates) const
 {
     bool result = m_name == other.m_name
+        && m_nameStyleScopeOrdinal == other.m_nameStyleScopeOrdinal
         && m_property == other.m_property
         && m_mode == other.m_mode
         && m_iterationCount == other.m_iterationCount
index 599a841..9a0df17 100644 (file)
@@ -32,6 +32,7 @@
 #endif
 #include "CSSPropertyNames.h"
 #include "RenderStyleConstants.h"
+#include "StyleScope.h"
 #include "TimingFunction.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
@@ -135,6 +136,7 @@ public:
     enum { IterationCountInfinite = -1 };
     double iterationCount() const { return m_iterationCount; }
     const String& name() const { return m_name; }
+    Style::ScopeOrdinal nameStyleScopeOrdinal() const { return m_nameStyleScopeOrdinal; }
     EAnimPlayState playState() const { return static_cast<EAnimPlayState>(m_playState); }
     CSSPropertyID property() const { return m_property; }
     PassRefPtr<TimingFunction> timingFunction() const { return m_timingFunction; }
@@ -148,7 +150,12 @@ public:
     void setDuration(double d) { ASSERT(d >= 0); m_duration = d; m_durationSet = true; }
     void setFillMode(unsigned f) { m_fillMode = f; m_fillModeSet = true; }
     void setIterationCount(double c) { m_iterationCount = c; m_iterationCountSet = true; }
-    void setName(const String& n) { m_name = n; m_nameSet = true; }
+    void setName(const String& name, Style::ScopeOrdinal scope = Style::ScopeOrdinal::Element)
+    {
+        m_name = name;
+        m_nameStyleScopeOrdinal = scope;
+        m_nameSet = true;
+    }
     void setPlayState(EAnimPlayState d) { m_playState = d; m_playStateSet = true; }
     void setProperty(CSSPropertyID t) { m_property = t; m_propertySet = true; }
     void setTimingFunction(PassRefPtr<TimingFunction> f) { m_timingFunction = f; m_timingFunctionSet = true; }
@@ -176,6 +183,7 @@ private:
     Animation(const Animation& o);
     
     String m_name;
+    Style::ScopeOrdinal m_nameStyleScopeOrdinal { Style::ScopeOrdinal::Element };
     CSSPropertyID m_property;
     AnimationMode m_mode;
     double m_iterationCount;
index 82270ad..8a8878e 100644 (file)
@@ -34,6 +34,7 @@
 #include "ExtensionStyleSheets.h"
 #include "HTMLIFrameElement.h"
 #include "HTMLLinkElement.h"
+#include "HTMLSlotElement.h"
 #include "HTMLStyleElement.h"
 #include "InspectorInstrumentation.h"
 #include "Page.h"
@@ -126,6 +127,35 @@ Scope& Scope::forNode(Node& node)
     return node.document().styleScope();
 }
 
+Scope* Scope::forOrdinal(Element& element, ScopeOrdinal ordinal)
+{
+    switch (ordinal) {
+    case ScopeOrdinal::Element:
+        return &forNode(element);
+    case ScopeOrdinal::ContainingHost: {
+        auto* containingShadowRoot = element.containingShadowRoot();
+        if (!containingShadowRoot)
+            return nullptr;
+        return &forNode(*containingShadowRoot->host());
+    }
+    case ScopeOrdinal::Shadow: {
+        auto* shadowRoot = element.shadowRoot();
+        if (!shadowRoot)
+            return nullptr;
+        return &shadowRoot->styleScope();
+    }
+    default: {
+        ASSERT(ordinal >= ScopeOrdinal::FirstSlot);
+        auto slotIndex = ScopeOrdinal::FirstSlot;
+        for (auto* slot = element.assignedSlot(); slot; slot = slot->assignedSlot(), ++slotIndex) {
+            if (slotIndex == ordinal)
+                return &forNode(*slot);
+        }
+        return nullptr;
+    }
+    }
+}
+
 void Scope::setPreferredStylesheetSetName(const String& name)
 {
     if (m_preferredStylesheetSetName == name)
index 0a6e70f..9409d4b 100644 (file)
@@ -40,6 +40,7 @@ namespace WebCore {
 
 class CSSStyleSheet;
 class Document;
+class Element;
 class Node;
 class StyleResolver;
 class StyleSheet;
@@ -50,6 +51,15 @@ class TreeScope;
 
 namespace Style {
 
+// This is used to identify style scopes that can affect an element.
+// Scopes are in tree-of-trees order. Styles from earlier scopes win over later ones (modulo !important).
+enum class ScopeOrdinal : int {
+    ContainingHost = -1, // Author-exposed UA pseudo classes from the host tree scope.
+    Element = 0, // Normal rules in the same tree where the element is.
+    FirstSlot = 1, // ::slotted rules in the parent's shadow tree. Values greater than FirstSlot indicate subsequent slots in the chain.
+    Shadow = std::numeric_limits<int>::max(), // :host rules in element's own shadow tree.
+};
+
 class Scope {
     WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -102,6 +112,7 @@ public:
     const Document& document() const { return m_document; }
 
     static Scope& forNode(Node&);
+    static Scope* forOrdinal(Element&, ScopeOrdinal);
 
 private:
     bool shouldUseSharedUserAgentShadowTreeStyleResolver() const;
@@ -164,5 +175,11 @@ inline void Scope::flushPendingUpdate()
         flushPendingSelfUpdate();
 }
 
+inline ScopeOrdinal& operator++(ScopeOrdinal& ordinal)
+{
+    ASSERT(ordinal < ScopeOrdinal::Shadow);
+    return ordinal = static_cast<ScopeOrdinal>(static_cast<int>(ordinal) + 1);
+}
+
 }
 }