// Copyright 2014 The Chromium Authors. All rights reserved.
// Copyright (C) 2016 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "config.h"
#include "CSSSelectorParser.h"

#include "RuntimeEnabledFeatures.h"
#include "CSSParserIdioms.h"
#include "CSSParserMode.h"
#include "CSSSelectorList.h"
#include "Document.h"
#include "Element.h"
#include "SelectorPseudoTypeMap.h"
#include "StyleSheetContents.h"
#include <memory>
#include <wtf/SetForScope.h>

namespace WebCore {

static AtomicString serializeANPlusB(const std::pair<int, int>&);

Optional<CSSSelectorList> CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, StyleSheetContents* styleSheet)
{
    CSSSelectorParser parser(context, styleSheet);
    range.consumeWhitespace();
    CSSSelectorList result = parser.consumeComplexSelectorList(range);
    if (result.isEmpty() || !range.atEnd())
		return Nullopt;
	return WTFMove(result);
}

CSSSelectorParser::CSSSelectorParser(const CSSParserContext& context, StyleSheetContents* styleSheet)
    : m_context(context)
    , m_styleSheet(styleSheet)
	, m_failedParsing(false)
	, m_disallowPseudoElements(false)
{
}

CSSSelectorList CSSSelectorParser::consumeComplexSelectorList(CSSParserTokenRange& range)
{
    Vector<OwnPtr<CSSParserSelector>> selectorList;
    OwnPtr<CSSParserSelector> selector = consumeComplexSelector(range);
    if (!selector)
        return CSSSelectorList();

    selectorList.append(selector.release());
    while (!range.atEnd() && range.peek().type() == CommaToken) {
        range.consumeIncludingWhitespace();
        selector = consumeComplexSelector(range);
        if (!selector)
            return CSSSelectorList();

        selectorList.append(selector.release());
    }

    if (m_failedParsing)
        return CSSSelectorList();

    CSSSelectorList list;
    list.adoptSelectorVector(WTFMove(selectorList));
    return WTFMove(list);
}

bool CSSSelectorParser::supportsComplexSelector(CSSParserTokenRange range, const CSSParserContext& context)
{
    range.consumeWhitespace();
    CSSSelectorParser parser(context, nullptr);

    // @supports requires that all arguments parse.
    parser.m_disableForgivingParsing = true;

    auto parserSelector = parser.consumeComplexSelector(range);
    if (parser.m_failedParsing || !range.atEnd() || !parserSelector)
        return false;

    auto complexSelector = parserSelector->releaseSelector();
    ASSERT(complexSelector);

    return !containsUnknownWebKitPseudoElements(*complexSelector);
}

template<typename ConsumeSelector>
inline CSSSelectorList CSSSelectorParser::consumeForgivingSelectorList(CSSParserTokenRange& range, ConsumeSelector&& consumeSelector)
{
    if (m_failedParsing)
        return CSSSelectorList();

    Vector<OwnPtr<CSSParserSelector>> selectorList;

    auto consumeForgiving = [&] {
    	OwnPtr<CSSParserSelector> selector = consumeSelector(range);

    	if (m_failedParsing && !m_disableForgivingParsing) {
            selector = nullptr;
            m_failedParsing = false;
        }
        if (!range.atEnd() && range.peek().type() != CommaToken) {
            while (!range.atEnd() && range.peek().type() != CommaToken)
                range.consume();
            return;
        }
        if (selector)
            selectorList.append(selector.release());
    };

    consumeForgiving();

    while (!range.atEnd() && range.peek().type() == CommaToken) {
        range.consumeIncludingWhitespace();
        consumeForgiving();
    }

    if (selectorList.isEmpty()) {
        if (m_disableForgivingParsing)
            m_failedParsing = true;
        return CSSSelectorList();
    }

    CSSSelectorList list;
    list.adoptSelectorVector(WTFMove(selectorList));
    return WTFMove(list);
}

CSSSelectorList CSSSelectorParser::consumeForgivingComplexSelectorList(CSSParserTokenRange& range)
{
    return consumeForgivingSelectorList(range, [&](CSSParserTokenRange& range) {
        return consumeComplexSelector(range);
    });
}

CSSSelectorList CSSSelectorParser::consumeForgivingRelativeSelectorList(CSSParserTokenRange& range)
{
    return consumeForgivingSelectorList(range, [&](CSSParserTokenRange& range) {
        return consumeRelativeSelector(range);
    });
}
CSSSelectorList CSSSelectorParser::consumeCompoundSelectorList(CSSParserTokenRange& range)
{
    Vector<OwnPtr<CSSParserSelector>> selectorList;
    OwnPtr<CSSParserSelector> selector = consumeCompoundSelector(range);
    range.consumeWhitespace();
    if (!selector)
        return CSSSelectorList();
    selectorList.append(selector.release());
    while (!range.atEnd() && range.peek().type() == CommaToken) {
        range.consumeIncludingWhitespace();
        selector = consumeCompoundSelector(range);
        range.consumeWhitespace();
        if (!selector)
            return CSSSelectorList();
        selectorList.append(selector.release());
    }

    if (m_failedParsing)
        return CSSSelectorList();
    CSSSelectorList list;
    list.adoptSelectorVector(WTFMove(selectorList));
    return WTFMove(list);
}

static bool consumeLangArgumentList(OwnPtr<Vector<AtomicString>>& argumentList, CSSParserTokenRange& range)
{
    const CSSParserToken& ident = range.consumeIncludingWhitespace();
    if (ident.type() != IdentToken && ident.type() != StringToken)
        return false;
    StringView string = ident.value();
    if (string.startsWith("--"))
        return false;
    argumentList->append(string.toAtomicString());
    while (!range.atEnd() && range.peek().type() == CommaToken) {
        range.consumeIncludingWhitespace();
        const CSSParserToken& ident = range.consumeIncludingWhitespace();
        if (ident.type() != IdentToken && ident.type() != StringToken)
            return false;
        StringView string = ident.value();
        if (string.startsWith("--"))
            return false;
        argumentList->append(string.toAtomicString());
    }
    return range.atEnd();
}

enum CompoundSelectorFlags {
    HasPseudoElementForRightmostCompound = 1 << 0,
    HasContentPseudoElement = 1 << 1
};

static unsigned extractCompoundFlags(const CSSParserSelector& simpleSelector, CSSParserMode parserMode)
{
    if (simpleSelector.match() != CSSSelector::PseudoElement)
        return 0;

    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
    // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
    // input[type="range" i]::-webkit-media-slider-container > div {
    if (parserMode == UASheetMode && simpleSelector.pseudoElementType() == CSSSelector::PseudoElementWebKitCustom)
        return 0;
    return HasPseudoElementForRightmostCompound;
}

static inline bool isDescendantCombinator(CSSSelector::RelationType relation)
{
    return relation == CSSSelector::DescendantSpace;
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParserTokenRange& range)
{
    OwnPtr<CSSParserSelector> selector = consumeCompoundSelector(range);
    if (!selector)
        return nullptr;

    unsigned previousCompoundFlags = 0;

    for (CSSParserSelector* simple = selector.get(); simple && !previousCompoundFlags; simple = simple->tagHistory())
        previousCompoundFlags |= extractCompoundFlags(*simple, m_context.mode);

    while (auto combinator = consumeCombinator(range)) {
        OwnPtr<CSSParserSelector> nextSelector = consumeCompoundSelector(range);
        if (!nextSelector)
            return combinator == isDescendantCombinator(combinator) ? selector.release() : nullptr;
        if (previousCompoundFlags & HasPseudoElementForRightmostCompound)
            return nullptr;
        CSSParserSelector* end = nextSelector.get();
        unsigned compoundFlags = extractCompoundFlags(*end, m_context.mode);
        while (end->tagHistory()) {
            end = end->tagHistory();
            compoundFlags |= extractCompoundFlags(*end, m_context.mode);
        }
        end->setRelation(combinator);
        previousCompoundFlags = compoundFlags;
        end->setTagHistory(selector.release());

        selector = nextSelector.release();
    }

    return selector.release();
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeRelativeSelector(CSSParserTokenRange& range)
{
    auto scopeCombinator = consumeCombinator(range);

    if (scopeCombinator == CSSSelector::Subselector)
        scopeCombinator = CSSSelector::DescendantSpace;

    OwnPtr<CSSParserSelector> selector = consumeComplexSelector(range);
    if (!selector)
        return nullptr;

    auto hasScopePseudoClass = [](const CSSParserSelector& selector) {
        return selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassScope;
    };

    bool hasExplicitScope = hasScopePseudoClass(*selector);

    auto* end = selector.get();
    while (end->tagHistory()) {
        end = end->tagHistory();
        if (hasScopePseudoClass(*end))
            hasExplicitScope = true;
    }

    // If the selector doesn't have an explicit :scope on the left, add an implicit one.
    if (!hasExplicitScope || scopeCombinator != CSSSelector::DescendantSpace) {
        OwnPtr<CSSParserSelector> scopeSelector = adoptPtr(new CSSParserSelector());
        scopeSelector->setMatch(CSSSelector::PseudoClass);
        scopeSelector->setPseudoClassType(CSSSelector::PseudoClassRelativeScope);

        end->setRelation(scopeCombinator);
        end->setTagHistory(scopeSelector.release());
    }

    return selector.release();
}

static bool isScrollbarPseudoClass(CSSSelector::PseudoClassType pseudo)
{
    switch (pseudo) {
    case CSSSelector::PseudoClassEnabled:
    case CSSSelector::PseudoClassDisabled:
    case CSSSelector::PseudoClassHover:
    case CSSSelector::PseudoClassActive:
    case CSSSelector::PseudoClassHorizontal:
    case CSSSelector::PseudoClassVertical:
    case CSSSelector::PseudoClassDecrement:
    case CSSSelector::PseudoClassIncrement:
    case CSSSelector::PseudoClassStart:
    case CSSSelector::PseudoClassEnd:
    case CSSSelector::PseudoClassDoubleButton:
    case CSSSelector::PseudoClassSingleButton:
    case CSSSelector::PseudoClassNoButton:
    case CSSSelector::PseudoClassCornerPresent:
    case CSSSelector::PseudoClassWindowInactive:
        return true;
    default:
        return false;
    }
}

static bool isUserActionPseudoClass(CSSSelector::PseudoClassType pseudo)
{
    switch (pseudo) {
    case CSSSelector::PseudoClassHover:
    case CSSSelector::PseudoClassFocus:
    case CSSSelector::PseudoClassActive:
    case CSSSelector::PseudoClassFocusVisible:
    case CSSSelector::PseudoClassFocusWithin:
        return true;
    default:
        return false;
    }
}

static bool isLogicalCombinationPseudoClass(CSSSelector::PseudoClassType pseudo)
{
    switch (pseudo) {
    case CSSSelector::PseudoClassIs:
    case CSSSelector::PseudoClassWhere:
    case CSSSelector::PseudoClassNot:
    case CSSSelector::PseudoClassAny:
    case CSSSelector::PseudoClassMatches:
    case CSSSelector::PseudoClassHas:
        return true;
    default:
        return false;
    }
}

static bool isPseudoClassValidAfterPseudoElement(CSSSelector::PseudoClassType pseudoClass, CSSSelector::PseudoElementType compoundPseudoElement)
{
    // Validity of these is determined by their content.
    if (isLogicalCombinationPseudoClass(pseudoClass))
        return true;

    switch (compoundPseudoElement) {
    case CSSSelector::PseudoElementPart:
        return !isTreeStructuralPseudoClass(pseudoClass);
    case CSSSelector::PseudoElementSlotted:
        return false;
    case CSSSelector::PseudoElementResizer:
    case CSSSelector::PseudoElementScrollbar:
    case CSSSelector::PseudoElementScrollbarCorner:
    case CSSSelector::PseudoElementScrollbarButton:
    case CSSSelector::PseudoElementScrollbarThumb:
    case CSSSelector::PseudoElementScrollbarTrack:
    case CSSSelector::PseudoElementScrollbarTrackPiece:
        return isScrollbarPseudoClass(pseudoClass);
    case CSSSelector::PseudoElementSelection:
        return pseudoClass == CSSSelector::PseudoClassWindowInactive;
    case CSSSelector::PseudoElementWebKitCustom:
    case CSSSelector::PseudoElementWebKitCustomLegacyPrefixed:
        return isUserActionPseudoClass(pseudoClass);
    default:
        return false;
    }
}

static bool isTreeAbidingPseudoElement(CSSSelector::PseudoElementType pseudoElementType)
{
    switch (pseudoElementType) {
    // FIXME: This list should also include ::placeholder and ::file-selector-button
    case CSSSelector::PseudoElementBefore:
    case CSSSelector::PseudoElementAfter:
    case CSSSelector::PseudoElementMarker:
    case CSSSelector::PseudoElementBackdrop:
        return true;
    default:
        return false;
    }
}

static bool isSimpleSelectorValidAfterPseudoElement(const CSSParserSelector& simpleSelector, CSSSelector::PseudoElementType compoundPseudoElement)
{
    ASSERT(compoundPseudoElement != CSSSelector::PseudoElementUnknown);

    if (compoundPseudoElement == CSSSelector::PseudoElementPart) {
        if (simpleSelector.match() == CSSSelector::PseudoElement && simpleSelector.pseudoElementType() != CSSSelector::PseudoElementPart)
            return true;
    }
    if (compoundPseudoElement == CSSSelector::PseudoElementSlotted) {
        if (simpleSelector.match() == CSSSelector::PseudoElement && isTreeAbidingPseudoElement(simpleSelector.pseudoElementType()))
            return true;
    }
    if (simpleSelector.match() != CSSSelector::PseudoClass)
        return false;

    return isPseudoClassValidAfterPseudoElement(simpleSelector.pseudoClassType(), compoundPseudoElement);
}

static bool atEndIgnoringWhitespace(CSSParserTokenRange range)
{
    range.consumeWhitespace();
    return range.atEnd();
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
{
    ASSERT(!m_precedingPseudoElement || m_disallowPseudoElements);

    OwnPtr<CSSParserSelector> compoundSelector;

    AtomicString namespacePrefix;
    AtomicString elementName;
    const bool hasName = consumeName(range, elementName, namespacePrefix);
	if (!hasName) {
        compoundSelector = consumeSimpleSelector(range);
        if (!compoundSelector)
            return nullptr;
        if (compoundSelector->match() == CSSSelector::PseudoElement)
        	m_precedingPseudoElement = compoundSelector->pseudoElementType();
    }

    while (OwnPtr<CSSParserSelector> simpleSelector = consumeSimpleSelector(range)) {
        if (simpleSelector->match() == CSSSelector::PseudoElement)
        	m_precedingPseudoElement = simpleSelector->pseudoElementType();

        if (compoundSelector)
            compoundSelector = addSimpleSelectorToCompound(compoundSelector.release(), simpleSelector.release());
        else
            compoundSelector = simpleSelector.release();
    }

    if (!m_disallowPseudoElements)
        m_precedingPseudoElement = Optional<CSSSelector::PseudoElementType>();

    // While inside a nested selector like :is(), the default namespace shall be ignored when [1]:
    // * The compound selector represents the subject [2], and
    // * The compound selector does not contain a type/universal selector.
    //
    // [1] https://drafts.csswg.org/selectors/#matches
    // [2] https://drafts.csswg.org/selectors/#selector-subject
    SetForScope<bool> ignoreDefaultNamespace(m_ignoreDefaultNamespace, m_resistDefaultNamespace && !hasName && atEndIgnoringWhitespace(range));
    if (!compoundSelector) {
        AtomicString namespaceURI = determineNamespace(namespacePrefix);
        if (namespaceURI.isNull()) {
            m_failedParsing = true;
            return nullptr;
        }
        if (namespaceURI == defaultNamespace())
            namespacePrefix = nullAtom;
        
        OwnPtr<CSSParserSelector> selector = adoptPtr(new CSSParserSelector(QualifiedName(namespacePrefix, elementName, namespaceURI)));
        return selector.release();
    }
    prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.get());
    return splitCompoundAtImplicitShadowCrossingCombinator(compoundSelector.release(), m_context);
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParserTokenRange& range)
{
    const CSSParserToken& token = range.peek();
    OwnPtr<CSSParserSelector> selector;
    if (token.type() == HashToken)
        selector = consumeId(range);
    else if (token.type() == DelimiterToken && token.delimiter() == '.')
        selector = consumeClass(range);
    else if (token.type() == LeftBracketToken)
        selector = consumeAttribute(range);
    else if (token.type() == ColonToken)
        selector = consumePseudo(range);
    else
        return nullptr;

    if (!selector) {
        m_failedParsing = true;
        return nullptr;
    }

    if (m_precedingPseudoElement) {
        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
        // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
        // video::-webkit-media-text-track-region-container.scrolling
        if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*selector, *m_precedingPseudoElement))
            m_failedParsing = true;
    }

    return selector.release();
}

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;

    namespacePrefix = name;
    if (range.peek(1).type() == IdentToken) {
        range.consume();
        name = range.consume().value().toAtomicString();
    } else if (range.peek(1).type() == DelimiterToken && range.peek(1).delimiter() == '*') {
        range.consume();
        range.consume();
        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().getHashTokenType() != HashTokenId)
        return nullptr;
    OwnPtr<CSSParserSelector> selector = adoptPtr(new CSSParserSelector());
    selector->setMatch(CSSSelector::Id);

    // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
    // to be compatible for now.
    CSSParserToken token = range.consume();
    selector->setValue(token.value().toAtomicString(), m_context.mode == HTMLQuirksMode);
    return selector.release();
}

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 = adoptPtr(new CSSParserSelector());
    selector->setMatch(CSSSelector::Class);

    // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
    // to be compatible for now.
    CSSParserToken token = range.consume();
    selector->setValue(token.value().toAtomicString(), m_context.mode == HTMLQuirksMode);
    return selector.release();
}

PassOwnPtr<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();

    AtomicString namespaceURI = determineNamespace(namespacePrefix);
    if (namespaceURI.isNull())
        return nullptr;

    QualifiedName qualifiedName = namespacePrefix.isNull()
        ? QualifiedName(nullAtom, attributeName, nullAtom)
        : QualifiedName(namespacePrefix, attributeName, namespaceURI);

    OwnPtr<CSSParserSelector> selector = adoptPtr(new CSSParserSelector());

    if (block.atEnd()) {
        selector->setAttribute(qualifiedName, m_context.isHTMLDocument, CSSSelector::CaseSensitive);
        selector->setMatch(CSSSelector::Set);
        return selector.release();
    }

    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, m_context.isHTMLDocument, consumeAttributeFlags(block));

    if (!block.atEnd())
        return nullptr;
    return selector.release();
}

static bool isOnlyPseudoClassFunction(CSSSelector::PseudoClassType pseudoClassType)
{
    switch (pseudoClassType) {
    case CSSSelector::PseudoClassNot:
    case CSSSelector::PseudoClassIs:
    case CSSSelector::PseudoClassMatches:
    case CSSSelector::PseudoClassWhere:
    case CSSSelector::PseudoClassHas:
    case CSSSelector::PseudoClassNthChild:
    case CSSSelector::PseudoClassNthLastChild:
    case CSSSelector::PseudoClassNthOfType:
    case CSSSelector::PseudoClassNthLastOfType:
    case CSSSelector::PseudoClassLang:
    case CSSSelector::PseudoClassAny:
    case CSSSelector::PseudoClassDir:
    case CSSSelector::PseudoClassRole:
        return true;
    default:
        break;
    }
    return false;
}

static bool isOnlyPseudoElementFunction(CSSSelector::PseudoElementType pseudoElementType)
{
    // Note that we omit ::cue since it can be either an ident or a function.
    switch (pseudoElementType) {
    case CSSSelector::PseudoElementPart:
    case CSSSelector::PseudoElementSlotted:
    case CSSSelector::PseudoElementHighlight:
        return true;
    default:
        break;
    }
    return false;
}

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;

    String lowercasedValue = token.value().toString().convertToASCIILowercase();
    StringView value(lowercasedValue);

    if (colons == 1) {
        selector = CSSParserSelector::parsePseudoClassSelectorFromStringView(value);
        if (!selector)
			return nullptr;
		if (selector->match() == CSSSelector::PseudoClass) {
            if (!m_context.focusVisibleEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassFocusVisible)
                return nullptr;
            if (!m_context.hasPseudoClassEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHas)
                return nullptr;
            if (m_context.mode != UASheetMode && selector->pseudoClassType() == CSSSelector::PseudoClassHtmlDocument)
                return nullptr;
#if ENABLE(ATTACHMENT_ELEMENT)
			if (!m_context.attachmentEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHasAttachment)
				return nullptr;
#endif
		}
	} else {
        selector = CSSParserSelector::parsePseudoElementSelectorFromStringView(value);
        // Treat the ident version of cue as PseudoElementWebkitCustom.
		if (token.type() == IdentToken && selector && selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue)
			selector->setPseudoElementType(CSSSelector::PseudoElementWebKitCustom);
	}

    if (!selector)
        return nullptr;

    // Pseudo-elements are not allowed inside pseudo-classes or pseudo-elements.
    if (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements)
        return nullptr;
    SetForScope<bool> disallowPseudoElementsScope(m_disallowPseudoElements, true);

    if (token.type() == IdentToken) {
        range.consume();
        if ((selector->match() == CSSSelector::PseudoElement && (selector->pseudoElementType() == CSSSelector::PseudoElementUnknown || isOnlyPseudoElementFunction(selector->pseudoElementType())))
            || (selector->match() == CSSSelector::PseudoClass && (selector->pseudoClassType() == CSSSelector::PseudoClassUnknown || isOnlyPseudoClassFunction(selector->pseudoClassType()))))
            return nullptr;
        return selector.release();
    }

    CSSParserTokenRange block = range.consumeBlock();
    block.consumeWhitespace();
    if (token.type() != FunctionToken)
        return nullptr;

    if (selector->match() == CSSSelector::PseudoClass) {
        switch (selector->pseudoClassType()) {
        case CSSSelector::PseudoClassNot: {
        	SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
			OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
            *selectorList = consumeComplexSelectorList(block);
            if (!selectorList->componentCount() || !block.atEnd())
                return nullptr;
            selector->setSelectorList(selectorList.release());
            return selector.release();
        }
		case CSSSelector::PseudoClassNthChild:
		case CSSSelector::PseudoClassNthLastChild:
		case CSSSelector::PseudoClassNthOfType:
		case CSSSelector::PseudoClassNthLastOfType: {
			std::pair<int, int> ab;
			if (!consumeANPlusB(block, ab))
				return nullptr;
			block.consumeWhitespace();
            // FIXME: We should be able to do this lazily. See: https://bugs.webkit.org/show_bug.cgi?id=217149
            selector->setArgument(serializeANPlusB(ab));
			if (!block.atEnd()) {
				if (block.peek().type() != IdentToken)
					return nullptr;
				const CSSParserToken& ident = block.consume();
				if (!equalLettersIgnoringASCIICase(ident.value(), "of"))
					return nullptr;
				block.consumeWhitespace();
				OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
				*selectorList = consumeComplexSelectorList(block);
                if (selectorList->isEmpty() || !block.atEnd())
					return nullptr;
				selector->setSelectorList(selectorList.release());
			}
			selector->setNth(ab.first, ab.second);
			return selector.release();
		}
		case CSSSelector::PseudoClassLang: {
			// FIXME: CSS Selectors Level 4 allows :lang(*-foo)
			OwnPtr<Vector<AtomicString>> argumentList = adoptPtr(new Vector<AtomicString>());
			if (!consumeLangArgumentList(argumentList, block))
				return nullptr;
			selector->setArgumentList(argumentList.release());
			return selector.release();
		}
	    case CSSSelector::PseudoClassIs:
		case CSSSelector::PseudoClassMatches:
        case CSSSelector::PseudoClassWhere: {
        	SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
			OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
            *selectorList = consumeForgivingComplexSelectorList(block);
            if (!block.atEnd())
				return nullptr;
			selector->setSelectorList(selectorList.release());
			return selector.release();
		}
		case CSSSelector::PseudoClassAny:
		case CSSSelector::PseudoClassHost: {
			OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
			*selectorList = consumeCompoundSelectorList(block);
            if (selectorList->isEmpty() || !block.atEnd())
				return nullptr;
			selector->setSelectorList(selectorList.release());
			return selector.release();
		}
        case CSSSelector::PseudoClassHas: {
            OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
            *selectorList = consumeForgivingRelativeSelectorList(block);
            if (selectorList->isEmpty() || !block.atEnd())
                return nullptr;
            selector->setSelectorList(selectorList.release());
            return selector.release();
        }
		case CSSSelector::PseudoClassDir:
		case CSSSelector::PseudoClassRole: {
			const CSSParserToken& ident = block.consumeIncludingWhitespace();
			if (ident.type() != IdentToken || !block.atEnd())
				return nullptr;
			selector->setArgument(ident.value().toAtomicString());
			return selector.release();
		}
		default:
			break;
		}
    }

    if (selector->match() == CSSSelector::PseudoElement) {
		switch (selector->pseudoElementType()) {
		case CSSSelector::PseudoElementCue: {
			OwnPtr<CSSSelectorList> selectorList = adoptPtr(new CSSSelectorList());
			*selectorList = consumeCompoundSelectorList(block);
            if (selectorList->isEmpty() || !block.atEnd())
				return nullptr;
			selector->setSelectorList(selectorList.release());
			return selector.release();
		}
        case CSSSelector::PseudoElementHighlight: {
            auto& ident = block.consumeIncludingWhitespace();
            if (ident.type() != IdentToken || !block.atEnd())
                return nullptr;

            OwnPtr<Vector<AtomicString>> argumentList = adoptPtr(new Vector<AtomicString>());
            argumentList->append(ident.value().toAtomicString());
            selector->setArgumentList(argumentList.release());

            return selector.release();
        }
        case CSSSelector::PseudoElementPart: {
            OwnPtr<Vector<AtomicString>> argumentList = adoptPtr(new Vector<AtomicString>());
            do {
                auto& ident = block.consumeIncludingWhitespace();
                if (ident.type() != IdentToken)
                    return nullptr;
                argumentList->append(ident.value().toAtomicString());
            } while (!block.atEnd());

            selector->setArgumentList(argumentList.release());
            return selector.release();
        }
		case CSSSelector::PseudoElementSlotted: {
			OwnPtr<CSSParserSelector> innerSelector = consumeCompoundSelector(block);
			block.consumeWhitespace();
			if (!innerSelector || !block.atEnd())
				return nullptr;
			Vector<OwnPtr<CSSParserSelector>> selectorVector;
			selectorVector.append(innerSelector.release());
			selector->adoptSelectorVector(WTFMove(selectorVector));
			return selector.release();
		}
		default:
			break;
		}
    }

    return nullptr;
}

CSSSelector::RelationType CSSSelectorParser::consumeCombinator(CSSParserTokenRange& range)
{
    CSSSelector::RelationType fallbackResult = CSSSelector::Subselector;
    while (range.peek().type() == WhitespaceToken) {
        range.consume();
        fallbackResult = CSSSelector::DescendantSpace;
    }

    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;
    }

    return fallbackResult;
}

CSSSelector::Match CSSSelectorParser::consumeAttributeMatch(CSSParserTokenRange& range)
{
    const CSSParserToken& token = range.consumeIncludingWhitespace();
    switch (token.type()) {
    case IncludeMatchToken:
        return CSSSelector::List;
    case DashMatchToken:
        return CSSSelector::Hyphen;
    case PrefixMatchToken:
        return CSSSelector::Begin;
    case SuffixMatchToken:
        return CSSSelector::End;
    case SubstringMatchToken:
        return CSSSelector::Contain;
    case DelimiterToken:
        if (token.delimiter() == '=')
            return CSSSelector::Exact;
        FALLTHROUGH;
    default:
        m_failedParsing = true;
        return CSSSelector::Exact;
    }
}

CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags(CSSParserTokenRange& range)
{
    if (range.peek().type() != IdentToken)
        return CSSSelector::CaseSensitive;
    const CSSParserToken& flag = range.consumeIncludingWhitespace();
    if (equalLettersIgnoringASCIICase(flag.value(), "i"))
        return CSSSelector::CaseInsensitive;
    m_failedParsing = true;
    return CSSSelector::CaseSensitive;
}

// <an+b> token sequences have special serialization rules: https://www.w3.org/TR/css-syntax-3/#serializing-anb
static AtomicString serializeANPlusB(const std::pair<int, int>& ab)
{
    if (!ab.first)
        return AtomicString::number(ab.second);

    StringBuilder builder;

    if (ab.first == -1)
        builder.append('-');
    else if (ab.first != 1)
        builder.appendCharacter(ab.first);
    builder.append('n');

    if (ab.second) {
        if (ab.second > 0)
            builder.append('+');
        builder.appendCharacter(ab.second);
    }

    return builder.toAtomicString();
}

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 (equalLettersIgnoringASCIICase(token.value(), "odd")) {
            result = std::make_pair(2, 1);
            return true;
        }
        if (equalLettersIgnoringASCIICase(token.value(), "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().toString();
    } else if (token.type() == DimensionToken && token.numericValueType() == IntegerValueType) {
        result.first = token.numericValue();
        nString = token.value().toString();
    } else if (token.type() == IdentToken) {
        if (token.value()[0] == '-') {
            result.first = -1;
            nString = token.value().toString().substring(1);
        } else {
            result.first = 1;
            nString = token.value().toString();
        }
    }

    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;
}

const AtomicString& CSSSelectorParser::defaultNamespace() const
{
    if (!m_styleSheet || m_ignoreDefaultNamespace)
        return starAtom;
    return m_styleSheet->defaultNamespace();
}

const AtomicString& CSSSelectorParser::determineNamespace(const AtomicString& prefix)
{
    if (prefix.isNull())
        return defaultNamespace();
    if (prefix.isEmpty())
        return emptyAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
    if (prefix == starAtom)
        return starAtom; // We'll match any namespace.
    if (!m_styleSheet)
        return nullAtom; // Cannot resolve prefix to namespace without a stylesheet, syntax error.
    return m_styleSheet->namespaceURIFromPrefix(prefix);
}

void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
{
    bool isShadowDOM = compoundSelector->needsImplicitShadowCombinatorForMatching();

    if (elementName.isNull() && defaultNamespace() == starAtom && !isShadowDOM)
        return;

    AtomicString determinedElementName = elementName.isNull() ? starAtom : elementName;
    AtomicString namespaceURI = determineNamespace(namespacePrefix);
    if (namespaceURI.isNull()) {
        m_failedParsing = true;
        return;
    }
    AtomicString determinedPrefix = namespacePrefix;
    if (namespaceURI == defaultNamespace())
        determinedPrefix = nullAtom;
    QualifiedName tag = QualifiedName(determinedPrefix, determinedElementName, namespaceURI);

    // *:host never matches, so we can't discard the *,
    // otherwise we can't tell the difference between *:host and just :host.
    //
    // Also, selectors where we use a ShadowPseudo combinator between the
    // element and the pseudo element for matching (custom pseudo elements,
    // ::cue), we need a universal selector to set the combinator
    // (relation) on in the cases where there are no simple selectors preceding
    // the pseudo element.
    bool isHostPseudo = compoundSelector->isHostPseudoSelector();
	if (isHostPseudo && elementName.isNull() && namespacePrefix.isNull())
		return;
	if (tag != anyQName() || isHostPseudo || isShadowDOM)
		compoundSelector->prependTagSelector(tag, determinedPrefix == nullAtom && determinedElementName == starAtom && !isHostPseudo);
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(PassOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpleSelector)
{
    compoundSelector->appendTagHistory(CSSSelector::Subselector, simpleSelector);
    return compoundSelector;
}

PassOwnPtr<CSSParserSelector> CSSSelectorParser::splitCompoundAtImplicitShadowCrossingCombinator(PassOwnPtr<CSSParserSelector> compoundSelector, const CSSParserContext& context)
{
    // The tagHistory is a linked list that stores combinator separated compound selectors
    // from right-to-left. Yet, within a single compound selector, stores the simple selectors
    // from left-to-right.
    //
    // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each element in the
    // list stored with an associated relation (combinator or SubSelector).
    //
    // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo combinator
    // to their left, which really makes for a new compound selector, yet it's consumed by
    // the selector parser as a single compound selector.
    //
    // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input, #x ]
    //
    CSSParserSelector* splitAfter = compoundSelector.get();
    while (splitAfter->tagHistory() && !splitAfter->tagHistory()->needsImplicitShadowCombinatorForMatching())
        splitAfter = splitAfter->tagHistory();

    if (!splitAfter || !splitAfter->tagHistory())
        return compoundSelector;

    // ::part() combines with other pseudo elements.
    bool isPart = splitAfter->tagHistory()->match() == CSSSelector::PseudoElement && splitAfter->tagHistory()->pseudoElementType() == CSSSelector::PseudoElementPart;

    // ::slotted() combines with other pseudo elements.
    bool isSlotted = splitAfter->tagHistory()->match() == CSSSelector::PseudoElement && splitAfter->tagHistory()->pseudoElementType() == CSSSelector::PseudoElementSlotted;

    OwnPtr<CSSParserSelector> secondCompound;
    if (context.mode == UASheetMode || isPart) {
		// FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
		// We have to recur, since we have rules in media controls like video::a::b. This should not be allowed, and
		// we should remove this recursion once those rules are gone.
        secondCompound = splitCompoundAtImplicitShadowCrossingCombinator(splitAfter->releaseTagHistory(), context);
    } else
        secondCompound = splitAfter->releaseTagHistory();

    auto relation = [&] {
        if (isSlotted)
            return CSSSelector::ShadowSlotted;
        if (isPart)
            return CSSSelector::ShadowPartDescendant;
        return CSSSelector::ShadowDescendant;
    }();
    secondCompound->appendTagHistory(relation, compoundSelector);
    return secondCompound.release();
}

bool CSSSelectorParser::containsUnknownWebKitPseudoElements(const CSSSelector& complexSelector)
{
    for (auto current = &complexSelector; current; current = current->tagHistory()) {
        if (current->match() == CSSSelector::PseudoElement) {
            if (current->pseudoElementType() != CSSSelector::PseudoElementWebKitCustom)
                continue;

            if (parsePseudoElementString(StringView(current->value())) == CSSSelector::PseudoElementUnknown)
                return true;
        }
    }

    return false;
}

} // namespace WebCore
