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; }
DeferredStyleGroupRuleList::DeferredStyleGroupRuleList(const CSSParserTokenRange& range, CSSDeferredParser& parser) : m_parser(parser) { size_t length = range.end() - range.begin(); m_tokens.reserveCapacity(length); m_tokens.append(range.begin(), length); }
bool CSSLazyParsingState::shouldLazilyParseProperties( const CSSSelectorList& selectors, const CSSParserTokenRange& block) const { // Simple heuristic for an empty block. Note that |block| here does not // include {} brackets. We avoid lazy parsing empty blocks so we can avoid // considering them when possible for matching. Lazy blocks must always be // considered. Three tokens is a reasonable minimum for a block: // ident ':' <value>. if (block.end() - block.begin() <= 2) return false; // Disallow lazy parsing for blocks which have before/after in their selector // list. This ensures we don't cause a collectFeatures() when we trigger // parsing for attr() functions which would trigger expensive invalidation // propagation. for (const auto* s = selectors.first(); s; s = CSSSelectorList::next(*s)) { for (const CSSSelector* current = s; current; current = current->tagHistory()) { const CSSSelector::PseudoType type(current->getPseudoType()); if (type == CSSSelector::PseudoBefore || type == CSSSelector::PseudoAfter) return false; if (current->relation() != CSSSelector::SubSelector) break; } } return true; }
static bool parseHSLParameters(CSSParserTokenRange& range, RGBA32& result, bool parseAlpha) { ASSERT(range.peek().functionId() == CSSValueHsl || range.peek().functionId() == CSSValueHsla); CSSParserTokenRange args = consumeFunction(range); CSSPrimitiveValue* hslValue = consumeNumber(args, ValueRangeAll); if (!hslValue) return false; double colorArray[3]; colorArray[0] = (((hslValue->getIntValue() % 360) + 360) % 360) / 360.0; for (int i = 1; i < 3; i++) { if (!consumeCommaIncludingWhitespace(args)) return false; hslValue = consumePercent(args, ValueRangeAll); if (!hslValue) return false; double doubleValue = hslValue->getDoubleValue(); colorArray[i] = clampTo<double>(doubleValue, 0.0, 100.0) / 100.0; // Needs to be value between 0 and 1.0. } double alpha = 1.0; if (parseAlpha) { if (!consumeCommaIncludingWhitespace(args)) return false; if (!consumeNumberRaw(args, alpha)) return false; alpha = clampTo<double>(alpha, 0.0, 1.0); } result = makeRGBAFromHSLA(colorArray[0], colorArray[1], colorArray[2], alpha); return args.atEnd(); }
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; }
static bool parseRGBParameters(CSSParserTokenRange& range, RGBA32& result, bool parseAlpha) { ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba); CSSParserTokenRange args = consumeFunction(range); CSSPrimitiveValue* colorParameter = consumeInteger(args); if (!colorParameter) colorParameter = consumePercent(args, ValueRangeAll); if (!colorParameter) return false; const bool isPercent = colorParameter->isPercentage(); int colorArray[3]; colorArray[0] = clampRGBComponent(*colorParameter); for (int i = 1; i < 3; i++) { if (!consumeCommaIncludingWhitespace(args)) return false; colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeInteger(args); if (!colorParameter) return false; colorArray[i] = clampRGBComponent(*colorParameter); } if (parseAlpha) { if (!consumeCommaIncludingWhitespace(args)) return false; double alpha; if (!consumeNumberRaw(args, alpha)) return false; // Convert the floating pointer number of alpha to an integer in the range [0, 256), // with an equal distribution across all 256 values. int alphaComponent = static_cast<int>(clampTo<double>(alpha, 0.0, 1.0) * nextafter(256.0, 0.0)); result = makeRGBA(colorArray[0], colorArray[1], colorArray[2], alphaComponent); } else { result = makeRGB(colorArray[0], colorArray[1], colorArray[2]); } return args.atEnd(); }
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(); }
CSSParserTokenRange consumeFunction(CSSParserTokenRange& range) { ASSERT(range.peek().type() == FunctionToken); CSSParserTokenRange contents = range.consumeBlock(); range.consumeWhitespace(); contents.consumeWhitespace(); return contents; }
bool CSSVariableData::resolveVariableFallback(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const { if (range.atEnd()) return false; ASSERT(range.peek().type() == CommaToken); range.consume(); return resolveTokenRange(customProperties, range, result); }
CSSLazyPropertyParserImpl::CSSLazyPropertyParserImpl(CSSParserTokenRange block, CSSLazyParsingState* state) : CSSLazyPropertyParser(), m_lazyState(state) { // Reserve capacity to minimize heap bloat. size_t length = block.end() - block.begin(); m_tokens.reserveCapacity(length); m_tokens.append(block.begin(), length); }
bool consumeCommaIncludingWhitespace(CSSParserTokenRange& range) { CSSParserToken value = range.peek(); if (value.type() != CommaToken) return false; range.consumeIncludingWhitespace(); return true; }
bool CSSVariableResolver::resolveFallback(CSSParserTokenRange range, Vector<CSSParserToken>& result) { if (range.atEnd()) return false; ASSERT(range.peek().type() == CommaToken); range.consume(); return resolveTokenRange(range, result); }
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 consumeSlashIncludingWhitespace(CSSParserTokenRange& range) { CSSParserToken value = range.peek(); if (value.type() != DelimiterToken || value.delimiter() != '/') return false; range.consumeIncludingWhitespace(); return true; }
PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens) { Value result; tokens.consumeWhitespace(); bool ok = parseValueExpression(tokens, 0, &result); if (!ok || !tokens.atEnd()) return nullptr; return result.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; }
std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeId( CSSParserTokenRange& range) { ASSERT(range.peek().type() == HashToken); if (range.peek().getHashTokenType() != HashTokenId) return nullptr; std::unique_ptr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(CSSSelector::Id); AtomicString value = range.consume().value().toAtomicString(); selector->setValue(value, isQuirksModeBehavior(m_context.matchMode())); return selector; }
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; }
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; }
void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, const AtomicString& defaultNamespace, StyleSheetContents* styleSheet, CSSSelectorList& output) { CSSSelectorParser parser(context, defaultNamespace, styleSheet); range.consumeWhitespace(); CSSSelectorList result; parser.consumeComplexSelectorList(range, result); if (range.atEnd()) { output.adopt(result); recordSelectorStats(context, output); } ASSERT(!(output.isValid() && parser.m_failedParsing)); }
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; }
CSSSelectorList CSSSelectorParser::parseSelector( CSSParserTokenRange range, const CSSParserContext& context, StyleSheetContents* styleSheet) { CSSSelectorParser parser(context, styleSheet); range.consumeWhitespace(); CSSSelectorList result = parser.consumeComplexSelectorList(range); if (!range.atEnd()) return CSSSelectorList(); recordSelectorStats(context, result); return result; }
std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeClass( CSSParserTokenRange& range) { ASSERT(range.peek().type() == DelimiterToken); ASSERT(range.peek().delimiter() == '.'); range.consume(); if (range.peek().type() != IdentToken) return nullptr; std::unique_ptr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(CSSSelector::Class); AtomicString value = range.consume().value().toAtomicString(); selector->setValue(value, isQuirksModeBehavior(m_context.matchMode())); return selector; }
bool CSSParserImpl::consumeRuleList(CSSParserTokenRange range, RuleListType ruleListType, const T callback) { AllowedRulesType allowedRules = RegularRules; switch (ruleListType) { case TopLevelRuleList: allowedRules = AllowCharsetRules; break; case RegularRuleList: allowedRules = RegularRules; break; case KeyframesRuleList: allowedRules = KeyframeRules; break; default: ASSERT_NOT_REACHED(); } bool seenRule = false; bool firstRuleValid = false; while (!range.atEnd()) { RefPtrWillBeRawPtr<StyleRuleBase> rule; switch (range.peek().type()) { case WhitespaceToken: range.consumeWhitespace(); continue; case AtKeywordToken: rule = consumeAtRule(range, allowedRules); break; case CDOToken: case CDCToken: if (ruleListType == TopLevelRuleList) { range.consume(); continue; } // fallthrough default: rule = consumeQualifiedRule(range, allowedRules); break; } if (!seenRule) { seenRule = true; firstRuleValid = rule; } if (rule) { allowedRules = computeNewAllowedRules(allowedRules, rule.get()); callback(rule.release()); } } return firstRuleValid; }
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); }
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; }
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeId(CSSParserTokenRange& range) { ASSERT(range.peek().type() == HashToken); if (range.peek().hashTokenType() != HashTokenId) return nullptr; OwnPtr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(CSSSelector::Id); const AtomicString& value = range.consume().value(); if (isQuirksModeBehavior(m_context.mode())) selector->setValue(value.lower()); else selector->setValue(value); return selector.release(); }
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); }
PassRefPtrWillBeRawPtr<StyleRulePage> CSSParserImpl::consumePageRule(CSSParserTokenRange prelude, CSSParserTokenRange block) { // We only support a small subset of the css-page spec. prelude.consumeWhitespace(); AtomicString typeSelector; if (prelude.peek().type() == IdentToken) typeSelector = prelude.consume().value(); AtomicString pseudo; if (prelude.peek().type() == ColonToken) { prelude.consume(); if (prelude.peek().type() != IdentToken) return nullptr; // Parse error; expected ident token following colon in @page header pseudo = prelude.consume().value(); } prelude.consumeWhitespace(); if (!prelude.atEnd()) return nullptr; // Parse error; extra tokens in @page header OwnPtr<CSSParserSelector> selector; if (!typeSelector.isNull() && pseudo.isNull()) { selector = CSSParserSelector::create(QualifiedName(nullAtom, typeSelector, m_defaultNamespace)); } else { selector = CSSParserSelector::create(); if (!pseudo.isNull()) { selector->setMatch(CSSSelector::PagePseudoClass); selector->setValue(pseudo.lower()); if (selector->pseudoType() == CSSSelector::PseudoUnknown) return nullptr; // Parse error; unknown page pseudo-class } if (!typeSelector.isNull()) selector->prependTagSelector(QualifiedName(nullAtom, typeSelector, m_defaultNamespace)); } if (m_observerWrapper) { unsigned endOffset = m_observerWrapper->endOffset(prelude); m_observerWrapper->observer().startRuleHeader(StyleRule::Page, m_observerWrapper->startOffset(prelude)); m_observerWrapper->observer().endRuleHeader(endOffset); } selector->setForPage(); RefPtrWillBeRawPtr<StyleRulePage> pageRule = StyleRulePage::create(); Vector<OwnPtr<CSSParserSelector>> selectorVector; selectorVector.append(selector.release()); pageRule->parserAdoptSelectorVector(selectorVector); consumeDeclarationList(block, StyleRule::Style); pageRule->setProperties(createStylePropertySet(m_parsedProperties, m_context.mode())); m_parsedProperties.clear(); return pageRule.release(); }