bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range, std::pair<int, int>& result) { const CSSParserToken& token = range.consume(); if (token.type() == NumberToken && token.numericValueType() == IntegerValueType) { result = std::make_pair(0, static_cast<int>(token.numericValue())); return true; } if (token.type() == IdentToken) { if (token.valueEqualsIgnoringCase("odd")) { result = std::make_pair(2, 1); return true; } if (token.valueEqualsIgnoringCase("even")) { result = std::make_pair(2, 0); return true; } } // The 'n' will end up as part of an ident or dimension. For a valid <an+b>, // this will store a string of the form 'n', 'n-', or 'n-123'. String nString; if (token.type() == DelimiterToken && token.delimiter() == '+' && range.peek().type() == IdentToken) { result.first = 1; nString = range.consume().value(); } else if (token.type() == DimensionToken && token.numericValueType() == IntegerValueType) { result.first = token.numericValue(); nString = token.value(); } else if (token.type() == IdentToken) { if (token.value()[0] == '-') { result.first = -1; nString = String(token.value()).substring(1); } else { result.first = 1; nString = token.value(); } } range.consumeWhitespace(); if (nString.isEmpty() || !isASCIIAlphaCaselessEqual(nString[0], 'n')) return false; if (nString.length() > 1 && nString[1] != '-') return false; if (nString.length() > 2) { bool valid; result.second = nString.substring(1).toIntStrict(&valid); return valid; } NumericSign sign = nString.length() == 1 ? NoSign : MinusSign; if (sign == NoSign && range.peek().type() == DelimiterToken) { char delimiterSign = range.consumeIncludingWhitespace().delimiter(); if (delimiterSign == '+') sign = PlusSign; else if (delimiterSign == '-') sign = MinusSign; else return false; } if (sign == NoSign && range.peek().type() != NumberToken) { result.second = 0; return true; } const CSSParserToken& b = range.consume(); if (b.type() != NumberToken || b.numericValueType() != IntegerValueType) return false; if ((b.numericSign() == NoSign) == (sign == NoSign)) return false; result.second = b.numericValue(); if (sign == MinusSign) result.second = -result.second; return true; }
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; }