void CSSVariableResolver::resolveApplyAtRule(CSSParserTokenRange& range, Vector<CSSParserToken>& result) { ASSERT(range.peek().type() == AtKeywordToken && range.peek().valueEqualsIgnoringASCIICase("apply")); range.consumeIncludingWhitespace(); const CSSParserToken& variableName = range.consumeIncludingWhitespace(); // TODO(timloh): Should we actually be consuming this? if (range.peek().type() == SemicolonToken) range.consume(); CSSVariableData* variableData = valueForCustomProperty(variableName.value()); if (!variableData) return; // Invalid custom property CSSParserTokenRange rule = variableData->tokenRange(); rule.consumeWhitespace(); if (rule.peek().type() != LeftBraceToken) return; CSSParserTokenRange ruleContents = rule.consumeBlock(); rule.consumeWhitespace(); if (!rule.atEnd()) return; result.appendRange(ruleContents.begin(), ruleContents.end()); }
bool CSSVariableData::checkVariablesForCyclesWithRange(CSSParserTokenRange range, CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const { while (!range.atEnd()) { if (range.peek().functionId() == CSSValueVar) { CSSParserTokenRange block = range.consumeBlock(); block.consumeWhitespace(); ASSERT(block.peek().type() == IdentToken); AtomicString variableName = block.consumeIncludingWhitespace().value().toAtomicString(); ASSERT(block.atEnd() || block.peek().type() == CommaToken); if (seenProperties.contains(variableName)) return false; RefPtr<CSSCustomPropertyValue> value = customProperties.get(variableName); if (value && value->containsVariables() && !value->checkVariablesForCycles(variableName, customProperties, seenProperties, invalidProperties)) return false; if (range.peek().type() == CommaToken) { // Fallback. range.consume(); return checkVariablesForCyclesWithRange(block, customProperties, seenProperties, invalidProperties); } } else range.consume(); } return true; }
CSSParserTokenRange consumeFunction(CSSParserTokenRange& range) { ASSERT(range.peek().type() == FunctionToken); CSSParserTokenRange contents = range.consumeBlock(); range.consumeWhitespace(); contents.consumeWhitespace(); return contents; }
bool CSSVariableData::resolveTokenRange(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const { bool success = true; while (!range.atEnd()) { if (range.peek().functionId() == CSSValueVar) success &= resolveVariableReference(customProperties, range.consumeBlock(), result); else result.append(range.consume()); } return success; }
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules) { ASSERT(range.peek().type() == AtKeywordToken); const CSSParserString& name = range.consume().value(); const CSSParserToken* preludeStart = &range.peek(); while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken) range.consumeComponentValue(); CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek()); CSSAtRuleID id = cssAtRuleID(name); if (id != CSSAtRuleInvalid && m_context.useCounter()) countAtRule(m_context.useCounter(), id); if (range.atEnd() || range.peek().type() == SemicolonToken) { range.consume(); if (allowedRules == AllowCharsetRules && id == CSSAtRuleCharset) return consumeCharsetRule(prelude); if (allowedRules <= AllowImportRules && id == CSSAtRuleImport) return consumeImportRule(prelude); if (allowedRules <= AllowNamespaceRules && id == CSSAtRuleNamespace) return consumeNamespaceRule(prelude); return nullptr; // Parse error, unrecognised at-rule without block } CSSParserTokenRange block = range.consumeBlock(); if (allowedRules == KeyframeRules) return nullptr; // Parse error, no at-rules supported inside @keyframes if (allowedRules == NoRules) return nullptr; // Parse error, no at-rules supported inside declaration lists ASSERT(allowedRules <= RegularRules); switch (id) { case CSSAtRuleMedia: return consumeMediaRule(prelude, block); case CSSAtRuleSupports: return consumeSupportsRule(prelude, block); case CSSAtRuleViewport: return consumeViewportRule(prelude, block); case CSSAtRuleFontFace: return consumeFontFaceRule(prelude, block); case CSSAtRuleWebkitKeyframes: return consumeKeyframesRule(true, prelude, block); case CSSAtRuleKeyframes: return consumeKeyframesRule(false, prelude, block); case CSSAtRulePage: return consumePageRule(prelude, block); default: return nullptr; // Parse error, unrecognised at-rule with block } }
bool parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result) { if (checkDepthAndIndex(&depth, tokens) != OK) return false; if (tokens.peek().type() == LeftParenthesisToken) { CSSParserTokenRange innerRange = tokens.consumeBlock(); tokens.consumeWhitespace(); innerRange.consumeWhitespace(); return parseValueExpression(innerRange, depth, result); } return parseValue(tokens, result); }
bool CSSVariableResolver::resolveTokenRange(CSSParserTokenRange range, Vector<CSSParserToken>& result) { bool success = true; while (!range.atEnd()) { if (range.peek().functionId() == CSSValueVar) { success &= resolveVariableReference(range.consumeBlock(), result); } else if (range.peek().type() == AtKeywordToken && range.peek().valueEqualsIgnoringASCIICase("apply") && RuntimeEnabledFeatures::cssApplyAtRulesEnabled()) { resolveApplyAtRule(range, result); } else { result.append(range.consume()); } } return success; }
// This may still consume tokens if it fails static AtomicString consumeStringOrURI(CSSParserTokenRange& range) { const CSSParserToken& token = range.peek(); if (token.type() == StringToken || token.type() == UrlToken) return range.consumeIncludingWhitespace().value(); if (token.type() != FunctionToken || !token.valueEqualsIgnoringCase("url")) return AtomicString(); CSSParserTokenRange contents = range.consumeBlock(); const CSSParserToken& uri = contents.consumeIncludingWhitespace(); ASSERT(uri.type() == StringToken); if (!contents.atEnd()) return AtomicString(); return uri.value(); }
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules) { ASSERT(range.peek().type() == AtKeywordToken); const CSSParserString& name = range.consume().value(); const CSSParserToken* preludeStart = &range.peek(); while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken) range.consumeComponentValue(); CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek()); if (range.atEnd() || range.peek().type() == SemicolonToken) { range.consume(); if (allowedRules == AllowCharsetRules && name.equalIgnoringCase("charset")) return consumeCharsetRule(prelude); if (allowedRules <= AllowImportRules && name.equalIgnoringCase("import")) return consumeImportRule(prelude); if (allowedRules <= AllowNamespaceRules && name.equalIgnoringCase("namespace")) return consumeNamespaceRule(prelude); return nullptr; // Parse error, unrecognised at-rule without block } CSSParserTokenRange block = range.consumeBlock(); if (allowedRules == KeyframeRules) return nullptr; // Parse error, no at-rules supported inside @keyframes if (allowedRules == NoRules) return nullptr; // Parse error, no at-rules supported inside declaration lists ASSERT(allowedRules <= RegularRules); if (name.equalIgnoringCase("media")) return consumeMediaRule(prelude, block); if (name.equalIgnoringCase("supports")) return consumeSupportsRule(prelude, block); if (name.equalIgnoringCase("viewport")) return consumeViewportRule(prelude, block); if (name.equalIgnoringCase("font-face")) return consumeFontFaceRule(prelude, block); if (name.equalIgnoringCase("-webkit-keyframes")) return consumeKeyframesRule(true, prelude, block); if (name.equalIgnoringCase("keyframes")) return consumeKeyframesRule(false, prelude, block); if (name.equalIgnoringCase("page")) return consumePageRule(prelude, block); return nullptr; // Parse error, unrecognised at-rule with block }
std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeAttribute( CSSParserTokenRange& range) { ASSERT(range.peek().type() == LeftBracketToken); CSSParserTokenRange block = range.consumeBlock(); block.consumeWhitespace(); AtomicString namespacePrefix; AtomicString attributeName; if (!consumeName(block, attributeName, namespacePrefix)) return nullptr; block.consumeWhitespace(); if (m_context.isHTMLDocument()) attributeName = attributeName.lower(); AtomicString namespaceURI = determineNamespace(namespacePrefix); if (namespaceURI.isNull()) return nullptr; QualifiedName qualifiedName = namespacePrefix.isNull() ? QualifiedName(nullAtom, attributeName, nullAtom) : QualifiedName(namespacePrefix, attributeName, namespaceURI); std::unique_ptr<CSSParserSelector> selector = CSSParserSelector::create(); if (block.atEnd()) { selector->setAttribute(qualifiedName, CSSSelector::CaseSensitive); selector->setMatch(CSSSelector::AttributeSet); return selector; } selector->setMatch(consumeAttributeMatch(block)); const CSSParserToken& attributeValue = block.consumeIncludingWhitespace(); if (attributeValue.type() != IdentToken && attributeValue.type() != StringToken) return nullptr; selector->setValue(attributeValue.value().toAtomicString()); selector->setAttribute(qualifiedName, consumeAttributeFlags(block)); if (!block.atEnd()) return nullptr; return selector; }
CSSSupportsParser::SupportsResult CSSSupportsParser::consumeConditionInParenthesis(CSSParserTokenRange& range) { if (range.peek().type() == FunctionToken) { range.consumeComponentValue(); return Unsupported; } if (range.peek().type() != LeftParenthesisToken) return Invalid; CSSParserTokenRange innerRange = range.consumeBlock(); innerRange.consumeWhitespace(); SupportsResult result = consumeCondition(innerRange); if (result != Invalid) return result; return innerRange.peek().type() == IdentToken && m_parser.supportsDeclaration(innerRange) ? Supported : Unsupported; }
bool classifyBlock(CSSParserTokenRange range, bool& hasReferences, bool isTopLevelBlock = true) { while (!range.atEnd()) { if (range.peek().blockType() == CSSParserToken::BlockStart) { const CSSParserToken& token = range.peek(); CSSParserTokenRange block = range.consumeBlock(); if (token.functionId() == CSSValueVar) { if (!isValidVariableReference(block)) return false; // Bail if any references are invalid hasReferences = true; continue; } if (!classifyBlock(block, hasReferences, false)) return false; continue; } ASSERT(range.peek().blockType() != CSSParserToken::BlockEnd); const CSSParserToken& token = range.consume(); switch (token.type()) { case DelimiterToken: { if (token.delimiter() == '!' && isTopLevelBlock) return false; break; } case RightParenthesisToken: case RightBraceToken: case RightBracketToken: case BadStringToken: case BadUrlToken: return false; case SemicolonToken: if (isTopLevelBlock) return false; break; default: break; } } return true; }
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeQualifiedRule(CSSParserTokenRange& range, AllowedRulesType allowedRules) { const CSSParserToken* preludeStart = &range.peek(); while (!range.atEnd() && range.peek().type() != LeftBraceToken) range.consumeComponentValue(); if (range.atEnd()) return nullptr; // Parse error, EOF instead of qualified rule block CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek()); CSSParserTokenRange block = range.consumeBlock(); if (allowedRules <= RegularRules) return consumeStyleRule(prelude, block); if (allowedRules == KeyframeRules) return consumeKeyframeStyleRule(prelude, block); ASSERT_NOT_REACHED(); return nullptr; }
String consumeUrl(CSSParserTokenRange& range) { const CSSParserToken& token = range.peek(); if (token.type() == UrlToken) { range.consumeIncludingWhitespace(); return token.value(); } if (token.functionId() == CSSValueUrl) { CSSParserTokenRange urlRange = range; CSSParserTokenRange urlArgs = urlRange.consumeBlock(); const CSSParserToken& next = urlArgs.consumeIncludingWhitespace(); if (next.type() == BadStringToken || !urlArgs.atEnd()) return String(); ASSERT(next.type() == StringToken); range = urlRange; range.consumeWhitespace(); return next.value(); } return String(); }
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; }
CSSParserValueList::CSSParserValueList(CSSParserTokenRange range) : m_current(0) { Vector<CSSParserValueList*> stack; Vector<int> bracketCounts; stack.append(this); bracketCounts.append(0); while (!range.atEnd()) { ASSERT(stack.size() == bracketCounts.size()); ASSERT(!stack.isEmpty()); const CSSParserToken& token = range.peek(); if (token.type() != FunctionToken) range.consume(); CSSParserValue value; switch (token.type()) { case FunctionToken: { if (token.valueEqualsIgnoringCase("url")) { range.consume(); const CSSParserToken& next = range.consumeIncludingWhitespace(); if (next.type() == BadStringToken || range.consume().type() != RightParenthesisToken) { destroyAndClear(); return; } ASSERT(next.type() == StringToken); value.id = CSSValueInvalid; value.isInt = false; value.m_unit = CSSParserValue::URI; value.string = next.value(); break; } else if (token.valueEqualsIgnoringCase("var")) { destroyAndClear(); return; } value.id = CSSValueInvalid; value.isInt = false; CSSValueID id = token.functionId(); if (id == CSSValueCalc || id == CSSValueWebkitCalc) { value.m_unit = CSSParserValue::CalcFunction; value.calcFunction = new CSSParserCalcFunction(range.consumeBlock()); break; } range.consume(); value.m_unit = CSSParserValue::Function; CSSParserFunction* function = new CSSParserFunction; function->id = id; CSSParserValueList* list = new CSSParserValueList; function->args = adoptPtr(list); value.function = function; stack.last()->addValue(value); stack.append(list); bracketCounts.append(0); continue; } case LeftParenthesisToken: { CSSParserValueList* list = new CSSParserValueList; value.setFromValueList(adoptPtr(list)); stack.last()->addValue(value); stack.append(list); bracketCounts.append(0); continue; } case RightParenthesisToken: { if (bracketCounts.last() == 0) { stack.removeLast(); bracketCounts.removeLast(); if (bracketCounts.isEmpty()) { destroyAndClear(); return; } continue; } bracketCounts.last()--; value.setFromOperator(')'); break; } case IdentToken: { value.id = token.id(); value.isInt = false; value.m_unit = CSSParserValue::Identifier; value.string = token.value(); break; } case DimensionToken: if (token.unitType() == CSSPrimitiveValue::UnitType::Unknown) { // Unknown dimensions are handled as a list of two values value.m_unit = CSSParserValue::DimensionList; CSSParserValueList* list = new CSSParserValueList; value.valueList = list; value.id = CSSValueInvalid; CSSParserValue number; number.setFromNumber(token.numericValue(), CSSPrimitiveValue::UnitType::Number); number.isInt = (token.numericValueType() == IntegerValueType); list->addValue(number); CSSParserValue unit; unit.string = token.value(); unit.m_unit = CSSParserValue::Identifier; list->addValue(unit); break; } // fallthrough case NumberToken: case PercentageToken: value.setFromNumber(token.numericValue(), token.unitType()); value.isInt = (token.numericValueType() == IntegerValueType); break; case UnicodeRangeToken: { value.id = CSSValueInvalid; value.isInt = false; value.m_unicodeRange.start = token.unicodeRangeStart(); value.m_unicodeRange.end = token.unicodeRangeEnd(); value.m_unit = CSSParserValue::UnicodeRange; break; } case HashToken: case StringToken: case UrlToken: { value.id = CSSValueInvalid; value.isInt = false; if (token.type() == HashToken) value.m_unit = CSSParserValue::HexColor; else if (token.type() == StringToken) value.m_unit = CSSParserValue::String; else value.m_unit = CSSParserValue::URI; value.string = token.value(); break; } case DelimiterToken: value.setFromOperator(token.delimiter()); break; case CommaToken: value.setFromOperator(','); break; case LeftBracketToken: value.setFromOperator('['); break; case RightBracketToken: value.setFromOperator(']'); break; case LeftBraceToken: value.setFromOperator('{'); break; case RightBraceToken: value.setFromOperator('}'); break; case WhitespaceToken: continue; case CommentToken: case EOFToken: ASSERT_NOT_REACHED(); case CDOToken: case CDCToken: case AtKeywordToken: case IncludeMatchToken: case DashMatchToken: case PrefixMatchToken: case SuffixMatchToken: case SubstringMatchToken: case ColumnToken: case ColonToken: case SemicolonToken: destroyAndClear(); return; case BadStringToken: case BadUrlToken: destroyAndClear(); return; } stack.last()->addValue(value); } CSSParserValue rightParenthesis; rightParenthesis.setFromOperator(')'); while (!stack.isEmpty()) { while (bracketCounts.last() > 0) { bracketCounts.last()--; stack.last()->addValue(rightParenthesis); } stack.removeLast(); bracketCounts.removeLast(); } }
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; }