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()); }
CSSSelector::RelationType CSSSelectorParser::consumeCombinator( CSSParserTokenRange& range) { CSSSelector::RelationType fallbackResult = CSSSelector::SubSelector; while (range.peek().type() == WhitespaceToken) { range.consume(); fallbackResult = CSSSelector::Descendant; } if (range.peek().type() != DelimiterToken) return fallbackResult; UChar delimiter = range.peek().delimiter(); if (delimiter == '+' || delimiter == '~' || delimiter == '>') { range.consumeIncludingWhitespace(); if (delimiter == '+') return CSSSelector::DirectAdjacent; if (delimiter == '~') return CSSSelector::IndirectAdjacent; return CSSSelector::Child; } // Match /deep/ if (delimiter != '/') return fallbackResult; range.consume(); const CSSParserToken& ident = range.consume(); if (ident.type() != IdentToken || !equalIgnoringASCIICase(ident.value(), "deep")) m_failedParsing = true; const CSSParserToken& slash = range.consumeIncludingWhitespace(); if (slash.type() != DelimiterToken || slash.delimiter() != '/') m_failedParsing = true; return CSSSelector::ShadowDeep; }
PassRefPtrWillBeRawPtr<StyleRuleKeyframes> CSSParserImpl::consumeKeyframesRule(bool webkitPrefixed, CSSParserTokenRange prelude, CSSParserTokenRange block) { prelude.consumeWhitespace(); const CSSParserToken& nameToken = prelude.consumeIncludingWhitespace(); if (!prelude.atEnd()) return nullptr; // Parse error; expected single non-whitespace token in @keyframes header String name; if (nameToken.type() == IdentToken) { name = nameToken.value(); } else if (nameToken.type() == StringToken && webkitPrefixed) { if (m_context.useCounter()) m_context.useCounter()->count(UseCounter::QuotedKeyframesRule); name = nameToken.value(); } else { return nullptr; // Parse error; expected ident token in @keyframes header } if (m_observerWrapper) { unsigned endOffset = m_observerWrapper->endOffset(prelude); m_observerWrapper->observer().startRuleHeader(StyleRule::Keyframes, m_observerWrapper->startOffset(prelude)); m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude)); m_observerWrapper->observer().startRuleBody(endOffset); m_observerWrapper->observer().endRuleBody(endOffset); } RefPtrWillBeRawPtr<StyleRuleKeyframes> keyframeRule = StyleRuleKeyframes::create(); consumeRuleList(block, KeyframesRuleList, [keyframeRule](PassRefPtrWillBeRawPtr<StyleRuleBase> keyframe) { keyframeRule->parserAppendKeyframe(toStyleRuleKeyframe(keyframe.get())); }); keyframeRule->setName(name); keyframeRule->setVendorPrefixed(webkitPrefixed); return keyframeRule.release(); }
bool parseValueMultiplicativeExpression(CSSParserTokenRange& tokens, int depth, Value* result) { if (checkDepthAndIndex(&depth, tokens) != OK) return false; if (!parseValueTerm(tokens, depth, result)) return false; while (!tokens.atEnd()) { char operatorCharacter = operatorValue(tokens.peek()); if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide) break; tokens.consumeIncludingWhitespace(); Value rhs; if (!parseValueTerm(tokens, depth, &rhs)) return false; result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter)); if (!result->value) return false; } return true; }
bool parseAdditiveValueExpression(CSSParserTokenRange& tokens, int depth, Value* result) { if (checkDepthAndIndex(&depth, tokens) != OK) return false; if (!parseValueMultiplicativeExpression(tokens, depth, result)) return false; while (!tokens.atEnd()) { char operatorCharacter = operatorValue(tokens.peek()); if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract) break; if ((&tokens.peek() - 1)->type() != WhitespaceToken) return false; // calc(1px+ 2px) is invalid tokens.consume(); if (tokens.peek().type() != WhitespaceToken) return false; // calc(1px +2px) is invalid tokens.consumeIncludingWhitespace(); Value rhs; if (!parseValueMultiplicativeExpression(tokens, depth, &rhs)) return false; result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter)); if (!result->value) return false; } return true; }
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; }
static bool parseHexColor(CSSParserTokenRange& range, RGBA32& result, bool acceptQuirkyColors) { const CSSParserToken& token = range.peek(); String color; if (acceptQuirkyColors) { if (token.type() == NumberToken || token.type() == DimensionToken) { if (token.numericValueType() != IntegerValueType || token.numericValue() < 0. || token.numericValue() >= 1000000.) return false; if (token.type() == NumberToken) // e.g. 112233 color = String::format("%d", static_cast<int>(token.numericValue())); else // e.g. 0001FF color = String::number(static_cast<int>(token.numericValue())) + String(token.value()); while (color.length() < 6) color = "0" + color; } else if (token.type() == IdentToken) { // e.g. FF0000 unsigned length = token.value().length(); if (length != 3 && length != 6) return false; color = token.value(); } } if (token.type() == HashToken) color = token.value(); if (!Color::parseHexColor(color, result)) return false; range.consumeIncludingWhitespace(); return true; }
PassRefPtrWillBeRawPtr<StyleRuleCharset> CSSParserImpl::consumeCharsetRule(CSSParserTokenRange prelude) { prelude.consumeWhitespace(); const CSSParserToken& string = prelude.consumeIncludingWhitespace(); if (string.type() != StringToken || !prelude.atEnd()) return nullptr; // Parse error, expected a single string return StyleRuleCharset::create(); }
bool consumeCommaIncludingWhitespace(CSSParserTokenRange& range) { CSSParserToken value = range.peek(); if (value.type() != CommaToken) return false; range.consumeIncludingWhitespace(); return true; }
bool consumeSlashIncludingWhitespace(CSSParserTokenRange& range) { CSSParserToken value = range.peek(); if (value.type() != DelimiterToken || value.delimiter() != '/') return false; range.consumeIncludingWhitespace(); return true; }
// 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(); }
bool consumeNumberRaw(CSSParserTokenRange& range, double& result) { if (range.peek().type() == NumberToken) { result = range.consumeIncludingWhitespace().numericValue(); return true; } CalcParser calcParser(range, ValueRangeAll); return calcParser.consumeNumberRaw(result); }
CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags( CSSParserTokenRange& range) { if (range.peek().type() != IdentToken) return CSSSelector::CaseSensitive; const CSSParserToken& flag = range.consumeIncludingWhitespace(); if (equalIgnoringASCIICase(flag.value(), "i")) return CSSSelector::CaseInsensitive; m_failedParsing = true; return CSSSelector::CaseSensitive; }
CSSSupportsParser::SupportsResult CSSSupportsParser::consumeCondition( CSSParserTokenRange range) { if (range.peek().type() == IdentToken) return consumeNegation(range); bool result; ClauseType clauseType = Unresolved; while (true) { SupportsResult nextResult = consumeConditionInParenthesis(range); if (nextResult == Invalid) return Invalid; bool nextSupported = nextResult; if (clauseType == Unresolved) result = nextSupported; else if (clauseType == Conjunction) result &= nextSupported; else result |= nextSupported; if (range.atEnd()) break; if (range.consumeIncludingWhitespace().type() != WhitespaceToken) return Invalid; if (range.atEnd()) break; const CSSParserToken& token = range.consume(); if (token.type() != IdentToken) return Invalid; if (clauseType == Unresolved) clauseType = token.value().length() == 3 ? Conjunction : Disjunction; if ((clauseType == Conjunction && !equalIgnoringASCIICase(token.value(), "and")) || (clauseType == Disjunction && !equalIgnoringASCIICase(token.value(), "or"))) return Invalid; if (range.consumeIncludingWhitespace().type() != WhitespaceToken) return Invalid; } return result ? Supported : Unsupported; }
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(); }
CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags(CSSParserTokenRange& range) { if (range.peek().type() != IdentToken) return CSSSelector::CaseSensitive; const CSSParserToken& flag = range.consumeIncludingWhitespace(); if (String(flag.value()) == "i") { if (RuntimeEnabledFeatures::cssAttributeCaseSensitivityEnabled() || isUASheetBehavior(m_context.mode())) return CSSSelector::CaseInsensitive; } m_failedParsing = true; return CSSSelector::CaseSensitive; }
CSSSupportsParser::SupportsResult CSSSupportsParser::consumeNegation( CSSParserTokenRange range) { ASSERT(range.peek().type() == IdentToken); if (!equalIgnoringASCIICase(range.consume().value(), "not")) return Invalid; if (range.consumeIncludingWhitespace().type() != WhitespaceToken) return Invalid; SupportsResult result = consumeConditionInParenthesis(range); range.consumeWhitespace(); if (!range.atEnd() || result == Invalid) return Invalid; return result ? Unsupported : Supported; }
PassRefPtrWillBeRawPtr<StyleRuleNamespace> CSSParserImpl::consumeNamespaceRule(CSSParserTokenRange prelude) { prelude.consumeWhitespace(); AtomicString namespacePrefix; if (prelude.peek().type() == IdentToken) namespacePrefix = prelude.consumeIncludingWhitespace().value(); AtomicString uri(consumeStringOrURI(prelude)); prelude.consumeWhitespace(); if (uri.isNull() || !prelude.atEnd()) return nullptr; // Parse error, expected string or URI return StyleRuleNamespace::create(namespacePrefix, uri); }
static CSSValueID classifyVariableRange(CSSParserTokenRange range, bool& hasReferences) { hasReferences = false; range.consumeWhitespace(); if (range.peek().type() == IdentToken) { CSSValueID id = range.consumeIncludingWhitespace().id(); if (range.atEnd() && (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset)) return id; } if (classifyBlock(range, hasReferences)) return CSSValueInternalVariableValue; return CSSValueInvalid; }
bool parseValue(CSSParserTokenRange& tokens, Value* result) { CSSParserToken token = tokens.consumeIncludingWhitespace(); if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken)) return false; CSSPrimitiveValue::UnitType type = token.unitType(); if (unitCategory(type) == CalcOther) return false; result->value = CSSCalcPrimitiveValue::create( CSSPrimitiveValue::create(token.numericValue(), type), token.numericValueType() == IntegerValueType); return true; }
void CSSParserImpl::consumeDeclaration(CSSParserTokenRange range, StyleRule::Type ruleType) { CSSParserTokenRange rangeCopy = range; // For inspector callbacks ASSERT(range.peek().type() == IdentToken); const CSSParserToken& token = range.consumeIncludingWhitespace(); CSSPropertyID unresolvedProperty = token.parseAsUnresolvedCSSPropertyID(); if (range.consume().type() != ColonToken) return; // Parse error bool important = false; const CSSParserToken* declarationValueEnd = range.end(); const CSSParserToken* last = range.end() - 1; while (last->type() == WhitespaceToken) --last; if (last->type() == IdentToken && last->valueEqualsIgnoringCase("important")) { --last; while (last->type() == WhitespaceToken) --last; if (last->type() == DelimiterToken && last->delimiter() == '!') { important = true; declarationValueEnd = last; } } if (RuntimeEnabledFeatures::cssVariablesEnabled() && unresolvedProperty == CSSPropertyInvalid && CSSVariableParser::isValidVariableName(token)) { AtomicString variableName = token.value(); consumeVariableDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), variableName, important); return; } if (important && (ruleType == StyleRule::FontFace || ruleType == StyleRule::Keyframes)) return; if (m_observerWrapper && ruleType == StyleRule::Style) { size_t propertiesCount = m_parsedProperties.size(); if (unresolvedProperty != CSSPropertyInvalid) consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), unresolvedProperty, important, ruleType); m_observerWrapper->observer().observeProperty( m_observerWrapper->startOffset(rangeCopy), m_observerWrapper->endOffset(rangeCopy), important, m_parsedProperties.size() != propertiesCount); return; } if (unresolvedProperty == CSSPropertyInvalid) return; consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), unresolvedProperty, important, ruleType); }
bool CSSVariableResolver::resolveVariableReference(CSSParserTokenRange range, Vector<CSSParserToken>& result) { range.consumeWhitespace(); ASSERT(range.peek().type() == IdentToken); AtomicString variableName = range.consumeIncludingWhitespace().value(); ASSERT(range.atEnd() || (range.peek().type() == CommaToken)); CSSVariableData* variableData = valueForCustomProperty(variableName); if (!variableData) return resolveFallback(range, result); result.appendVector(variableData->tokens()); Vector<CSSParserToken> trash; resolveFallback(range, trash); return true; }
bool isValidVariableReference(CSSParserTokenRange range) { range.consumeWhitespace(); if (!CSSVariableParser::isValidVariableName(range.consumeIncludingWhitespace())) return false; if (range.atEnd()) return true; if (range.consume().type() != CommaToken) return false; if (range.atEnd()) return false; bool hasReferences = false; return classifyBlock(range, hasReferences); }
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; }
void CSSSelectorParser::consumeComplexSelectorList(CSSParserTokenRange& range, CSSSelectorList& output) { Vector<OwnPtr<CSSParserSelector>> selectorList; OwnPtr<CSSParserSelector> selector = consumeComplexSelector(range); if (!selector) return; selectorList.append(selector.release()); while (!range.atEnd() && range.peek().type() == CommaToken) { range.consumeIncludingWhitespace(); selector = consumeComplexSelector(range); if (!selector) return; selectorList.append(selector.release()); } if (!m_failedParsing) output.adoptSelectorVector(selectorList); }
static void observeSelectors(CSSParserObserverWrapper& wrapper, CSSParserTokenRange selectors) { // This is easier than hooking into the CSSSelectorParser selectors.consumeWhitespace(); CSSParserTokenRange originalRange = selectors; wrapper.observer().startRuleHeader(StyleRule::Style, wrapper.startOffset(originalRange)); while (!selectors.atEnd()) { const CSSParserToken* selectorStart = &selectors.peek(); while (!selectors.atEnd() && selectors.peek().type() != CommaToken) selectors.consumeComponentValue(); CSSParserTokenRange selector = selectors.makeSubRange(selectorStart, &selectors.peek()); selectors.consumeIncludingWhitespace(); wrapper.observer().observeSelector(wrapper.startOffset(selector), wrapper.endOffset(selector)); } wrapper.observer().endRuleHeader(wrapper.endOffset(originalRange)); }
CSSSelectorList CSSSelectorParser::consumeComplexSelectorList( CSSParserTokenRange& range) { Vector<std::unique_ptr<CSSParserSelector>> selectorList; std::unique_ptr<CSSParserSelector> selector = consumeComplexSelector(range); if (!selector) return CSSSelectorList(); selectorList.append(std::move(selector)); while (!range.atEnd() && range.peek().type() == CommaToken) { range.consumeIncludingWhitespace(); selector = consumeComplexSelector(range); if (!selector) return CSSSelectorList(); selectorList.append(std::move(selector)); } if (m_failedParsing) return CSSSelectorList(); return CSSSelectorList::adoptSelectorVector(selectorList); }
PassOwnPtr<Vector<double>> CSSParserImpl::consumeKeyframeKeyList(CSSParserTokenRange range) { OwnPtr<Vector<double>> result = adoptPtr(new Vector<double>); while (true) { range.consumeWhitespace(); const CSSParserToken& token = range.consumeIncludingWhitespace(); if (token.type() == PercentageToken && token.numericValue() >= 0 && token.numericValue() <= 100) result->append(token.numericValue() / 100); else if (token.type() == IdentToken && token.valueEqualsIgnoringCase("from")) result->append(0); else if (token.type() == IdentToken && token.valueEqualsIgnoringCase("to")) result->append(1); else return nullptr; // Parser error, invalid value in keyframe selector if (range.atEnd()) return result.release(); if (range.consume().type() != CommaToken) return nullptr; // Parser error } }
void CSSParserImpl::consumeDeclaration(CSSParserTokenRange range, StyleRule::Type ruleType) { CSSParserTokenRange rangeCopy = range; // For inspector callbacks ASSERT(range.peek().type() == IdentToken); CSSPropertyID unresolvedProperty = range.consumeIncludingWhitespace().parseAsUnresolvedCSSPropertyID(); if (range.consume().type() != ColonToken) return; // Parse error // FIXME: We shouldn't allow !important in @keyframes or @font-face bool important = false; const CSSParserToken* declarationValueEnd = range.end(); const CSSParserToken* last = range.end() - 1; while (last->type() == WhitespaceToken) --last; if (last->type() == IdentToken && last->valueEqualsIgnoringCase("important")) { --last; while (last->type() == WhitespaceToken) --last; if (last->type() == DelimiterToken && last->delimiter() == '!') { important = true; declarationValueEnd = last; } } if (m_observerWrapper && ruleType == StyleRule::Style) { size_t propertiesCount = m_parsedProperties.size(); if (unresolvedProperty != CSSPropertyInvalid) consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), unresolvedProperty, important, ruleType); m_observerWrapper->observer().startProperty(m_observerWrapper->startOffset(rangeCopy)); m_observerWrapper->observer().endProperty(important, m_parsedProperties.size() != propertiesCount, m_observerWrapper->endOffset(rangeCopy), NoCSSError); return; } if (unresolvedProperty == CSSPropertyInvalid) return; consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), unresolvedProperty, important, ruleType); }
bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const { range.consumeWhitespace(); ASSERT(range.peek().type() == IdentToken); AtomicString variableName = range.consumeIncludingWhitespace().value().toAtomicString(); ASSERT(range.atEnd() || (range.peek().type() == CommaToken)); RefPtr<CSSCustomPropertyValue> property = customProperties.get(variableName); if (!property || !property->value()) return resolveVariableFallback(customProperties, range, result); if (property->containsVariables()) { // FIXME: Avoid doing this work more than once. RefPtr<CSSVariableData> resolvedData = property->value()->resolveVariableReferences(customProperties); if (!resolvedData) return false; result.appendVector(resolvedData->tokens()); } else result.appendVector(property->value()->tokens()); return true; }