Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Unified Diff: third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp

Issue 1803933002: Use correct cascading order for Shadow DOM v1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix for review comment #16 Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
index 6498da7f9319bfa955130682f2792c7b7864ef7a..3cee9f558ddf9765e536d184c327ffd0ce364493 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp
@@ -389,7 +389,7 @@ static inline ScopedStyleResolver* scopedResolverFor(const Element& element)
{
// Ideally, returning element->treeScope().scopedStyleResolver() should be
// enough, but ::cue and custom pseudo elements like ::-webkit-meter-bar pierce
- // through a shadow dom boundary, yet they are not part of m_treeBoundaryCrossingRules.
+ // through a shadow dom boundary, yet they are not part of m_treeBoundaryCrossingScopes.
// The assumption here is that these rules only pierce through one boundary and
// that the scope of these elements do not have a style resolver due to the fact
// that VTT scopes and UA shadow trees don't have <style> elements. This is
@@ -413,8 +413,129 @@ static inline ScopedStyleResolver* scopedResolverFor(const Element& element)
return treeScope->scopedStyleResolver();
}
+static void matchHostRules(const Element& element, ElementRuleCollector& collector)
+{
+ ElementShadow* shadow = element.shadow();
+ if (!shadow)
+ return;
+
+ for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) {
+ if (!shadowRoot->numberOfStyles())
+ continue;
+ if (ScopedStyleResolver* resolver = shadowRoot->scopedStyleResolver()) {
+ collector.clearMatchedRules();
+ resolver->collectMatchingShadowHostRules(collector);
+ collector.sortAndTransferMatchedRules();
+ collector.finishAddingAuthorRulesForTreeScope();
+ }
+ }
+}
+
+static void matchElementScopeRules(const Element& element, ScopedStyleResolver* elementScopeResolver, ElementRuleCollector& collector)
+{
+ if (elementScopeResolver) {
+ collector.clearMatchedRules();
+ elementScopeResolver->collectMatchingAuthorRules(collector);
+ elementScopeResolver->collectMatchingTreeBoundaryCrossingRules(collector);
+ collector.sortAndTransferMatchedRules();
+ }
+
+ if (element.isStyledElement() && element.inlineStyle() && !collector.isCollectingForPseudoElement()) {
+ // Inline style is immutable as long as there is no CSSOM wrapper.
+ bool isInlineStyleCacheable = !element.inlineStyle()->isMutable();
+ collector.addElementStyleProperties(element.inlineStyle(), isInlineStyleCacheable);
+ }
+
+ collector.finishAddingAuthorRulesForTreeScope();
+}
+
+static bool shouldCheckScope(const Element& element, const Node& scopingNode, bool isInnerTreeScope)
+{
+ if (isInnerTreeScope && element.treeScope() != scopingNode.treeScope()) {
+ // Check if |element| may be affected by a ::content rule in |scopingNode|'s style.
+ // If |element| is a descendant of a shadow host which is ancestral to |scopingNode|,
+ // the |element| should be included for rule collection.
+ // Skip otherwise.
+ const TreeScope* scope = &scopingNode.treeScope();
+ while (scope && scope->parentTreeScope() != &element.treeScope())
+ scope = scope->parentTreeScope();
+ Element* shadowHost = scope ? scope->rootNode().shadowHost() : nullptr;
+ return shadowHost && element.isDescendantOf(shadowHost);
+ }
+
+ // When |element| can be distributed to |scopingNode| via <shadow>, ::content rule can match,
+ // thus the case should be included.
+ if (!isInnerTreeScope && scopingNode.parentOrShadowHostNode() == element.treeScope().rootNode().parentOrShadowHostNode())
+ return true;
+
+ // Obviously cases when ancestor scope has /deep/ or ::shadow rule should be included.
+ // Skip otherwise.
+ return scopingNode.treeScope().scopedStyleResolver()->hasDeepOrShadowSelector();
+}
+
+void StyleResolver::matchScopedRules(const Element& element, ElementRuleCollector& collector)
+{
+ // Match rules from treeScopes in the reverse tree-of-trees order, since the
+ // cascading order for normal rules is such that when comparing rules from
+ // different shadow trees, the rule from the tree which comes first in the
+ // tree-of-trees order wins. From other treeScopes than the element's own
+ // scope, only tree-boundary-crossing rules may match.
+
+ ScopedStyleResolver* elementScopeResolver = scopedResolverFor(element);
+ bool matchElementScopeDone = !elementScopeResolver && !element.inlineStyle();
+
+ // TODO(kochi): This loops through m_treeBoundaryCrossingScopes because to handle
+ // Shadow DOM V0 documents as well. In pure V1 document only have to go through
+ // the chain of scopes for assigned slots. Add fast path for pure V1 document.
+ for (auto it = m_treeBoundaryCrossingScopes.rbegin(); it != m_treeBoundaryCrossingScopes.rend(); ++it) {
+ const TreeScope& scope = (*it)->treeScope();
+ ScopedStyleResolver* resolver = scope.scopedStyleResolver();
+ ASSERT(resolver);
+
+ bool isInnerTreeScope = element.treeScope().isInclusiveAncestorOf(scope);
+ if (!shouldCheckScope(element, **it, isInnerTreeScope))
+ continue;
+
+ if (!matchElementScopeDone && scope.isInclusiveAncestorOf(element.treeScope())) {
+
+ matchElementScopeDone = true;
+
+ // At this point, the iterator has either encountered the scope for the element
+ // itself (if that scope has boundary-crossing rules), or the iterator has moved
+ // to a scope which appears before the element's scope in the tree-of-trees order.
+ // Try to match all rules from the element's scope.
+
+ matchElementScopeRules(element, elementScopeResolver, collector);
+ if (resolver == elementScopeResolver) {
+ // Boundary-crossing rules already collected in matchElementScopeRules.
+ continue;
+ }
+ }
+
+ collector.clearMatchedRules();
+ resolver->collectMatchingTreeBoundaryCrossingRules(collector);
+ collector.sortAndTransferMatchedRules();
+ collector.finishAddingAuthorRulesForTreeScope();
+ }
+
+ if (!matchElementScopeDone)
+ matchElementScopeRules(element, elementScopeResolver, collector);
+}
+
void StyleResolver::matchAuthorRules(const Element& element, ElementRuleCollector& collector)
{
+ if (element.document().styleEngine().shadowCascadeOrder() != ShadowCascadeOrder::ShadowCascadeV1) {
+ matchAuthorRulesV0(element, collector);
+ return;
+ }
+
+ ASSERT(RuntimeEnabledFeatures::shadowDOMV1Enabled());
+ matchHostRules(element, collector);
+ matchScopedRules(element, collector);
+}
+
+void StyleResolver::matchAuthorRulesV0(const Element& element, ElementRuleCollector& collector)
+{
collector.clearMatchedRules();
CascadeOrder cascadeOrder = 0;
@@ -487,7 +608,8 @@ void StyleResolver::matchAllRules(StyleResolverState& state, ElementRuleCollecto
matchAuthorRules(*state.element(), collector);
if (state.element()->isStyledElement()) {
- if (state.element()->inlineStyle()) {
+ // For Shadow DOM V1, inline style is already collected in matchScopedRules().
+ if (state.element()->document().styleEngine().shadowCascadeOrder() != ShadowCascadeOrder::ShadowCascadeV1 && state.element()->inlineStyle()) {
// Inline style is immutable as long as there is no CSSOM wrapper.
bool isInlineStyleCacheable = !state.element()->inlineStyle()->isMutable();
collector.addElementStyleProperties(state.element()->inlineStyle(), isInlineStyleCacheable);
@@ -501,30 +623,6 @@ void StyleResolver::matchAllRules(StyleResolverState& state, ElementRuleCollecto
collector.finishAddingAuthorRulesForTreeScope();
}
-static bool shouldCheckScope(const Element& element, const Node& scopingNode, bool isInnerTreeScope)
-{
- if (isInnerTreeScope && element.treeScope() != scopingNode.treeScope()) {
- // Check if |element| may be affected by a ::content rule in |scopingNode|'s style.
- // If |element| is a descendant of a shadow host which is ancestral to |scopingNode|,
- // the |element| should be included for rule collection.
- // Skip otherwise.
- const TreeScope* scope = &scopingNode.treeScope();
- while (scope && scope->parentTreeScope() != &element.treeScope())
- scope = scope->parentTreeScope();
- Element* shadowHost = scope ? scope->rootNode().shadowHost() : nullptr;
- return shadowHost && element.isDescendantOf(shadowHost);
- }
-
- // When |element| can be distributed to |scopingNode| via <shadow>, ::content rule can match,
- // thus the case should be included.
- if (!isInnerTreeScope && scopingNode.parentOrShadowHostNode() == element.treeScope().rootNode().parentOrShadowHostNode())
- return true;
-
- // Obviously cases when ancestor scope has /deep/ or ::shadow rule should be included.
- // Skip otherwise.
- return scopingNode.treeScope().scopedStyleResolver()->hasDeepOrShadowSelector();
-}
-
void StyleResolver::collectTreeBoundaryCrossingRules(const Element& element, ElementRuleCollector& collector)
{
if (m_treeBoundaryCrossingScopes.isEmpty())

Powered by Google App Engine
This is Rietveld 408576698