| 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())
|
|
|