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
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;
}
Exemple #3
0
CSSParserTokenRange consumeFunction(CSSParserTokenRange& range)
{
    ASSERT(range.peek().type() == FunctionToken);
    CSSParserTokenRange contents = range.consumeBlock();
    range.consumeWhitespace();
    contents.consumeWhitespace();
    return contents;
}
Exemple #4
0
bool CSSVariableData::resolveTokenRange(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
{
    bool success = true;
    while (!range.atEnd()) {
        if (range.peek().functionId() == CSSValueVar)
            success &= resolveVariableReference(customProperties, range.consumeBlock(), result);
        else
            result.append(range.consume());
    }
    return success;
}
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
{
    ASSERT(range.peek().type() == AtKeywordToken);
    const CSSParserString& name = range.consume().value();
    const CSSParserToken* preludeStart = &range.peek();
    while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken)
        range.consumeComponentValue();

    CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
    CSSAtRuleID id = cssAtRuleID(name);
    if (id != CSSAtRuleInvalid && m_context.useCounter())
        countAtRule(m_context.useCounter(), id);

    if (range.atEnd() || range.peek().type() == SemicolonToken) {
        range.consume();
        if (allowedRules == AllowCharsetRules && id == CSSAtRuleCharset)
            return consumeCharsetRule(prelude);
        if (allowedRules <= AllowImportRules && id == CSSAtRuleImport)
            return consumeImportRule(prelude);
        if (allowedRules <= AllowNamespaceRules && id == CSSAtRuleNamespace)
            return consumeNamespaceRule(prelude);
        return nullptr; // Parse error, unrecognised at-rule without block
    }

    CSSParserTokenRange block = range.consumeBlock();
    if (allowedRules == KeyframeRules)
        return nullptr; // Parse error, no at-rules supported inside @keyframes
    if (allowedRules == NoRules)
        return nullptr; // Parse error, no at-rules supported inside declaration lists

    ASSERT(allowedRules <= RegularRules);

    switch (id) {
    case CSSAtRuleMedia:
        return consumeMediaRule(prelude, block);
    case CSSAtRuleSupports:
        return consumeSupportsRule(prelude, block);
    case CSSAtRuleViewport:
        return consumeViewportRule(prelude, block);
    case CSSAtRuleFontFace:
        return consumeFontFaceRule(prelude, block);
    case CSSAtRuleWebkitKeyframes:
        return consumeKeyframesRule(true, prelude, block);
    case CSSAtRuleKeyframes:
        return consumeKeyframesRule(false, prelude, block);
    case CSSAtRulePage:
        return consumePageRule(prelude, block);
    default:
        return nullptr; // Parse error, unrecognised at-rule with block
    }
}
    bool parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result)
    {
        if (checkDepthAndIndex(&depth, tokens) != OK)
            return false;

        if (tokens.peek().type() == LeftParenthesisToken) {
            CSSParserTokenRange innerRange = tokens.consumeBlock();
            tokens.consumeWhitespace();
            innerRange.consumeWhitespace();
            return parseValueExpression(innerRange, depth, result);
        }

        return parseValue(tokens, result);
    }
bool CSSVariableResolver::resolveTokenRange(CSSParserTokenRange range,
    Vector<CSSParserToken>& result)
{
    bool success = true;
    while (!range.atEnd()) {
        if (range.peek().functionId() == CSSValueVar) {
            success &= resolveVariableReference(range.consumeBlock(), result);
        } else if (range.peek().type() == AtKeywordToken && range.peek().valueEqualsIgnoringASCIICase("apply")
            && RuntimeEnabledFeatures::cssApplyAtRulesEnabled()) {
            resolveApplyAtRule(range, result);
        } else {
            result.append(range.consume());
        }
    }
    return success;
}
// This may still consume tokens if it fails
static AtomicString consumeStringOrURI(CSSParserTokenRange& range)
{
    const CSSParserToken& token = range.peek();

    if (token.type() == StringToken || token.type() == UrlToken)
        return range.consumeIncludingWhitespace().value();

    if (token.type() != FunctionToken || !token.valueEqualsIgnoringCase("url"))
        return AtomicString();

    CSSParserTokenRange contents = range.consumeBlock();
    const CSSParserToken& uri = contents.consumeIncludingWhitespace();
    ASSERT(uri.type() == StringToken);
    if (!contents.atEnd())
        return AtomicString();
    return uri.value();
}
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
{
    ASSERT(range.peek().type() == AtKeywordToken);
    const CSSParserString& name = range.consume().value();
    const CSSParserToken* preludeStart = &range.peek();
    while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken)
        range.consumeComponentValue();

    CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());

    if (range.atEnd() || range.peek().type() == SemicolonToken) {
        range.consume();
        if (allowedRules == AllowCharsetRules && name.equalIgnoringCase("charset"))
            return consumeCharsetRule(prelude);
        if (allowedRules <= AllowImportRules && name.equalIgnoringCase("import"))
            return consumeImportRule(prelude);
        if (allowedRules <= AllowNamespaceRules && name.equalIgnoringCase("namespace"))
            return consumeNamespaceRule(prelude);
        return nullptr; // Parse error, unrecognised at-rule without block
    }

    CSSParserTokenRange block = range.consumeBlock();
    if (allowedRules == KeyframeRules)
        return nullptr; // Parse error, no at-rules supported inside @keyframes
    if (allowedRules == NoRules)
        return nullptr; // Parse error, no at-rules supported inside declaration lists

    ASSERT(allowedRules <= RegularRules);

    if (name.equalIgnoringCase("media"))
        return consumeMediaRule(prelude, block);
    if (name.equalIgnoringCase("supports"))
        return consumeSupportsRule(prelude, block);
    if (name.equalIgnoringCase("viewport"))
        return consumeViewportRule(prelude, block);
    if (name.equalIgnoringCase("font-face"))
        return consumeFontFaceRule(prelude, block);
    if (name.equalIgnoringCase("-webkit-keyframes"))
        return consumeKeyframesRule(true, prelude, block);
    if (name.equalIgnoringCase("keyframes"))
        return consumeKeyframesRule(false, prelude, block);
    if (name.equalIgnoringCase("page"))
        return consumePageRule(prelude, block);
    return nullptr; // Parse error, unrecognised at-rule with block
}
Exemple #10
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;
}
Exemple #11
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;
}
bool classifyBlock(CSSParserTokenRange range, bool& hasReferences, bool isTopLevelBlock = true)
{
    while (!range.atEnd()) {
        if (range.peek().blockType() == CSSParserToken::BlockStart) {
            const CSSParserToken& token = range.peek();
            CSSParserTokenRange block = range.consumeBlock();
            if (token.functionId() == CSSValueVar) {
                if (!isValidVariableReference(block))
                    return false; // Bail if any references are invalid
                hasReferences = true;
                continue;
            }
            if (!classifyBlock(block, hasReferences, false))
                return false;
            continue;
        }

        ASSERT(range.peek().blockType() != CSSParserToken::BlockEnd);

        const CSSParserToken& token = range.consume();
        switch (token.type()) {
        case DelimiterToken: {
            if (token.delimiter() == '!' && isTopLevelBlock)
                return false;
            break;
        }
        case RightParenthesisToken:
        case RightBraceToken:
        case RightBracketToken:
        case BadStringToken:
        case BadUrlToken:
            return false;
        case SemicolonToken:
            if (isTopLevelBlock)
                return false;
            break;
        default:
            break;
        }
    }
    return true;
}
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::consumeQualifiedRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
{
    const CSSParserToken* preludeStart = &range.peek();
    while (!range.atEnd() && range.peek().type() != LeftBraceToken)
        range.consumeComponentValue();

    if (range.atEnd())
        return nullptr; // Parse error, EOF instead of qualified rule block

    CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
    CSSParserTokenRange block = range.consumeBlock();

    if (allowedRules <= RegularRules)
        return consumeStyleRule(prelude, block);
    if (allowedRules == KeyframeRules)
        return consumeKeyframeStyleRule(prelude, block);

    ASSERT_NOT_REACHED();
    return nullptr;
}
Exemple #14
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();
}
Exemple #15
0
PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumePseudo(CSSParserTokenRange& range)
{
    ASSERT(range.peek().type() == ColonToken);
    range.consume();

    int colons = 1;
    if (range.peek().type() == ColonToken) {
        range.consume();
        colons++;
    }

    const CSSParserToken& token = range.peek();
    if (token.type() != IdentToken && token.type() != FunctionToken)
        return nullptr;

    OwnPtr<CSSParserSelector> selector = CSSParserSelector::create();
    selector->setMatch(colons == 1 ? CSSSelector::PseudoClass : CSSSelector::PseudoElement);
    selector->setValue(AtomicString(String(token.value()).lower()));

    if (token.type() == IdentToken) {
        range.consume();
        if (selector->pseudoType() == CSSSelector::PseudoUnknown)
            return nullptr;
        return selector.release();
    }

    CSSParserTokenRange block = range.consumeBlock();
    block.consumeWhitespace();

    if ((colons == 1
        && (token.valueEqualsIgnoringCase("host")
            || token.valueEqualsIgnoringCase("host-context")
            || token.valueEqualsIgnoringCase("-webkit-any")))
        || (colons == 2 && token.valueEqualsIgnoringCase("cue"))) {

        CSSSelectorList* selectorList = new CSSSelectorList();
        consumeCompoundSelectorList(block, *selectorList);
        if (!selectorList->isValid() || !block.atEnd())
            return nullptr;

        selector->setSelectorList(adoptPtr(selectorList));
        selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached?
        ASSERT(selector->pseudoType() != CSSSelector::PseudoUnknown);
        return selector.release();
    }

    if (colons == 1 && token.valueEqualsIgnoringCase("not")) {
        OwnPtr<CSSParserSelector> innerSelector = consumeCompoundSelector(block);
        if (!innerSelector || !innerSelector->isSimple() || !block.atEnd())
            return nullptr;
        Vector<OwnPtr<CSSParserSelector>> selectorVector;
        selectorVector.append(innerSelector.release());
        selector->adoptSelectorVector(selectorVector);
        return selector.release();
    }

    if (colons == 1 && token.valueEqualsIgnoringCase("lang")) {
        // FIXME: CSS Selectors Level 4 allows :lang(*-foo)
        const CSSParserToken& ident = block.consumeIncludingWhitespace();
        if (ident.type() != IdentToken || !block.atEnd())
            return nullptr;
        selector->setArgument(ident.value());
        selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached?
        ASSERT(selector->pseudoType() == CSSSelector::PseudoLang);
        return selector.release();
    }

    if (colons == 1
        && (token.valueEqualsIgnoringCase("nth-child")
            || token.valueEqualsIgnoringCase("nth-last-child")
            || token.valueEqualsIgnoringCase("nth-of-type")
            || token.valueEqualsIgnoringCase("nth-last-of-type"))) {
        std::pair<int, int> ab;
        if (!consumeANPlusB(block, ab))
            return nullptr;
        block.consumeWhitespace();
        if (!block.atEnd())
            return nullptr;
        // FIXME: We shouldn't serialize here and reparse in CSSSelector!
        // Serialization should be in CSSSelector::selectorText instead.
        int a = ab.first;
        int b = ab.second;
        String string;
        if (a == 0 && b == 0)
            string = "0";
        else if (a == 0)
            string = String::number(b);
        else if (b == 0)
            string = String::format("%dn", a);
        else if (ab.second < 0)
            string = String::format("%dn%d", a, b);
        else
            string = String::format("%dn+%d", a, b);
        selector->setArgument(AtomicString(string));
        selector->pseudoType(); // FIXME: Do we need to force the pseudo type to be cached?
        ASSERT(selector->pseudoType() != CSSSelector::PseudoUnknown);
        return selector.release();
    }

    return nullptr;
}
CSSParserValueList::CSSParserValueList(CSSParserTokenRange range)
: m_current(0)
{
    Vector<CSSParserValueList*> stack;
    Vector<int> bracketCounts;
    stack.append(this);
    bracketCounts.append(0);
    while (!range.atEnd()) {
        ASSERT(stack.size() == bracketCounts.size());
        ASSERT(!stack.isEmpty());
        const CSSParserToken& token = range.peek();
        if (token.type() != FunctionToken)
            range.consume();
        CSSParserValue value;
        switch (token.type()) {
        case FunctionToken: {
            if (token.valueEqualsIgnoringCase("url")) {
                range.consume();
                const CSSParserToken& next = range.consumeIncludingWhitespace();
                if (next.type() == BadStringToken || range.consume().type() != RightParenthesisToken) {
                    destroyAndClear();
                    return;
                }
                ASSERT(next.type() == StringToken);
                value.id = CSSValueInvalid;
                value.isInt = false;
                value.m_unit = CSSParserValue::URI;
                value.string = next.value();
                break;
            } else if (token.valueEqualsIgnoringCase("var")) {
                destroyAndClear();
                return;
            }

            value.id = CSSValueInvalid;
            value.isInt = false;

            CSSValueID id = token.functionId();
            if (id == CSSValueCalc || id == CSSValueWebkitCalc) {
                value.m_unit = CSSParserValue::CalcFunction;
                value.calcFunction = new CSSParserCalcFunction(range.consumeBlock());
                break;
            }
            range.consume();
            value.m_unit = CSSParserValue::Function;
            CSSParserFunction* function = new CSSParserFunction;
            function->id = id;
            CSSParserValueList* list = new CSSParserValueList;
            function->args = adoptPtr(list);

            value.function = function;

            stack.last()->addValue(value);
            stack.append(list);
            bracketCounts.append(0);
            continue;
        }
        case LeftParenthesisToken: {
            CSSParserValueList* list = new CSSParserValueList;
            value.setFromValueList(adoptPtr(list));
            stack.last()->addValue(value);
            stack.append(list);
            bracketCounts.append(0);
            continue;
        }
        case RightParenthesisToken: {
            if (bracketCounts.last() == 0) {
                stack.removeLast();
                bracketCounts.removeLast();
                if (bracketCounts.isEmpty()) {
                    destroyAndClear();
                    return;
                }
                continue;
            }
            bracketCounts.last()--;
            value.setFromOperator(')');
            break;
        }
        case IdentToken: {
            value.id = token.id();
            value.isInt = false;
            value.m_unit = CSSParserValue::Identifier;
            value.string = token.value();
            break;
        }
        case DimensionToken:
            if (token.unitType() == CSSPrimitiveValue::UnitType::Unknown) {
                // Unknown dimensions are handled as a list of two values
                value.m_unit = CSSParserValue::DimensionList;
                CSSParserValueList* list = new CSSParserValueList;
                value.valueList = list;
                value.id = CSSValueInvalid;

                CSSParserValue number;
                number.setFromNumber(token.numericValue(), CSSPrimitiveValue::UnitType::Number);
                number.isInt = (token.numericValueType() == IntegerValueType);
                list->addValue(number);

                CSSParserValue unit;
                unit.string = token.value();
                unit.m_unit = CSSParserValue::Identifier;
                list->addValue(unit);

                break;
            }
            // fallthrough
        case NumberToken:
        case PercentageToken:
            value.setFromNumber(token.numericValue(), token.unitType());
            value.isInt = (token.numericValueType() == IntegerValueType);
            break;
        case UnicodeRangeToken: {
            value.id = CSSValueInvalid;
            value.isInt = false;
            value.m_unicodeRange.start = token.unicodeRangeStart();
            value.m_unicodeRange.end = token.unicodeRangeEnd();
            value.m_unit = CSSParserValue::UnicodeRange;
            break;
        }
        case HashToken:
        case StringToken:
        case UrlToken: {
            value.id = CSSValueInvalid;
            value.isInt = false;
            if (token.type() == HashToken)
                value.m_unit = CSSParserValue::HexColor;
            else if (token.type() == StringToken)
                value.m_unit = CSSParserValue::String;
            else
                value.m_unit = CSSParserValue::URI;
            value.string = token.value();
            break;
        }
        case DelimiterToken:
            value.setFromOperator(token.delimiter());
            break;
        case CommaToken:
            value.setFromOperator(',');
            break;
        case LeftBracketToken:
            value.setFromOperator('[');
            break;
        case RightBracketToken:
            value.setFromOperator(']');
            break;
        case LeftBraceToken:
            value.setFromOperator('{');
            break;
        case RightBraceToken:
            value.setFromOperator('}');
            break;
        case WhitespaceToken:
            continue;
        case CommentToken:
        case EOFToken:
            ASSERT_NOT_REACHED();
        case CDOToken:
        case CDCToken:
        case AtKeywordToken:
        case IncludeMatchToken:
        case DashMatchToken:
        case PrefixMatchToken:
        case SuffixMatchToken:
        case SubstringMatchToken:
        case ColumnToken:
        case ColonToken:
        case SemicolonToken:
            destroyAndClear();
            return;
        case BadStringToken:
        case BadUrlToken:
            destroyAndClear();
            return;
        }
        stack.last()->addValue(value);
    }

    CSSParserValue rightParenthesis;
    rightParenthesis.setFromOperator(')');
    while (!stack.isEmpty()) {
        while (bracketCounts.last() > 0) {
            bracketCounts.last()--;
            stack.last()->addValue(rightParenthesis);
        }
        stack.removeLast();
        bracketCounts.removeLast();
    }
}
Exemple #17
0
std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumePseudo(
    CSSParserTokenRange& range) {
  ASSERT(range.peek().type() == ColonToken);
  range.consume();

  int colons = 1;
  if (range.peek().type() == ColonToken) {
    range.consume();
    colons++;
  }

  const CSSParserToken& token = range.peek();
  if (token.type() != IdentToken && token.type() != FunctionToken)
    return nullptr;

  std::unique_ptr<CSSParserSelector> selector = CSSParserSelector::create();
  selector->setMatch(colons == 1 ? CSSSelector::PseudoClass
                                 : CSSSelector::PseudoElement);

  AtomicString value = token.value().toAtomicString().lowerASCII();
  bool hasArguments = token.type() == FunctionToken;
  selector->updatePseudoType(value, hasArguments);

  if (selector->match() == CSSSelector::PseudoElement &&
      m_disallowPseudoElements)
    return nullptr;

  if (token.type() == IdentToken) {
    range.consume();
    if (selector->pseudoType() == CSSSelector::PseudoUnknown)
      return nullptr;
    return selector;
  }

  CSSParserTokenRange block = range.consumeBlock();
  block.consumeWhitespace();
  if (selector->pseudoType() == CSSSelector::PseudoUnknown)
    return nullptr;

  switch (selector->pseudoType()) {
    case CSSSelector::PseudoHost:
    case CSSSelector::PseudoHostContext:
    case CSSSelector::PseudoAny:
    case CSSSelector::PseudoCue: {
      DisallowPseudoElementsScope scope(this);

      std::unique_ptr<CSSSelectorList> selectorList =
          wrapUnique(new CSSSelectorList());
      *selectorList = consumeCompoundSelectorList(block);
      if (!selectorList->isValid() || !block.atEnd())
        return nullptr;
      selector->setSelectorList(std::move(selectorList));
      return selector;
    }
    case CSSSelector::PseudoNot: {
      std::unique_ptr<CSSParserSelector> innerSelector =
          consumeCompoundSelector(block);
      block.consumeWhitespace();
      if (!innerSelector || !innerSelector->isSimple() || !block.atEnd())
        return nullptr;
      Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
      selectorVector.append(std::move(innerSelector));
      selector->adoptSelectorVector(selectorVector);
      return selector;
    }
    case CSSSelector::PseudoSlotted: {
      DisallowPseudoElementsScope scope(this);

      std::unique_ptr<CSSParserSelector> innerSelector =
          consumeCompoundSelector(block);
      block.consumeWhitespace();
      if (!innerSelector || !block.atEnd() ||
          !RuntimeEnabledFeatures::shadowDOMV1Enabled())
        return nullptr;
      Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
      selectorVector.append(std::move(innerSelector));
      selector->adoptSelectorVector(selectorVector);
      return selector;
    }
    case CSSSelector::PseudoLang: {
      // FIXME: CSS Selectors Level 4 allows :lang(*-foo)
      const CSSParserToken& ident = block.consumeIncludingWhitespace();
      if (ident.type() != IdentToken || !block.atEnd())
        return nullptr;
      selector->setArgument(ident.value().toAtomicString());
      return selector;
    }
    case CSSSelector::PseudoNthChild:
    case CSSSelector::PseudoNthLastChild:
    case CSSSelector::PseudoNthOfType:
    case CSSSelector::PseudoNthLastOfType: {
      std::pair<int, int> ab;
      if (!consumeANPlusB(block, ab))
        return nullptr;
      block.consumeWhitespace();
      if (!block.atEnd())
        return nullptr;
      selector->setNth(ab.first, ab.second);
      return selector;
    }
    default:
      break;
  }

  return nullptr;
}