Chromium Code Reviews| 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 fdfe9b47921c1d23db88047c98a4d187a92cef4e..437ce158859b80201055b24fd9829e511b5929b3 100644 |
| --- a/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp |
| +++ b/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp |
| @@ -387,7 +387,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 |
| @@ -411,8 +411,126 @@ 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()) { |
|
rune
2016/03/17 10:59:02
This method is used for V1. Can we have multiple s
kochi
2016/03/18 09:00:52
Yeah, it is counterintuitive at a glance but once
rune
2016/03/18 12:04:12
No, it was just me thinking out loud to confirm it
|
| + 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(); |
| + |
| + for (auto it = m_treeBoundaryCrossingScopes.rbegin(); it != m_treeBoundaryCrossingScopes.rend(); ++it) { |
|
rune
2016/03/17 10:59:02
We should not need to walk m_treeBoundaryCrossingS
kochi
2016/03/18 09:00:52
Added TODO comment.
Probably we can have an "pure
rune
2016/03/18 12:04:12
Acknowledged.
We can iterate on this.
|
| + 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().useCascadeOrderForShadowDOMV1()) { |
| + 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; |
| @@ -485,7 +603,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().useCascadeOrderForShadowDOMV1() && 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); |
| @@ -499,30 +618,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()) |