std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumePseudo( CSSParserTokenRange& range) { ASSERT(range.peek().type() == ColonToken); range.consume(); int colons = 1; if (range.peek().type() == ColonToken) { range.consume(); colons++; } const CSSParserToken& token = range.peek(); if (token.type() != IdentToken && token.type() != FunctionToken) return nullptr; std::unique_ptr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(colons == 1 ? CSSSelector::PseudoClass : CSSSelector::PseudoElement); AtomicString value = token.value().toAtomicString().lowerASCII(); bool hasArguments = token.type() == FunctionToken; selector->updatePseudoType(value, hasArguments); if (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements) return nullptr; if (token.type() == IdentToken) { range.consume(); if (selector->pseudoType() == CSSSelector::PseudoUnknown) return nullptr; return selector; } CSSParserTokenRange block = range.consumeBlock(); block.consumeWhitespace(); if (selector->pseudoType() == CSSSelector::PseudoUnknown) return nullptr; switch (selector->pseudoType()) { case CSSSelector::PseudoHost: case CSSSelector::PseudoHostContext: case CSSSelector::PseudoAny: case CSSSelector::PseudoCue: { DisallowPseudoElementsScope scope(this); std::unique_ptr<CSSSelectorList> selectorList = wrapUnique(new CSSSelectorList()); *selectorList = consumeCompoundSelectorList(block); if (!selectorList->isValid() || !block.atEnd()) return nullptr; selector->setSelectorList(std::move(selectorList)); return selector; } case CSSSelector::PseudoNot: { std::unique_ptr<CSSParserSelector> innerSelector = consumeCompoundSelector(block); block.consumeWhitespace(); if (!innerSelector || !innerSelector->isSimple() || !block.atEnd()) return nullptr; Vector<std::unique_ptr<CSSParserSelector>> selectorVector; selectorVector.append(std::move(innerSelector)); selector->adoptSelectorVector(selectorVector); return selector; } case CSSSelector::PseudoSlotted: { DisallowPseudoElementsScope scope(this); std::unique_ptr<CSSParserSelector> innerSelector = consumeCompoundSelector(block); block.consumeWhitespace(); if (!innerSelector || !block.atEnd() || !RuntimeEnabledFeatures::shadowDOMV1Enabled()) return nullptr; Vector<std::unique_ptr<CSSParserSelector>> selectorVector; selectorVector.append(std::move(innerSelector)); selector->adoptSelectorVector(selectorVector); return selector; } case CSSSelector::PseudoLang: { // FIXME: CSS Selectors Level 4 allows :lang(*-foo) const CSSParserToken& ident = block.consumeIncludingWhitespace(); if (ident.type() != IdentToken || !block.atEnd()) return nullptr; selector->setArgument(ident.value().toAtomicString()); return selector; } case CSSSelector::PseudoNthChild: case CSSSelector::PseudoNthLastChild: case CSSSelector::PseudoNthOfType: case CSSSelector::PseudoNthLastOfType: { std::pair<int, int> ab; if (!consumeANPlusB(block, ab)) return nullptr; block.consumeWhitespace(); if (!block.atEnd()) return nullptr; selector->setNth(ab.first, ab.second); return selector; } default: break; } return nullptr; }
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumePseudo(CSSParserTokenRange& range) { ASSERT(range.peek().type() == ColonToken); range.consume(); int colons = 1; if (range.peek().type() == ColonToken) { range.consume(); colons++; } const CSSParserToken& token = range.peek(); if (token.type() != IdentToken && token.type() != FunctionToken) return nullptr; OwnPtr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(colons == 1 ? CSSSelector::PseudoClass : CSSSelector::PseudoElement); selector->setValue(AtomicString(String(token.value()).lower())); if (token.type() == IdentToken) { range.consume(); if (selector->pseudoType() == CSSSelector::PseudoUnknown) return nullptr; return selector.release(); } CSSParserTokenRange block = range.consumeBlock(); block.consumeWhitespace(); if ((colons == 1 && (token.valueEqualsIgnoringCase("host") || token.valueEqualsIgnoringCase("host-context") || token.valueEqualsIgnoringCase("-webkit-any"))) || (colons == 2 && token.valueEqualsIgnoringCase("cue"))) { CSSSelectorList* selectorList = new CSSSelectorList(); consumeCompoundSelectorList(block, *selectorList); if (!selectorList->isValid() || !block.atEnd()) return nullptr; selector->setSelectorList(adoptPtr(selectorList)); selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached? ASSERT(selector->pseudoType() != CSSSelector::PseudoUnknown); return selector.release(); } if (colons == 1 && token.valueEqualsIgnoringCase("not")) { OwnPtr<CSSParserSelector> innerSelector = consumeCompoundSelector(block); if (!innerSelector || !innerSelector->isSimple() || !block.atEnd()) return nullptr; Vector<OwnPtr<CSSParserSelector>> selectorVector; selectorVector.append(innerSelector.release()); selector->adoptSelectorVector(selectorVector); return selector.release(); } if (colons == 1 && token.valueEqualsIgnoringCase("lang")) { // FIXME: CSS Selectors Level 4 allows :lang(*-foo) const CSSParserToken& ident = block.consumeIncludingWhitespace(); if (ident.type() != IdentToken || !block.atEnd()) return nullptr; selector->setArgument(ident.value()); selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached? ASSERT(selector->pseudoType() == CSSSelector::PseudoLang); return selector.release(); } if (colons == 1 && (token.valueEqualsIgnoringCase("nth-child") || token.valueEqualsIgnoringCase("nth-last-child") || token.valueEqualsIgnoringCase("nth-of-type") || token.valueEqualsIgnoringCase("nth-last-of-type"))) { std::pair<int, int> ab; if (!consumeANPlusB(block, ab)) return nullptr; block.consumeWhitespace(); if (!block.atEnd()) return nullptr; // FIXME: We shouldn't serialize here and reparse in CSSSelector! // Serialization should be in CSSSelector::selectorText instead. int a = ab.first; int b = ab.second; String string; if (a == 0 && b == 0) string = "0"; else if (a == 0) string = String::number(b); else if (b == 0) string = String::format("%dn", a); else if (ab.second < 0) string = String::format("%dn%d", a, b); else string = String::format("%dn+%d", a, b); selector->setArgument(AtomicString(string)); selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached? ASSERT(selector->pseudoType() != CSSSelector::PseudoUnknown); return selector.release(); } return nullptr; }