Index: Source/core/css/resolver/ScopedStyleTree.cpp |
diff --git a/Source/core/css/resolver/ScopedStyleTree.cpp b/Source/core/css/resolver/ScopedStyleTree.cpp |
index fe3f8a3d2cb031f689dde5679ab7c84a80743faf..0621d3372059310cb7299ca0e2506f8e66c41167 100644 |
--- a/Source/core/css/resolver/ScopedStyleTree.cpp |
+++ b/Source/core/css/resolver/ScopedStyleTree.cpp |
@@ -66,6 +66,34 @@ ScopedStyleResolver* ScopedStyleTree::addScopedStyleResolver(ContainerNode& scop |
return addResult.iterator->value.get(); |
} |
+// To make younger shadow root act as a child of its older shadow root. |
+ContainerNode* ScopedStyleTree::traverseParent(const ContainerNode* node) const |
+{ |
+ if (node->isShadowRoot()) { |
+ const ShadowRoot* shadowRoot = toShadowRoot(node); |
+ if (shadowRoot->isOldest()) |
+ return shadowRoot->host(); |
+ return shadowRoot->olderShadowRoot(); |
+ } |
+ |
+ ContainerNode* parent = node->parentOrShadowHostNode(); |
+ return parent && parent->isShadowRoot() ? toShadowRoot(parent)->owner()->youngestShadowRoot() : parent; |
+} |
+ |
+static inline bool containsIncludingShadowRoots(const ContainerNode* node1, const ContainerNode* node2) |
+{ |
+ ASSERT(node1); |
+ ASSERT(node2); |
+ |
+ // If node1 is a older shadow root and node2 is an younger shadow root hosted by the |
+ // same shadow host, treat as if node1 contains node2. |
+ if (node1->isShadowRoot()) { |
+ Node* host = toShadowRoot(node1)->host(); |
+ return host != node2 && host->containsIncludingShadowDOM(node2); |
+ } |
+ return node1->containsIncludingShadowDOM(node2); |
+} |
+ |
void ScopedStyleTree::setupScopedStylesTree(ScopedStyleResolver* target) |
{ |
ASSERT(target); |
@@ -75,7 +103,7 @@ void ScopedStyleTree::setupScopedStylesTree(ScopedStyleResolver* target) |
// Since StyleResolver creates RuleSets according to styles' document |
// order, a parent of the given ScopedRuleData has been already |
// prepared. |
- for (ContainerNode* node = scopingNode.parentOrShadowHostNode(); node; node = node->parentOrShadowHostNode()) { |
+ for (ContainerNode* node = traverseParent(&scopingNode); node; node = traverseParent(node)) { |
if (ScopedStyleResolver* scopedResolver = scopedStyleResolverFor(*node)) { |
target->setParent(scopedResolver); |
break; |
@@ -97,7 +125,7 @@ void ScopedStyleTree::setupScopedStylesTree(ScopedStyleResolver* target) |
if (it->value == target) |
continue; |
ASSERT(it->key->inDocument()); |
- if (it->value->parent() == target->parent() && scopingNode.containsIncludingShadowDOM(it->key)) |
+ if (it->value->parent() == target->parent() && containsIncludingShadowRoots(&scopingNode, it->key)) |
it->value->setParent(target); |
} |
} |
@@ -115,43 +143,33 @@ void ScopedStyleTree::resolveScopedStyles(const Element* element, Vector<ScopedS |
resolvers.append(scopedResolver); |
} |
-void ScopedStyleTree::collectScopedResolversForHostedShadowTrees(const Element* element, Vector<ScopedStyleResolver*, 8>& resolvers) |
-{ |
- ElementShadow* shadow = element->shadow(); |
- if (!shadow) |
- return; |
- |
- // Adding scoped resolver for active shadow roots for shadow host styling. |
- for (ShadowRoot* shadowRoot = shadow->youngestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->olderShadowRoot()) { |
- if (shadowRoot->hasScopedHTMLStyleChild()) { |
- if (ScopedStyleResolver* resolver = scopedStyleResolverFor(*shadowRoot)) |
- resolvers.append(resolver); |
- } |
- if (!shadowRoot->containsShadowElements()) |
- break; |
- } |
-} |
- |
void ScopedStyleTree::resolveScopedKeyframesRules(const Element* element, Vector<ScopedStyleResolver*, 8>& resolvers) |
{ |
Document& document = element->document(); |
TreeScope& treeScope = element->treeScope(); |
bool applyAuthorStyles = treeScope.applyAuthorStyles(); |
- // Add resolvers for shadow roots hosted by the given element. |
- collectScopedResolversForHostedShadowTrees(element, resolvers); |
- |
// Add resolvers while walking up DOM tree from the given element. |
- for (ScopedStyleResolver* scopedResolver = scopedResolverFor(element); scopedResolver; scopedResolver = scopedResolver->parent()) { |
- if (scopedResolver->treeScope() == treeScope || (applyAuthorStyles && scopedResolver->treeScope() == document)) |
+ ScopedStyleResolver* scopedResolver = scopedResolverFor(element); |
+ for (; scopedResolver; scopedResolver = scopedResolver->parent()) { |
+ if (scopedResolver->treeScope() == treeScope |
+ || (scopedResolver->scopingNode().isShadowRoot() && toShadowRoot(&scopedResolver->scopingNode())->host() == element) |
+ || (applyAuthorStyles && scopedResolver->treeScope() == document)) |
resolvers.append(scopedResolver); |
} |
} |
inline ScopedStyleResolver* ScopedStyleTree::enclosingScopedStyleResolverFor(const ContainerNode* scopingNode) |
{ |
- for (; scopingNode; scopingNode = scopingNode->parentOrShadowHostNode()) { |
- if (ScopedStyleResolver* scopedStyleResolver = scopedStyleResolverFor(*scopingNode)) |
+ const ContainerNode* node; |
+ // FIXME: we should split "style tree" traversal logic out from this method. |
+ if (isShadowHost(scopingNode)) { |
+ node = toElement(scopingNode)->shadow()->youngestShadowRoot(); |
+ } else { |
+ node = scopingNode->isShadowRoot() ? toShadowRoot(scopingNode)->owner()->youngestShadowRoot() : scopingNode; |
+ } |
+ for (; node; node = traverseParent(node)) { |
+ if (ScopedStyleResolver* scopedStyleResolver = scopedStyleResolverFor(*node)) |
return scopedStyleResolver; |
} |
return 0; |
@@ -179,13 +197,43 @@ void ScopedStyleTree::pushStyleCache(const ContainerNode& scopingNode, const Con |
m_cache.nodeForScopedStyles = &scopingNode; |
} |
+static inline bool shadowRootHasTheSameShadowHost(const ContainerNode& node, const Node* host) |
+{ |
+ if (!node.isShadowRoot()) |
+ return false; |
+ return toShadowRoot(&node)->host() == host; |
+} |
+ |
void ScopedStyleTree::popStyleCache(const ContainerNode& scopingNode) |
{ |
if (!cacheIsValid(&scopingNode)) |
return; |
- if (m_cache.scopedResolver && m_cache.scopedResolver->scopingNode() == scopingNode) |
- m_cache.scopedResolver = m_cache.scopedResolver->parent(); |
+ if (!scopingNode.isShadowRoot()) { |
+ if (m_cache.scopedResolver && m_cache.scopedResolver->scopingNode() == scopingNode) |
+ m_cache.scopedResolver = m_cache.scopedResolver->parent(); |
+ |
+ m_cache.nodeForScopedStyles = scopingNode.parentOrShadowHostNode(); |
+ return; |
+ } |
+ |
+ const ShadowRoot* shadowRoot = toShadowRoot(&scopingNode); |
+ const Node* host = shadowRoot->host(); |
+ |
+ // If a given scoping node is the oldest shadow root and cached resolver's scoping node |
+ // is a shadow root (mostly, the youngest shadow root) hosted by the same host of |
+ // the given shadow root, update cacheed resolver. |
+ if (m_cache.scopedResolver |
+ && shadowRoot->isOldest() |
+ && shadowRootHasTheSameShadowHost(m_cache.scopedResolver->scopingNode(), host)) { |
+ ScopedStyleResolver* resolver = m_cache.scopedResolver; |
+ |
+ while (shadowRootHasTheSameShadowHost(resolver->scopingNode(), host)) |
+ resolver = resolver->parent(); |
+ |
+ m_cache.scopedResolver = resolver; |
+ } |
+ |
m_cache.nodeForScopedStyles = scopingNode.parentOrShadowHostNode(); |
} |