Commit 8cee5a4d authored by kochi@chromium.org's avatar kochi@chromium.org

Change shadow style's scope from Shadow Host to Shadow Root

No visible change, test expectation change is expected.

Scope of styles in shadow tree used to be its shadow host
for ease of checking the style against :host etc.

This caused code complexity for checking against boundary
breaking CSS selector rules (e.g. ::content, /deep/).
Changing the scope to its shadow root will simplify some
condition checks.

This is a step towards fixing issue 355674.

BUG=355674
TEST=pass all layout tests

Review URL: https://codereview.chromium.org/313303002

git-svn-id: svn://svn.chromium.org/blink/trunk@176304 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 004e60fb
......@@ -130,7 +130,7 @@ static bool rulesApplicableInCurrentTreeScope(const Element* element, const Cont
if (!scopingNode || treeScope == scopingNode->treeScope())
return true;
// d) the rules comes from a scoped style sheet within an active shadow root whose host is the given element
if (element->isInShadowTree() && (behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost) && scopingNode == element->containingShadowRoot()->host())
if (SelectorChecker::isHostInItsShadowTree(*element, behaviorAtBoundary, scopingNode))
return true;
return false;
}
......
......@@ -83,8 +83,8 @@ Element* SelectorChecker::parentElement(const SelectorCheckingContext& context,
if (allowToCrossBoundary)
return context.element->parentOrShadowHostElement();
// If context.scope is a shadow host, we should walk up from a shadow root to its shadow host.
if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost) && context.scope == context.element->shadowHost())
// If context.scope is a shadow root, we should walk up to its shadow host.
if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot) && context.scope == context.element->containingShadowRoot())
return context.element->parentOrShadowHostElement();
if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope)
......@@ -106,12 +106,12 @@ bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingCont
return true;
ASSERT(context.scope);
// If behaviorAtBoundary is not ScopeIsShadowHost, we can use "contains".
if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowHost))
// If behaviorAtBoundary is not ScopeIsShadowRoot, we can use "contains".
if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot))
return context.scope->contains(context.element);
// If a given element is scope, i.e. shadow host, matches.
if (context.element == context.scope && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element)))
if (context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element)))
return true;
ShadowRoot* root = context.element->containingShadowRoot();
......@@ -119,16 +119,14 @@ bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingCont
return false;
// If a host of the containing shadow root is scope, matches.
return root->host() == context.scope;
return root == context.scope;
}
static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
{
if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope)
return context.element == context.scope;
if (context.scope && context.scope->isInShadowTree())
return context.element == context.scope->containingShadowRoot()->host();
return context.element == context.scope->shadowHost();
return false;
}
......@@ -342,7 +340,7 @@ SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingC
case CSSSelector::ShadowPseudo:
{
// If we're in the same tree-scope as the scoping element, then following a shadow descendant combinator would escape that and thus the scope.
if (context.scope && context.scope->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
return SelectorFailsCompletely;
Element* shadowHost = context.element->shadowHost();
......@@ -389,15 +387,16 @@ SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element*
for (size_t i = 0; i < insertionPoints.size(); ++i) {
nextContext.element = insertionPoints[i];
// If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowHost,
// If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowRoot,
// we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly.
if (m_mode == SharingRules) {
nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
nextContext.scope = insertionPoints[i]->containingShadowRoot()->shadowHost();
} else if (scope == insertionPoints[i]->containingShadowRoot()->shadowHost() && !(behaviorAtBoundary & ScopeIsShadowHost))
nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowHost);
else
nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
nextContext.scope = insertionPoints[i]->containingShadowRoot();
} else if (scope == insertionPoints[i]->containingShadowRoot() && !(behaviorAtBoundary & ScopeIsShadowRoot)) {
nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
} else {
nextContext.behaviorAtBoundary = behaviorAtBoundary;
}
nextContext.isSubSelector = false;
nextContext.elementStyle = 0;
......@@ -537,7 +536,8 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
// Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element
if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass())
if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass()
&& !(context.behaviorAtBoundary & TreatShadowHostAsNormalScope))
return false;
if (selector.match() == CSSSelector::Tag)
......@@ -929,7 +929,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
// :host only matches a shadow host when :host is in a shadow tree of the shadow host.
if (!context.scope)
return false;
const ContainerNode* shadowHost = (context.behaviorAtBoundary & ScopeIsShadowHost) ? context.scope : (context.scope->isInShadowTree() ? context.scope->shadowHost() : 0);
const ContainerNode* shadowHost = context.scope->shadowHost();
if (!shadowHost || shadowHost != element)
return false;
ASSERT(element.shadow());
......@@ -947,7 +947,7 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
// If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter;
subContext.scope = shadowHost;
subContext.scope = context.scope;
// Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
Element* nextElement = &element;
SelectorCheckingContext hostContext(subContext);
......
......@@ -53,10 +53,10 @@ public:
StaysWithinTreeScope = 2,
BoundaryBehaviorMask = 3, // 2bit for boundary behavior
ScopeContainsLastMatchedElement = 4,
ScopeIsShadowHost = 8,
ScopeIsShadowRoot = 8,
TreatShadowHostAsNormalScope = 16,
ScopeIsShadowHostInPseudoHostParameter = ScopeIsShadowHost | TreatShadowHostAsNormalScope
ScopeIsShadowHostInPseudoHostParameter = ScopeIsShadowRoot | TreatShadowHostAsNormalScope
};
struct SelectorCheckingContext {
......@@ -179,11 +179,7 @@ inline bool SelectorChecker::checkExactAttribute(const Element& element, const Q
inline bool SelectorChecker::isHostInItsShadowTree(const Element& element, BehaviorAtBoundary behaviorAtBoundary, const ContainerNode* scope)
{
if ((behaviorAtBoundary & (ScopeIsShadowHost | TreatShadowHostAsNormalScope)) == ScopeIsShadowHost)
return scope == element;
if (scope && scope->isInShadowTree())
return scope->shadowHost() == element;
return false;
return scope && scope->isInShadowTree() && scope->shadowHost() == element;
}
}
......
......@@ -79,12 +79,9 @@ void TreeBoundaryCrossingRules::collectTreeBoundaryCrossingRules(Element* elemen
unsigned boundaryBehavior = SelectorChecker::ScopeContainsLastMatchedElement;
bool isInnerTreeScope = element->treeScope().isInclusiveAncestorOf(scopingNode->treeScope());
// If a given scoping node is a shadow root,
// we should use ScopeIsShadowHost.
if (scopingNode && scopingNode->isShadowRoot()) {
boundaryBehavior |= SelectorChecker::ScopeIsShadowHost;
scopingNode = toShadowRoot(scopingNode)->host();
}
// If a given scoping node is a shadow root, we should use ScopeIsShadowRoot.
if (scopingNode && scopingNode->isShadowRoot())
boundaryBehavior |= SelectorChecker::ScopeIsShadowRoot;
CascadeOrder cascadeOrder = isInnerTreeScope ? innerCascadeOrder : outerCascadeOrder;
for (CSSStyleSheetRuleSubSet::iterator it = ruleSubSet->begin(); it != ruleSubSet->end(); ++it) {
......
......@@ -120,10 +120,8 @@ void ScopedStyleResolver::collectMatchingAuthorRules(ElementRuleCollector& colle
if (!applyAuthorStyles)
behaviorAtBoundary |= SelectorChecker::ScopeContainsLastMatchedElement;
if (m_scopingNode.isShadowRoot()) {
scopingNode = toShadowRoot(m_scopingNode).host();
behaviorAtBoundary |= SelectorChecker::ScopeIsShadowHost;
}
if (m_scopingNode.isShadowRoot())
behaviorAtBoundary |= SelectorChecker::ScopeIsShadowRoot;
RuleRange ruleRange = collector.matchedResult().ranges.authorRuleRange();
for (size_t i = 0; i < m_authorStyleSheets.size(); ++i) {
......
......@@ -39,7 +39,6 @@ namespace WebCore {
class MediaQueryEvaluator;
class PageRuleCollector;
class ShadowRoot;
class StyleSheetContents;
// This class selects a RenderStyle for a given element based on a collection of stylesheets.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment