Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(587)

Unified Diff: Source/core/css/parser/CSSSelectorParser.cpp

Issue 1094213002: Refactored compound selector parsing methods. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Addresses review issues Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/css/parser/CSSSelectorParser.h ('k') | Source/core/css/parser/CSSSelectorParserTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/css/parser/CSSSelectorParser.cpp
diff --git a/Source/core/css/parser/CSSSelectorParser.cpp b/Source/core/css/parser/CSSSelectorParser.cpp
index 653a8d80d8e4bb1e7705ba8e84b8eafaba9b74a6..cc7d7b28e231e95f76ce4ddabc486ea386bc2331 100644
--- a/Source/core/css/parser/CSSSelectorParser.cpp
+++ b/Source/core/css/parser/CSSSelectorParser.cpp
@@ -101,36 +101,33 @@ PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParse
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
{
- OwnPtr<CSSParserSelector> selector;
+ OwnPtr<CSSParserSelector> compoundSelector;
AtomicString namespacePrefix;
AtomicString elementName;
bool hasNamespace;
if (!consumeName(range, elementName, namespacePrefix, hasNamespace)) {
- selector = consumeSimpleSelector(range);
- if (!selector)
+ compoundSelector = consumeSimpleSelector(range);
+ if (!compoundSelector)
return nullptr;
}
if (m_context.isHTMLDocument())
elementName = elementName.lower();
- while (OwnPtr<CSSParserSelector> nextSelector = consumeSimpleSelector(range)) {
- if (selector)
- selector = rewriteSpecifiers(selector.release(), nextSelector.release());
+ while (OwnPtr<CSSParserSelector> simpleSelector = consumeSimpleSelector(range)) {
+ if (compoundSelector)
+ compoundSelector = addSimpleSelectorToCompound(compoundSelector.release(), simpleSelector.release());
else
- selector = nextSelector.release();
+ compoundSelector = simpleSelector.release();
}
- if (!selector) {
+ if (!compoundSelector) {
if (hasNamespace)
return CSSParserSelector::create(determineNameInNamespace(namespacePrefix, elementName));
return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_defaultNamespace));
}
- if (elementName.isNull())
- rewriteSpecifiersWithNamespaceIfNeeded(selector.get());
- else
- rewriteSpecifiersWithElementName(namespacePrefix, elementName, selector.get());
- return selector.release();
+ prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.get());
+ return compoundSelector.release();
}
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParserTokenRange& range)
@@ -521,32 +518,29 @@ QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& pr
return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix));
}
-void CSSSelectorParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector* specifiers)
+void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
{
- if (m_defaultNamespace != starAtom || specifiers->crossesTreeScopes())
- rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true);
-}
+ if (elementName.isNull() && m_defaultNamespace == starAtom && !compoundSelector->crossesTreeScopes())
+ return;
-void CSSSelectorParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers, bool tagIsForNamespaceRule)
-{
+ AtomicString determinedElementName = elementName.isNull() ? starAtom : elementName;
AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace;
- QualifiedName tag(namespacePrefix, elementName, determinedNamespace);
+ QualifiedName tag(namespacePrefix, determinedElementName, determinedNamespace);
- if (specifiers->crossesTreeScopes())
- return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, specifiers, tagIsForNamespaceRule);
+ if (compoundSelector->crossesTreeScopes())
+ return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, compoundSelector, elementName.isNull());
- if (specifiers->isContentPseudoElement())
- return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, specifiers, tagIsForNamespaceRule);
+ if (compoundSelector->isContentPseudoElement())
+ return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, compoundSelector, elementName.isNull());
// *: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())
+ if (tag == anyQName() && !compoundSelector->hasHostPseudoSelector())
return;
- if (specifiers->pseudoType() != CSSSelector::PseudoCue)
- specifiers->prependTagSelector(tag, tagIsForNamespaceRule);
+ compoundSelector->prependTagSelector(tag, elementName.isNull());
}
-void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsForNamespaceRule)
+void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit)
{
CSSParserSelector* lastShadowPseudo = specifiers;
CSSParserSelector* history = specifiers;
@@ -558,18 +552,18 @@ void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c
if (lastShadowPseudo->tagHistory()) {
if (tag != anyQName())
- lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule);
+ lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsImplicit);
return;
}
// For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo combinator has to be used.
// We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*').
- OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelector(tag));
+ OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(tag);
lastShadowPseudo->setTagHistory(elementNameSelector.release());
lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo);
}
-void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsForNamespaceRule)
+void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit)
{
CSSParserSelector* last = specifiers;
CSSParserSelector* history = specifiers;
@@ -581,38 +575,64 @@ void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(
if (last->tagHistory()) {
if (tag != anyQName())
- last->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule);
+ last->tagHistory()->prependTagSelector(tag, tagIsImplicit);
return;
}
// For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo combinator has to be used.
// We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*').
- OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelector(tag));
+ OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(tag);
last->setTagHistory(elementNameSelector.release());
}
-PassOwnPtr<CSSParserSelector> CSSSelectorParser::rewriteSpecifiers(PassOwnPtr<CSSParserSelector> specifiers, PassOwnPtr<CSSParserSelector> newSpecifier)
+PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(PassOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpleSelector)
{
- if (newSpecifier->crossesTreeScopes()) {
- // Unknown pseudo element always goes at the top of selector chain.
- newSpecifier->appendTagHistory(CSSSelector::ShadowPseudo, specifiers);
- return newSpecifier;
- }
- if (newSpecifier->isContentPseudoElement()) {
- newSpecifier->appendTagHistory(CSSSelector::SubSelector, specifiers);
- return newSpecifier;
- }
- if (specifiers->crossesTreeScopes()) {
- // Specifiers for unknown pseudo element go right behind it in the chain.
- specifiers->insertTagHistory(CSSSelector::SubSelector, newSpecifier, CSSSelector::ShadowPseudo);
- return specifiers;
- }
- if (specifiers->isContentPseudoElement()) {
- specifiers->insertTagHistory(CSSSelector::SubSelector, newSpecifier, CSSSelector::SubSelector);
- return specifiers;
- }
- specifiers->appendTagHistory(CSSSelector::SubSelector, newSpecifier);
- return specifiers;
+ // The tagHistory is a linked list that stores combinator separated compound selectors
+ // from right-to-left. Yet, within a single compound selector, stores the simple selectors
+ // from left-to-right.
+ //
+ // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each element in the
+ // list stored with an associated relation (combinator or SubSelector).
+ //
+ // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo combinator
+ // to their left, which really makes for a new compound selector, yet it's consumed by
+ // the selector parser as a single compound selector.
+ //
+ // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input, #x ]
+ //
+ // ::content is kept at the end of the compound in order easily know when to call
+ // setRelationIsAffectedByPseudoContent.
+ //
+ // We are currently not dropping selectors containing multiple instances of ::content,
+ // ::shadow, ::cue, and custom pseudo elements in arbitrary order. There are known
+ // issues like crbug.com/478563
+ //
+ // TODO(rune@opera.com): We should try to remove the need for the re-ordering tricks
+ // below and in the remaining rewrite* methods by using a more suitable storage
+ // structure in CSSSelectorParser.
+ //
+ // The code below is to keep ::content at the end of the compound, and to keep the
+ // tagHistory order correct for implicit ShadowPseudo and juggling multiple (two?)
+ // compounds.
+
+ CSSSelector::Relation relation = CSSSelector::SubSelector;
+
+ if (simpleSelector->crossesTreeScopes() || simpleSelector->isContentPseudoElement()) {
+ if (simpleSelector->crossesTreeScopes())
+ relation = CSSSelector::ShadowPseudo;
+ simpleSelector->appendTagHistory(relation, compoundSelector);
+ return simpleSelector;
+ }
+ if (compoundSelector->crossesTreeScopes() || compoundSelector->isContentPseudoElement()) {
+ if (compoundSelector->crossesTreeScopes())
+ relation = CSSSelector::ShadowPseudo;
+ compoundSelector->insertTagHistory(CSSSelector::SubSelector, simpleSelector, relation);
+ return compoundSelector;
+ }
+
+ // All other simple selectors are added to the end of the compound.
+ compoundSelector->appendTagHistory(CSSSelector::SubSelector, simpleSelector);
+ return compoundSelector;
}
void CSSSelectorParser::recordSelectorStats(const CSSParserContext& context, const CSSSelectorList& selectorList)
« no previous file with comments | « Source/core/css/parser/CSSSelectorParser.h ('k') | Source/core/css/parser/CSSSelectorParserTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698