Example #1
0
bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range, std::pair<int, int>& result)
{
    const CSSParserToken& token = range.consume();
    if (token.type() == NumberToken && token.numericValueType() == IntegerValueType) {
        result = std::make_pair(0, static_cast<int>(token.numericValue()));
        return true;
    }
    if (token.type() == IdentToken) {
        if (token.valueEqualsIgnoringCase("odd")) {
            result = std::make_pair(2, 1);
            return true;
        }
        if (token.valueEqualsIgnoringCase("even")) {
            result = std::make_pair(2, 0);
            return true;
        }
    }

    // The 'n' will end up as part of an ident or dimension. For a valid <an+b>,
    // this will store a string of the form 'n', 'n-', or 'n-123'.
    String nString;

    if (token.type() == DelimiterToken && token.delimiter() == '+' && range.peek().type() == IdentToken) {
        result.first = 1;
        nString = range.consume().value();
    } else if (token.type() == DimensionToken && token.numericValueType() == IntegerValueType) {
        result.first = token.numericValue();
        nString = token.value();
    } else if (token.type() == IdentToken) {
        if (token.value()[0] == '-') {
            result.first = -1;
            nString = String(token.value()).substring(1);
        } else {
            result.first = 1;
            nString = token.value();
        }
    }

    range.consumeWhitespace();

    if (nString.isEmpty() || !isASCIIAlphaCaselessEqual(nString[0], 'n'))
        return false;
    if (nString.length() > 1 && nString[1] != '-')
        return false;

    if (nString.length() > 2) {
        bool valid;
        result.second = nString.substring(1).toIntStrict(&valid);
        return valid;
    }

    NumericSign sign = nString.length() == 1 ? NoSign : MinusSign;
    if (sign == NoSign && range.peek().type() == DelimiterToken) {
        char delimiterSign = range.consumeIncludingWhitespace().delimiter();
        if (delimiterSign == '+')
            sign = PlusSign;
        else if (delimiterSign == '-')
            sign = MinusSign;
        else
            return false;
    }

    if (sign == NoSign && range.peek().type() != NumberToken) {
        result.second = 0;
        return true;
    }

    const CSSParserToken& b = range.consume();
    if (b.type() != NumberToken || b.numericValueType() != IntegerValueType)
        return false;
    if ((b.numericSign() == NoSign) == (sign == NoSign))
        return false;
    result.second = b.numericValue();
    if (sign == MinusSign)
        result.second = -result.second;
    return true;
}
Example #2
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;
}
Example #3
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;
}