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; }
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; }
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(); }
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; }
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 } }
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeClass(CSSParserTokenRange& range) { ASSERT(range.peek().type() == DelimiterToken); ASSERT(range.peek().delimiter() == '.'); range.consume(); if (range.peek().type() != IdentToken) return nullptr; OwnPtr<CSSParserSelector> selector = CSSParserSelector::create(); selector->setMatch(CSSSelector::Class); const AtomicString& value = range.consume().value(); if (isQuirksModeBehavior(m_context.mode())) selector->setValue(value.lower()); else selector->setValue(value); return selector.release(); }
bool SizesAttributeParser::parse(CSSParserTokenRange range) { // Split on a comma token and parse the result tokens as (media-condition, length) pairs while (!range.atEnd()) { const CSSParserToken* mediaConditionStart = &range.peek(); // The length is the last component value before the comma which isn't whitespace or a comment const CSSParserToken* lengthTokenStart = &range.peek(); const CSSParserToken* lengthTokenEnd = &range.peek(); while (!range.atEnd() && range.peek().type() != CommaToken) { lengthTokenStart = &range.peek(); range.consumeComponentValue(); lengthTokenEnd = &range.peek(); range.consumeWhitespace(); } range.consume(); float length; if (!calculateLengthInPixels(range.makeSubRange(lengthTokenStart, lengthTokenEnd), length)) continue; RefPtrWillBeRawPtr<MediaQuerySet> mediaCondition = MediaQueryParser::parseMediaCondition(range.makeSubRange(mediaConditionStart, lengthTokenStart)); if (!mediaCondition || !mediaConditionMatches(mediaCondition)) continue; m_length = length; m_lengthWasSet = true; return true; } return false; }
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 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 CSSVariableResolver::resolveFallback(CSSParserTokenRange range, Vector<CSSParserToken>& result) { if (range.atEnd()) return false; ASSERT(range.peek().type() == CommaToken); range.consume(); return resolveTokenRange(range, result); }
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); }
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 }
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; }
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 CSSSelectorParser::consumeName(CSSParserTokenRange& range, AtomicString& name, AtomicString& namespacePrefix) { name = nullAtom; namespacePrefix = nullAtom; const CSSParserToken& firstToken = range.peek(); if (firstToken.type() == IdentToken) { name = firstToken.value().toAtomicString(); range.consume(); } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '*') { name = starAtom; range.consume(); } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '|') { // This is an empty namespace, which'll get assigned this value below name = emptyAtom; } else { return false; } if (range.peek().type() != DelimiterToken || range.peek().delimiter() != '|') return true; range.consume(); namespacePrefix = name; const CSSParserToken& nameToken = range.consume(); if (nameToken.type() == IdentToken) { name = nameToken.value().toAtomicString(); } else if (nameToken.type() == DelimiterToken && nameToken.delimiter() == '*') { name = starAtom; } else { name = nullAtom; namespacePrefix = nullAtom; return false; } return true; }
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; }
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 CSSSelectorParser::consumeName(CSSParserTokenRange& range, AtomicString& name, AtomicString& namespacePrefix, bool& hasNamespace) { name = nullAtom; namespacePrefix = nullAtom; hasNamespace = false; const CSSParserToken& firstToken = range.peek(); if (firstToken.type() == IdentToken) { name = firstToken.value(); range.consume(); } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '*') { name = starAtom; range.consume(); } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '|') { // No namespace } else { return false; } if (range.peek().type() != DelimiterToken || range.peek().delimiter() != '|') return true; range.consume(); hasNamespace = true; namespacePrefix = name; const CSSParserToken& nameToken = range.consume(); if (nameToken.type() == IdentToken) { name = nameToken.value(); } else if (nameToken.type() == DelimiterToken && nameToken.delimiter() == '*') { name = starAtom; } else { name = nullAtom; namespacePrefix = nullAtom; return false; } return true; }
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(); }
void CSSParserImpl::consumeDeclarationList(CSSParserTokenRange range, StyleRule::Type ruleType) { ASSERT(m_parsedProperties.isEmpty()); bool useObserver = m_observerWrapper && ruleType == StyleRule::Style; if (useObserver) { m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(range)); m_observerWrapper->skipCommentsBefore(range, true); } while (!range.atEnd()) { switch (range.peek().type()) { case WhitespaceToken: case SemicolonToken: range.consume(); break; case IdentToken: { const CSSParserToken* declarationStart = &range.peek(); if (useObserver) m_observerWrapper->yieldCommentsBefore(range); while (!range.atEnd() && range.peek().type() != SemicolonToken) range.consumeComponentValue(); consumeDeclaration(range.makeSubRange(declarationStart, &range.peek()), ruleType); if (useObserver) m_observerWrapper->skipCommentsBefore(range, false); break; } case AtKeywordToken: { RefPtrWillBeRawPtr<StyleRuleBase> rule = consumeAtRule(range, NoRules); ASSERT_UNUSED(rule, !rule); break; } default: // Parse error, unexpected token in declaration list while (!range.atEnd() && range.peek().type() != SemicolonToken) range.consumeComponentValue(); break; } } // Yield remaining comments if (useObserver) { m_observerWrapper->yieldCommentsBefore(range); m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(range)); } }
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 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); }
// The state machine loop PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl(CSSParserTokenRange range) { while (!range.atEnd()) processToken(range.consume()); // FIXME: Can we get rid of this special case? if (m_parserType == MediaQuerySetParser) processToken(CSSParserToken(EOFToken)); if (m_state != ReadAnd && m_state != ReadRestrictor && m_state != Done && m_state != ReadMediaNot) m_querySet->addMediaQuery(MediaQuery::createNotAll()); else if (m_mediaQueryData.currentMediaQueryChanged()) m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery()); return m_querySet; }
void CSSVariableData::consumeAndUpdateTokens(const CSSParserTokenRange& range) { StringBuilder stringBuilder; CSSParserTokenRange localRange = range; while (!localRange.atEnd()) { CSSParserToken token = localRange.consume(); if (token.hasStringBacking()) stringBuilder.append(token.value()); } m_backingString = stringBuilder.toString(); if (m_backingString.is8Bit()) updateTokens<LChar>(range); else updateTokens<UChar>(range); }
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; }
TEST(CSSTokenizerBlockTest, Basic) { BlockTestCase testCases[] = { {"(max-width: 800px()), (max-width: 800px)", 2, 0}, {"(max-width: 900px(()), (max-width: 900px)", 3, 1}, {"(max-width: 600px(())))), (max-width: 600px)", 3, 0}, {"(max-width: 500px(((((((((())))), (max-width: 500px)", 11, 6}, {"(max-width: 800px[]), (max-width: 800px)", 2, 0}, {"(max-width: 900px[[]), (max-width: 900px)", 3, 2}, {"(max-width: 600px[[]]]]), (max-width: 600px)", 3, 0}, {"(max-width: 500px[[[[[[[[[[]]]]), (max-width: 500px)", 11, 7}, {"(max-width: 800px{}), (max-width: 800px)", 2, 0}, {"(max-width: 900px{{}), (max-width: 900px)", 3, 2}, {"(max-width: 600px{{}}}}), (max-width: 600px)", 3, 0}, {"(max-width: 500px{{{{{{{{{{}}}}), (max-width: 500px)", 11, 7}, {"[(), (max-width: 400px)", 2, 1}, {"[{}, (max-width: 500px)", 2, 1}, {"[{]}], (max-width: 900px)", 2, 0}, {"[{[]{}{{{}}}}], (max-width: 900px)", 5, 0}, {"[{[}], (max-width: 900px)", 3, 2}, {"[({)}], (max-width: 900px)", 3, 2}, {"[]((), (max-width: 900px)", 2, 1}, {"((), (max-width: 900px)", 2, 1}, {"(foo(), (max-width: 900px)", 2, 1}, {"[](()), (max-width: 900px)", 2, 0}, {"all an[isdfs bla())(i())]icalc(i)(()), (max-width: 400px)", 3, 0}, {"all an[isdfs bla())(]icalc(i)(()), (max-width: 500px)", 4, 2}, {"all an[isdfs bla())(]icalc(i)(())), (max-width: 600px)", 4, 1}, {"all an[isdfs bla())(]icalc(i)(()))], (max-width: 800px)", 4, 0}, {0, 0, 0} // Do not remove the terminator line. }; for (int i = 0; testCases[i].input; ++i) { CSSTokenizer::Scope scope(testCases[i].input); CSSParserTokenRange range = scope.tokenRange(); MediaQueryBlockWatcher blockWatcher; unsigned maxLevel = 0; unsigned level = 0; while (!range.atEnd()) { blockWatcher.handleToken(range.consume()); level = blockWatcher.blockLevel(); maxLevel = std::max(level, maxLevel); } ASSERT_EQ(testCases[i].maxLevel, maxLevel); ASSERT_EQ(testCases[i].finalLevel, level); } }
// The state machine loop RefPtr<MediaQuerySet> MediaQueryParser::parseInternal(CSSParserTokenRange range) { while (!range.atEnd()) processToken(range.consume()); // FIXME: Can we get rid of this special case? if (m_parserType == MediaQuerySetParser) processToken(CSSParserToken(EOFToken)); if (m_state != ReadAnd && m_state != ReadRestrictor && m_state != Done && m_state != ReadMediaNot) { MediaQuery query = MediaQuery(MediaQuery::Not, "all", Vector<MediaQueryExpression>()); m_querySet->addMediaQuery(WTFMove(query)); } else if (m_mediaQueryData.currentMediaQueryChanged()) commitMediaQuery(); return m_querySet; }
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; }
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; }
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 } }