Index: Source/core/css/resolver/StyleResolver.cpp |
diff --git a/Source/core/css/resolver/StyleResolver.cpp b/Source/core/css/resolver/StyleResolver.cpp |
index 1d84bacaf6c88376f7580e695724a25b7b7100e1..c38e67ba3f130509554da735338c97107c2ec8ce 100644 |
--- a/Source/core/css/resolver/StyleResolver.cpp |
+++ b/Source/core/css/resolver/StyleResolver.cpp |
@@ -124,9 +124,9 @@ static StylePropertySet* rightToLeftDeclaration() |
return rightToLeftDecl; |
} |
-static void collectScopedResolversForHostedShadowTrees(const Element* element, WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8>& resolvers) |
+static void collectScopedResolversForHostedShadowTrees(const Element& element, WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8>& resolvers) |
{ |
- ElementShadow* shadow = element->shadow(); |
+ ElementShadow* shadow = element.shadow(); |
if (!shadow) |
return; |
@@ -139,6 +139,25 @@ static void collectScopedResolversForHostedShadowTrees(const Element* element, W |
} |
} |
+static void collectScopedResolversForTreeBoundaryCrossingRules(const Element& element, WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8>& resolversInOuterScopes, WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8>& resolversInDistributedScopes) |
+{ |
+ // TODO(rune@opera.com): TreeScope::treeScopeInComposedTree can potentially be expensive as its doing |
+ // composed tree traversal to find the closest tree-scope for the element in the composed tree. Getting the |
+ // ancestor tree-scopes are much cheaper as we do the composed tree traversal for each TreeScope once and then cache |
+ // its composed tree parent scope as a member. |
+ for (TreeScope* scope = TreeScope::treeScopeInComposedTree(element); scope; scope = scope->composedParent()) { |
+ if (scope == element.treeScope()) |
+ continue; |
+ ScopedStyleResolver* resolver = scope->scopedStyleResolver(); |
+ if (!resolver || !resolver->hasTreeBoundaryCrossingRules()) |
+ continue; |
+ if (element.treeScope().isInclusiveAncestorOf(resolver->treeScope())) |
+ resolversInDistributedScopes.append(resolver); |
+ else |
+ resolversInOuterScopes.append(resolver); |
+ } |
+} |
+ |
StyleResolver::StyleResolver(Document& document) |
: m_document(document) |
, m_viewportStyleResolver(ViewportStyleResolver::create(&document)) |
@@ -233,14 +252,8 @@ void StyleResolver::resetRuleFeatures() |
m_needCollectFeatures = true; |
} |
-void StyleResolver::addTreeBoundaryCrossingScope(ContainerNode& scope) |
-{ |
- m_treeBoundaryCrossingRules.addScope(scope); |
-} |
- |
void StyleResolver::resetAuthorStyle(TreeScope& treeScope) |
{ |
- m_treeBoundaryCrossingRules.removeScope(treeScope.rootNode()); |
resetRuleFeatures(); |
ScopedStyleResolver* resolver = treeScope.scopedStyleResolver(); |
@@ -359,7 +372,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 treeBoundaryCrossingRules. |
// 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 |
@@ -385,24 +398,69 @@ static inline ScopedStyleResolver* scopedResolverFor(const Element* element) |
void StyleResolver::matchAuthorRules(Element* element, ElementRuleCollector& collector, bool includeEmptyRules) |
{ |
- collector.clearMatchedRules(); |
collector.matchedResult().ranges.lastAuthorRule = collector.matchedResult().matchedProperties.size() - 1; |
- CascadeOrder cascadeOrder = 0; |
WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> resolversInShadowTree; |
- collectScopedResolversForHostedShadowTrees(element, resolversInShadowTree); |
+ WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> resolversInOuterScopes; |
+ WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> resolversInDistributedScopes; |
+ |
+ collectScopedResolversForHostedShadowTrees(*element, resolversInShadowTree); |
+ if (document().styleEngine().hasTreeBoundaryCrossingRules()) |
+ collectScopedResolversForTreeBoundaryCrossingRules(*element, resolversInOuterScopes, resolversInDistributedScopes); |
+ |
+ // According to the CSS Scoping spec, cascading of rules from inner vs outer scopes happens |
+ // such that outer scope rules wins regardless of specificity. For rules from tree-scopes |
+ // that do not have an ascendant/descendant relationship in the tree-of-trees (typical for |
+ // ::content rules from different scopes during re-distribution), tree-of-trees order of |
+ // appearance applies, hence specificity wins although last rule in order of appearance wins |
+ // if the specificity is the same. |
+ // |
+ // The code below uses CascadeOrder which is an order-of-appearance value, and should give |
+ // the correct cascading order when order-of-appearance applies. |
+ // The "outer scope wins" rule is ensured by calls to sortAndTransferMatchedRules(). |
+ // |
+ // TODO(rune@opera.com): The current implementation does not handle cascading order as |
+ // spec'ed for !important rules or multiple shadow trees per host. |
// Apply :host and :host-context rules from inner scopes. |
- for (int j = resolversInShadowTree.size() - 1; j >= 0; --j) |
- resolversInShadowTree.at(j)->collectMatchingShadowHostRules(collector, includeEmptyRules, ++cascadeOrder); |
+ if (resolversInShadowTree.size()) { |
+ collector.clearMatchedRules(); |
+ CascadeOrder cascadeOrder = 0; |
+ for (int i = resolversInShadowTree.size() - 1; i >= 0; --i) |
+ resolversInShadowTree.at(i)->collectMatchingShadowHostRules(collector, includeEmptyRules, ++cascadeOrder); |
+ collector.sortAndTransferMatchedRules(); |
+ } |
- // Apply normal rules from element scope. |
- if (ScopedStyleResolver* resolver = scopedResolverFor(element)) |
- resolver->collectMatchingAuthorRules(collector, includeEmptyRules, ++cascadeOrder); |
+ // Apply ::content rules from distributed scopes |
+ if (resolversInDistributedScopes.size()) { |
+ collector.clearMatchedRules(); |
+ CascadeOrder cascadeOrder = 0; |
+ for (int j = resolversInDistributedScopes.size() - 1; j >= 0; --j) |
+ resolversInDistributedScopes.at(j)->collectMatchingTreeBoundaryCrossingRules(collector, includeEmptyRules, ++cascadeOrder); |
+ collector.sortAndTransferMatchedRules(); |
+ } |
- // Apply /deep/ and ::shadow rules from outer scopes, and ::content from inner. |
- m_treeBoundaryCrossingRules.collectTreeBoundaryCrossingRules(element, collector, includeEmptyRules); |
- collector.sortAndTransferMatchedRules(); |
+ // Apply rules from the element's scope. |
+ if (ScopedStyleResolver* resolver = scopedResolverFor(element)) { |
+ collector.clearMatchedRules(); |
+ resolver->collectMatchingAuthorRules(collector, includeEmptyRules, ignoreCascadeOrder); |
+ |
+ // TODO(rune@opera.com): Only piercing rules, of the boundary crossing rules, |
+ // can match elements in the same scope as the rules, thus we can drop the code |
+ // below if piercing combinators are removed. |
+ if (resolver->hasTreeBoundaryCrossingRules()) |
+ resolver->collectMatchingTreeBoundaryCrossingRules(collector, includeEmptyRules, ignoreCascadeOrder); |
+ |
+ collector.sortAndTransferMatchedRules(); |
+ } |
+ |
+ // Apply /deep/ and ::shadow rules from outer scopes. |
+ // TODO(rune@opera.com): The code below can be removed if we remove support for shadow piercing selectors. |
+ for (unsigned k = 0; k < resolversInOuterScopes.size(); k++) { |
+ collector.clearMatchedRules(); |
+ resolversInOuterScopes.at(k)->collectMatchingTreeBoundaryCrossingRules(collector, includeEmptyRules, ignoreCascadeOrder); |
+ collector.sortAndTransferMatchedRules(); |
+ } |
} |
void StyleResolver::matchUARules(ElementRuleCollector& collector) |
@@ -994,7 +1052,7 @@ bool StyleResolver::applyAnimatedProperties(StyleResolverState& state, const Ele |
StyleRuleKeyframes* StyleResolver::findKeyframesRule(const Element* element, const AtomicString& animationName) |
{ |
WillBeHeapVector<RawPtrWillBeMember<ScopedStyleResolver>, 8> resolvers; |
- collectScopedResolversForHostedShadowTrees(element, resolvers); |
+ collectScopedResolversForHostedShadowTrees(*element, resolvers); |
if (ScopedStyleResolver* scopedResolver = element->treeScope().scopedStyleResolver()) |
resolvers.append(scopedResolver); |
@@ -1503,7 +1561,6 @@ DEFINE_TRACE(StyleResolver) |
visitor->trace(m_siblingRuleSet); |
visitor->trace(m_uncommonAttributeRuleSet); |
visitor->trace(m_watchedSelectorsRules); |
- visitor->trace(m_treeBoundaryCrossingRules); |
visitor->trace(m_styleResourceLoader); |
visitor->trace(m_styleSharingLists); |
visitor->trace(m_pendingStyleSheets); |