Commit fef81767 authored by esprehn@chromium.org's avatar esprehn@chromium.org

Only :host and :ancestor should ever match the host in a ShadowRoot

We were blocking tag, id and class from matching when put before :host, but were
not correctly stopping things like :first-child:host or *:host.

Instead of trying to black list selectors we need to whitelist :host and
:ancestor as being special.

It turns out we did have test coverage for this, but the expected results were
all wrong so it printed PASS even though we were failing what the test was
supposed to be testing.

BUG=355149, 355158

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

git-svn-id: svn://svn.chromium.org/blink/trunk@169799 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 74420b3c
...@@ -6,7 +6,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE ...@@ -6,7 +6,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
:ancestor out of shadow tree should not match any shadow hosts. :ancestor out of shadow tree should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:ancestor with * should not match any shadow hosts. :ancestor with * should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgb(0, 128, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:ancestor with tag selector should not match any shadow hosts. :ancestor with tag selector should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:ancestor with class selector should not match any shadow hosts. :ancestor with class selector should not match any shadow hosts.
......
...@@ -53,7 +53,7 @@ sandbox.appendChild( ...@@ -53,7 +53,7 @@ sandbox.appendChild(
createDOM('div', {}, createDOM('div', {},
document.createTextNode('Hello'))))); document.createTextNode('Hello')))));
backgroundColorShouldBe('host', 'rgb(0, 128, 0)'); backgroundColorShouldBe('host', 'rgba(0, 0, 0, 0)');
cleanUp(); cleanUp();
......
...@@ -6,7 +6,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE ...@@ -6,7 +6,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
:host out of shadow tree should not match any shadow hosts. :host out of shadow tree should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:host with * should not match any shadow hosts. :host with * should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgb(0, 128, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:host with tag selector should not match any shadow hosts. :host with tag selector should not match any shadow hosts.
PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)" PASS backgroundColorOf('host') is "rgba(0, 0, 0, 0)"
:host with class selector should not match any shadow hosts. :host with class selector should not match any shadow hosts.
......
...@@ -53,7 +53,7 @@ sandbox.appendChild( ...@@ -53,7 +53,7 @@ sandbox.appendChild(
createDOM('div', {}, createDOM('div', {},
document.createTextNode('Hello'))))); document.createTextNode('Hello')))));
backgroundColorShouldBe('host', 'rgb(0, 128, 0)'); backgroundColorShouldBe('host', 'rgba(0, 0, 0, 0)');
cleanUp(); cleanUp();
......
...@@ -9,9 +9,11 @@ PASS borderColorOf('host-child') is "rgb(0, 0, 0)" ...@@ -9,9 +9,11 @@ PASS borderColorOf('host-child') is "rgb(0, 0, 0)"
Test that * in a shadow tree cannot match without :host. Test that * in a shadow tree cannot match without :host.
PASS borderColorOf('host') is not "rgb(0, 128, 0)" PASS borderColorOf('host') is not "rgb(0, 128, 0)"
PASS borderColorOf('host-child') is "rgb(0, 0, 0)" PASS borderColorOf('host-child') is "rgb(0, 0, 0)"
Test that :not(*) in a shadow tree can match without :host. Test that :not(*) in a shadow tree cannot match without :host.
PASS borderColorOf('host') is "rgb(0, 128, 0)" PASS borderColorOf('host') is "rgb(0, 0, 0)"
PASS borderColorOf('host-child') is "rgb(0, 0, 0)" PASS borderColorOf('host-child') is "rgb(0, 0, 0)"
Test that :first-child in a shadow tree cannot match without :host.
PASS borderColorOf('host') is "rgb(0, 0, 0)"
Test that styles in a containing treescope wins if specificities are the same. Test that styles in a containing treescope wins if specificities are the same.
PASS borderColorOf('host') is "rgb(0, 128, 0)" PASS borderColorOf('host') is "rgb(0, 128, 0)"
Test that rules which has higher specificity win. Test that rules which has higher specificity win.
......
...@@ -63,7 +63,7 @@ shouldHaveBorderColor('host-child', 'rgb(0, 0, 0)'); ...@@ -63,7 +63,7 @@ shouldHaveBorderColor('host-child', 'rgb(0, 0, 0)');
cleanUp(); cleanUp();
debug('Test that :not(*) in a shadow tree can match without :host.'); debug('Test that :not(*) in a shadow tree cannot match without :host.');
sandbox.appendChild( sandbox.appendChild(
createDOM('div', {'id': 'host'}, createDOM('div', {'id': 'host'},
...@@ -74,11 +74,26 @@ sandbox.appendChild( ...@@ -74,11 +74,26 @@ sandbox.appendChild(
createDOM('div', {'id': 'host-child'}, createDOM('div', {'id': 'host-child'},
document.createTextNode('Hello, Host!')))); document.createTextNode('Hello, Host!'))));
shouldHaveBorderColor('host', 'rgb(0, 128, 0)'); shouldHaveBorderColor('host', 'rgb(0, 0, 0)');
shouldHaveBorderColor('host-child', 'rgb(0, 0, 0)'); shouldHaveBorderColor('host-child', 'rgb(0, 0, 0)');
cleanUp(); cleanUp();
debug('Test that :first-child in a shadow tree cannot match without :host.');
sandbox.appendChild(
createDOM('div', {'id': 'host'},
createShadowRoot(
createDOM('style', {},
document.createTextNode(':first-child { border: 1px solid green; }')),
createDOM('content', {})),
createDOM('div', {'id': 'host-child'},
document.createTextNode('Hello, Host!'))));
shouldHaveBorderColor('host', 'rgb(0, 0, 0)');
cleanUp();
debug('Test that styles in a containing treescope wins if specificities are the same.'); debug('Test that styles in a containing treescope wins if specificities are the same.');
sandbox.appendChild( sandbox.appendChild(
......
...@@ -258,4 +258,14 @@ CSSParserSelector* CSSParserSelector::findDistributedPseudoElementSelector() con ...@@ -258,4 +258,14 @@ CSSParserSelector* CSSParserSelector::findDistributedPseudoElementSelector() con
return 0; return 0;
} }
bool CSSParserSelector::hasHostPseudoSelector() const
{
CSSParserSelector* selector = const_cast<CSSParserSelector*>(this);
do {
if (selector->pseudoType() == CSSSelector::PseudoHost || selector->pseudoType() == CSSSelector::PseudoAncestor)
return true;
} while ((selector = selector->tagHistory()));
return false;
}
} // namespace WebCore } // namespace WebCore
...@@ -233,6 +233,7 @@ public: ...@@ -233,6 +233,7 @@ public:
void setFunctionArgumentSelector(CSSParserSelector* selector) { m_functionArgumentSelector = selector; } void setFunctionArgumentSelector(CSSParserSelector* selector) { m_functionArgumentSelector = selector; }
bool isDistributedPseudoElement() const { return m_selector->isDistributedPseudoElement(); } bool isDistributedPseudoElement() const { return m_selector->isDistributedPseudoElement(); }
CSSParserSelector* findDistributedPseudoElementSelector() const; CSSParserSelector* findDistributedPseudoElementSelector() const;
bool hasHostPseudoSelector() const;
CSSSelector::PseudoType pseudoType() const { return m_selector->pseudoType(); } CSSSelector::PseudoType pseudoType() const { return m_selector->pseudoType(); }
bool isCustomPseudoElement() const { return m_selector->isCustomPseudoElement(); } bool isCustomPseudoElement() const { return m_selector->isCustomPseudoElement(); }
......
...@@ -386,7 +386,7 @@ inline bool CSSSelector::isCustomPseudoElement() const ...@@ -386,7 +386,7 @@ inline bool CSSSelector::isCustomPseudoElement() const
inline bool CSSSelector::isHostPseudoClass() const inline bool CSSSelector::isHostPseudoClass() const
{ {
return m_match == PseudoClass && m_pseudoType == PseudoHost; return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoAncestor);
} }
inline bool CSSSelector::isSiblingSelector() const inline bool CSSSelector::isSiblingSelector() const
......
...@@ -490,18 +490,20 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib ...@@ -490,18 +490,20 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const Sib
bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope); 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())
return false;
if (selector.m_match == CSSSelector::Tag) if (selector.m_match == CSSSelector::Tag)
return SelectorChecker::tagMatches(element, selector.tagQName()) && !elementIsHostInItsShadowTree; return SelectorChecker::tagMatches(element, selector.tagQName());
if (selector.m_match == CSSSelector::Class) if (selector.m_match == CSSSelector::Class)
return element.hasClass() && element.classNames().contains(selector.value()) && !elementIsHostInItsShadowTree; return element.hasClass() && element.classNames().contains(selector.value());
if (selector.m_match == CSSSelector::Id) if (selector.m_match == CSSSelector::Id)
return element.hasID() && element.idForStyleResolution() == selector.value() && !elementIsHostInItsShadowTree; return element.hasID() && element.idForStyleResolution() == selector.value();
if (selector.isAttributeSelector()) { if (selector.isAttributeSelector()) {
if (elementIsHostInItsShadowTree)
return false;
if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector.m_match), selector)) if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector.m_match), selector))
return false; return false;
} }
......
...@@ -1934,9 +1934,11 @@ CSSParserSelector* BisonCSSParser::rewriteSpecifiersWithElementName(const Atomic ...@@ -1934,9 +1934,11 @@ CSSParserSelector* BisonCSSParser::rewriteSpecifiersWithElementName(const Atomic
if (specifiers->needsCrossingTreeScopeBoundary()) if (specifiers->needsCrossingTreeScopeBoundary())
return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, elementName, specifiers, tagIsForNamespaceRule); return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, elementName, specifiers, tagIsForNamespaceRule);
if (tag == anyQName()) // *:host never matches, so we can't discard the * otherwise we can't tell the
// difference between *:host and just :host.
if (tag == anyQName() && !specifiers->hasHostPseudoSelector())
return specifiers; return specifiers;
if (!(specifiers->pseudoType() == CSSSelector::PseudoCue)) if (specifiers->pseudoType() != CSSSelector::PseudoCue)
specifiers->prependTagSelector(tag, tagIsForNamespaceRule); specifiers->prependTagSelector(tag, tagIsForNamespaceRule);
return specifiers; return specifiers;
} }
......
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