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());
}
Exemple #2
0
CSSParserTokenRange consumeFunction(CSSParserTokenRange& range)
{
    ASSERT(range.peek().type() == FunctionToken);
    CSSParserTokenRange contents = range.consumeBlock();
    range.consumeWhitespace();
    contents.consumeWhitespace();
    return contents;
}
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();
}
    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);
    }
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);
}
Exemple #6
0
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;
}
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;
}
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();
}
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();
}
Exemple #10
0
CSSSupportsParser::SupportsResult CSSSupportsParser::supportsCondition(
    CSSParserTokenRange range,
    CSSParserImpl& parser) {
  // TODO(timloh): The spec allows leading whitespace in @supports but not
  // CSS.supports, but major browser vendors allow it in CSS.supports also.
  range.consumeWhitespace();
  return CSSSupportsParser(parser).consumeCondition(range);
}
Exemple #11
0
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;
}
 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens)
 {
     Value result;
     tokens.consumeWhitespace();
     bool ok = parseValueExpression(tokens, 0, &result);
     if (!ok || !tokens.atEnd())
         return nullptr;
     return result.value;
 }
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::parseRule(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, AllowedRulesType allowedRules)
{
    CSSParserImpl parser(context, styleSheet);
    CSSTokenizer::Scope scope(string);
    CSSParserTokenRange range = scope.tokenRange();
    range.consumeWhitespace();
    if (range.atEnd())
        return nullptr; // Parse error, empty rule
    RefPtrWillBeRawPtr<StyleRuleBase> rule;
    if (range.peek().type() == AtKeywordToken)
        rule = parser.consumeAtRule(range, allowedRules);
    else
        rule = parser.consumeQualifiedRule(range, allowedRules);
    if (!rule)
        return nullptr; // Parse error, failed to consume rule
    range.consumeWhitespace();
    if (!rule || !range.atEnd())
        return nullptr; // Parse error, trailing garbage
    return rule;
}
Exemple #14
0
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));
}
Exemple #15
0
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;
}
Exemple #16
0
CSSSelectorList CSSSelectorParser::consumeCompoundSelectorList(
    CSSParserTokenRange& range) {
  Vector<std::unique_ptr<CSSParserSelector>> selectorList;
  std::unique_ptr<CSSParserSelector> selector = consumeCompoundSelector(range);
  range.consumeWhitespace();
  if (!selector)
    return CSSSelectorList();
  selectorList.append(std::move(selector));
  while (!range.atEnd() && range.peek().type() == CommaToken) {
    range.consumeIncludingWhitespace();
    selector = consumeCompoundSelector(range);
    range.consumeWhitespace();
    if (!selector)
      return CSSSelectorList();
    selectorList.append(std::move(selector));
  }

  if (m_failedParsing)
    return CSSSelectorList();

  return CSSSelectorList::adoptSelectorVector(selectorList);
}
Exemple #17
0
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;
}
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;
}
Exemple #19
0
void CSSSelectorParser::consumeCompoundSelectorList(CSSParserTokenRange& range, CSSSelectorList& output)
{
    Vector<OwnPtr<CSSParserSelector>> selectorList;
    OwnPtr<CSSParserSelector> selector = consumeCompoundSelector(range);
    range.consumeWhitespace();
    if (!selector)
        return;
    selectorList.append(selector.release());
    while (!range.atEnd() && range.peek().type() == CommaToken) {
        // FIXME: This differs from the spec grammar:
        // Spec: compound_selector S* [ COMMA S* compound_selector ]* S*
        // Impl: compound_selector S* [ COMMA S* compound_selector S* ]*
        range.consumeIncludingWhitespace();
        selector = consumeCompoundSelector(range);
        range.consumeWhitespace();
        if (!selector)
            return;
        selectorList.append(selector.release());
    }

    if (!m_failedParsing)
        output.adoptSelectorVector(selectorList);
}
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 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);
}
PassRefPtrWillBeRawPtr<StyleRuleImport> CSSParserImpl::consumeImportRule(CSSParserTokenRange prelude)
{
    prelude.consumeWhitespace();
    AtomicString uri(consumeStringOrURI(prelude));
    if (uri.isNull())
        return nullptr; // Parse error, expected string or URI

    if (m_observerWrapper) {
        unsigned endOffset = m_observerWrapper->endOffset(prelude);
        m_observerWrapper->observer().startRuleHeader(StyleRule::Import, m_observerWrapper->startOffset(prelude));
        m_observerWrapper->observer().endRuleHeader(endOffset);
        m_observerWrapper->observer().startRuleBody(endOffset);
        m_observerWrapper->observer().endRuleBody(endOffset);
    }

    return StyleRuleImport::create(uri, MediaQueryParser::parseMediaQuerySet(prelude));
}
Exemple #24
0
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;
}
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));
}
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
    }
}
PassRefPtrWillBeRawPtr<StyleRuleFontFace> CSSParserImpl::consumeFontFaceRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
{
    prelude.consumeWhitespace();
    if (!prelude.atEnd())
        return nullptr; // Parse error; @font-face prelude should be empty

    if (m_observerWrapper) {
        unsigned endOffset = m_observerWrapper->endOffset(prelude);
        m_observerWrapper->observer().startRuleHeader(StyleRule::FontFace, m_observerWrapper->startOffset(prelude));
        m_observerWrapper->observer().endRuleHeader(endOffset);
        m_observerWrapper->observer().startRuleBody(endOffset);
        m_observerWrapper->observer().endRuleBody(endOffset);
    }

    if (m_styleSheet)
        m_styleSheet->setHasFontFaceRule(true);

    consumeDeclarationList(block, StyleRule::FontFace);
    return StyleRuleFontFace::create(createStylePropertySet(m_parsedProperties, m_context.mode()));
}
Exemple #28
0
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();
}
PassRefPtrWillBeRawPtr<StyleRuleViewport> CSSParserImpl::consumeViewportRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
{
    // Allow @viewport rules from UA stylesheets even if the feature is disabled.
    if (!RuntimeEnabledFeatures::cssViewportEnabled() && !isUASheetBehavior(m_context.mode()))
        return nullptr;

    prelude.consumeWhitespace();
    if (!prelude.atEnd())
        return nullptr; // Parser error; @viewport prelude should be empty

    if (m_observerWrapper) {
        unsigned endOffset = m_observerWrapper->endOffset(prelude);
        m_observerWrapper->observer().startRuleHeader(StyleRule::Viewport, m_observerWrapper->startOffset(prelude));
        m_observerWrapper->observer().endRuleHeader(endOffset);
        m_observerWrapper->observer().startRuleBody(endOffset);
        m_observerWrapper->observer().endRuleBody(endOffset);
    }

    consumeDeclarationList(block, StyleRule::Viewport);
    return StyleRuleViewport::create(createStylePropertySet(m_parsedProperties, CSSViewportRuleMode));
}
PassRefPtrWillBeRawPtr<StyleRuleMedia> CSSParserImpl::consumeMediaRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
{
    WillBeHeapVector<RefPtrWillBeMember<StyleRuleBase>> rules;

    if (m_observerWrapper) {
        CSSParserTokenRange preludeWithoutWhitespace = prelude;
        preludeWithoutWhitespace.consumeWhitespace();
        m_observerWrapper->observer().startRuleHeader(StyleRule::Media, m_observerWrapper->startOffset(preludeWithoutWhitespace));
        m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(preludeWithoutWhitespace));
        m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
    }

    consumeRuleList(block, RegularRuleList, [&rules](PassRefPtrWillBeRawPtr<StyleRuleBase> rule) {
        rules.append(rule);
    });

    if (m_observerWrapper)
        m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));

    return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude), rules);
}