for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            if (inRightmost)
                return IdMatchingType::Rightmost;
            return IdMatchingType::Filter;
        }
        if (selector->relation() != CSSSelector::Subselector)
            inRightmost = false;
    }

/*
 *
 */

#include "config.h"
#include "SelectorQuery.h"

#include "CSSParser.h"
#include "ElementDescendantIterator.h"
#include "HTMLNames.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
#include "StyledElement.h"

namespace WebCore {

#if !ASSERT_DISABLED
static bool isSingleTagNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag;
}

static bool isSingleClassNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class;
}
#endif

enum class IdMatchingType : uint8_t {
    None,
    Rightmost,
    Filter
};

static bool canBeUsedForIdFastPath(const CSSSelector& selector)
{
    return selector.match() == CSSSelector::Id
        || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive());
}

static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
{
    bool inRightmost = true;

    return IdMatchingType::None;
}

SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
{
    unsigned selectorCount = 0;
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        selectorCount++;

    m_selectors.reserveInitialCapacity(selectorCount);
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        m_selectors.uncheckedAppend(SelectorData(selector));

    if (selectorCount == 1) {
        const CSSSelector& selector = *m_selectors.first().selector;
        if (selector.isLastInTagHistory()) {
            switch (selector.match()) {
            case CSSSelector::Tag:
                m_matchType = TagNameMatch;
                break;
            case CSSSelector::Class:
                m_matchType = ClassNameMatch;
                break;
            default:
                if (canBeUsedForIdFastPath(selector))
                    m_matchType = RightMostWithIdMatch;
                else
                    m_matchType = CompilableSingle;
                break;
            }
        } else {
            switch (findIdMatchingType(selector)) {
            case IdMatchingType::None:
                m_matchType = CompilableSingle;
                break;
            case IdMatchingType::Rightmost:
                m_matchType = RightMostWithIdMatch;
                break;
            case IdMatchingType::Filter:
                m_matchType = CompilableSingleWithRootFilter;
                break;
            }
        }
    } else
        m_matchType = CompilableMultipleSelectorMatch;
}

inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity);
}

inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity))
        return nullptr;
    return &element;
}

bool SelectorDataList::matches(Element& targetElement) const
{
    for (auto& selctor : m_selectors) {
        if (selectorMatches(selctor, targetElement, targetElement))
            return true;
    }
    return false;
}

Element* SelectorDataList::closest(Element& targetElement) const
{
    Element* currentNode = &targetElement;
    do {
        for (auto& selector : m_selectors) {
            Element* candidateElement = selectorClosest(selector, *currentNode, targetElement);
            if (candidateElement)
                return candidateElement;
        }
        currentNode = currentNode->parentElement();
    } while (currentNode);
    return nullptr;
}

struct AllElementExtractorSelectorQueryTrait {
    typedef Vector<Ref<Element>> OutputType;
    static const bool shouldOnlyMatchFirstElement = false;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); }
};

Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
{
    Vector<Ref<Element>> result;
    execute<AllElementExtractorSelectorQueryTrait>(rootNode, result);
    return StaticElementList::create(WTFMove(result));
}

struct SingleElementExtractorSelectorQueryTrait {
    typedef Element* OutputType;
    static const bool shouldOnlyMatchFirstElement = true;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element)
    {
        ASSERT(element);
        ASSERT(!output);
        output = element;
    }
};

Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const
{
    Element* result = nullptr;
    execute<SingleElementExtractorSelectorQueryTrait>(rootNode, result);
    return result;
}

static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return nullptr;
    if (rootNode.document().inQuirksMode())
        return nullptr;

    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector))
            return selector;
        if (selector->relation() != CSSSelector::Subselector)
            break;
    }

    return nullptr;
}

static inline bool isTreeScopeRoot(const ContainerNode& node)
{
    return node.isDocumentNode() || node.isShadowRoot();
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(idSelector);

    const AtomString& idToMatch = idSelector->value();
    if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
        const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch);
        ASSERT(elements);
        bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode);
        for (auto& element : *elements) {
            if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
        return;
    }

    Element* element = rootNode.treeScope().getElementById(idToMatch);
    if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)))
        return;
    if (selectorMatches(selectorData, *element, rootNode))
        SelectorQueryTrait::appendOutputForElement(output, element);
}

static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return rootNode;
    if (rootNode.document().inQuirksMode())
        return rootNode;

    // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
    // Thus we can skip the rightmost match.
    const CSSSelector* selector = &firstSelector;
    do {
        ASSERT(!canBeUsedForIdFastPath(*selector));
        if (selector->relation() != CSSSelector::Subselector)
            break;
        selector = selector->tagHistory();
    } while (selector);

    bool inAdjacentChain = false;
    for (; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            const AtomString& idToMatch = selector->value();
            if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
                if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
                    if (inAdjacentChain)
                        searchRoot = searchRoot->parentNode();
                    if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode)))
                        return *searchRoot;
                }
            }
        }
        if (selector->relation() == CSSSelector::Subselector)
            continue;
        if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
            inAdjacentChain = true;
        else
            inAdjacentChain = false;
    }
    return rootNode;
}

static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomString& localName, const AtomString& lowercaseLocalName)
{
    if (element.isHTMLElement() && element.document().isHTMLDocument())
        return element.localName() == lowercaseLocalName;
    return element.localName() == localName;

}

template <typename SelectorQueryTrait>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output)
{
    if (localName == lowercaseLocalName) {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.tagQName().localName() == localName) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    } else {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (localNameMatches(element, localName, lowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output)
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        SelectorQueryTrait::appendOutputForElement(output, &element);
        if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
            return;
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleTagNameSelector(*selectorData.selector));

    const QualifiedName& tagQualifiedName = selectorData.selector->tagQName();
    const AtomString& selectorLocalName = tagQualifiedName.localName();
    const AtomString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName();
    const AtomString& selectorNamespaceURI = tagQualifiedName.namespaceURI();

    if (selectorNamespaceURI == starAtom()) {
        if (selectorLocalName != starAtom()) {
            // Common case: name defined, selectorNamespaceURI is a wildcard.
            elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output);
        } else {
            // Other fairly common case: both are wildcards.
            anyElement<SelectorQueryTrait>(rootNode, output);
        }
    } else {
        // Fallback: NamespaceURI is set, selectorLocalName may be starAtom().
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleClassNameSelector(*selectorData.selector));

    const AtomString& className = selectorData.selector->value();
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        if (element.hasClass() && element.classNames().contains(className)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
        if (selectorMatches(selectorData, element, rootNode)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
            if (selectorMatches(selector, element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element, &checkingContext)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
#if CSS_SELECTOR_JIT_PROFILING
            selector.compiledSelectorUsed();
#endif
            bool matched = false;
            void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress();
            if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
                auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element);
            } else {
                ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
                auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element, &checkingContext);
            }
            if (matched) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

static bool isCompiledSelector(SelectorCompilationStatus compilationStatus)
{
    return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext;
}

bool SelectorDataList::compileSelector(const SelectorData& selectorData)
{
    if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
        return isCompiledSelector(selectorData.compilationStatus);

    selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
    return isCompiledSelector(selectorData.compilationStatus);
}

#endif // ENABLE(CSS_SELECTOR_JIT)

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    ContainerNode* searchRootNode = &rootNode;
    switch (m_matchType) {
    case RightMostWithIdMatch:
        {
        const SelectorData& selectorData = m_selectors.first();
        if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
            executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
            break;
        }
#if ENABLE(CSS_SELECTOR_JIT)
        if (compileSelector(selectorData))
            goto CompiledSingleCase;
#endif // ENABLE(CSS_SELECTOR_JIT)
        goto SingleSelectorCase;
        ASSERT_NOT_REACHED();
        }

    case CompilableSingleWithRootFilter:
    case CompilableSingle:
        {
#if ENABLE(CSS_SELECTOR_JIT)
        const SelectorData& selectorData = m_selectors.first();
        ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
        ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter);
        if (compileSelector(selectorData)) {
            if (m_matchType == CompilableSingle) {
                m_matchType = CompiledSingle;
                goto CompiledSingleCase;
            }
            ASSERT(m_matchType == CompilableSingleWithRootFilter);
            m_matchType = CompiledSingleWithRootFilter;
            goto CompiledSingleWithRootFilterCase;
        }
#endif // ENABLE(CSS_SELECTOR_JIT)
        if (m_matchType == CompilableSingle) {
            m_matchType = SingleSelector;
            goto SingleSelectorCase;
        }
        ASSERT(m_matchType == CompilableSingleWithRootFilter);
        m_matchType = SingleSelectorWithRootFilter;
        goto SingleSelectorWithRootFilterCase;
        ASSERT_NOT_REACHED();
        }

#if ENABLE(CSS_SELECTOR_JIT)
    case CompiledSingleWithRootFilter:
        CompiledSingleWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case CompiledSingle:
        {
        CompiledSingleCase:
        const SelectorData& selectorData = m_selectors.first();
        void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
            SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
        } else {
            ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
            SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
        }
        break;
        }
#else
    case CompiledSingleWithRootFilter:
    case CompiledSingle:
        ASSERT_NOT_REACHED();
#if ASSERT_DISABLED
        FALLTHROUGH;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

    case SingleSelectorWithRootFilter:
        SingleSelectorWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case SingleSelector:
        SingleSelectorCase:
        executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output);
        break;

    case TagNameMatch:
        executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case ClassNameMatch:
        executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case CompilableMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        {
        for (auto& selector : m_selectors) {
            if (!compileSelector(selector)) {
                m_matchType = MultipleSelectorMatch;
                goto MultipleSelectorMatch;
            }
        }
        m_matchType = CompiledMultipleSelectorMatch;
        goto CompiledMultipleSelectorMatch;
        }
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case CompiledMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        CompiledMultipleSelectorMatch:
        executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case MultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        MultipleSelectorMatch:
#endif
        executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
    }
}

SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
    : m_selectorList(WTFMove(selectorList))
    , m_selectors(m_selectorList)
{
}

ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document)
{
    if (auto* entry = m_entries.get(selectors))
        return *entry;

    CSSParser parser(document);
    CSSSelectorList selectorList;
    parser.parseSelector(selectors, selectorList);

    if (!selectorList.first() || selectorList.hasInvalidSelector())
        return Exception { SyntaxError };

    if (selectorList.selectorsNeedNamespaceResolution())
        return Exception { SyntaxError };

    const int maximumSelectorQueryCacheSize = 256;
    if (m_entries.size() == maximumSelectorQueryCacheSize)
        m_entries.remove(m_entries.random());

    return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value;
}

}


/*
 *
 */

#include "config.h"
#include "SelectorQuery.h"

#include "CSSParser.h"
#include "ElementDescendantIterator.h"
#include "HTMLNames.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
#include "StyledElement.h"

namespace WebCore {

#if !ASSERT_DISABLED
static bool isSingleTagNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag;
}

static bool isSingleClassNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class;
}
#endif

enum class IdMatchingType : uint8_t {
    None,
    Rightmost,
    Filter
};

static bool canBeUsedForIdFastPath(const CSSSelector& selector)
{
    return selector.match() == CSSSelector::Id
        || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive());
}

static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
{
    bool inRightmost = true;
    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            if (inRightmost)
                return IdMatchingType::Rightmost;
            return IdMatchingType::Filter;
        }
        if (selector->relation() != CSSSelector::Subselector)
            inRightmost = false;
    }
    return IdMatchingType::None;
}

SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
{
    unsigned selectorCount = 0;
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        selectorCount++;

    m_selectors.reserveInitialCapacity(selectorCount);
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        m_selectors.uncheckedAppend(SelectorData(selector));

    if (selectorCount == 1) {
        const CSSSelector& selector = *m_selectors.first().selector;
        if (selector.isLastInTagHistory()) {
            switch (selector.match()) {
            case CSSSelector::Tag:
                m_matchType = TagNameMatch;
                break;
            case CSSSelector::Class:
                m_matchType = ClassNameMatch;
                break;
            default:
                if (canBeUsedForIdFastPath(selector))
                    m_matchType = RightMostWithIdMatch;
                else
                    m_matchType = CompilableSingle;
                break;
            }
        } else {
            switch (findIdMatchingType(selector)) {
            case IdMatchingType::None:
                m_matchType = CompilableSingle;
                break;
            case IdMatchingType::Rightmost:
                m_matchType = RightMostWithIdMatch;
                break;
            case IdMatchingType::Filter:
                m_matchType = CompilableSingleWithRootFilter;
                break;
            }
        }
    } else
        m_matchType = CompilableMultipleSelectorMatch;
}

inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity);
}

inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity))
        return nullptr;
    return &element;
}

bool SelectorDataList::matches(Element& targetElement) const
{
    for (auto& selctor : m_selectors) {
        if (selectorMatches(selctor, targetElement, targetElement))
            return true;
    }
    return false;
}

Element* SelectorDataList::closest(Element& targetElement) const
{
    Element* currentNode = &targetElement;
    do {
        for (auto& selector : m_selectors) {
            Element* candidateElement = selectorClosest(selector, *currentNode, targetElement);
            if (candidateElement)
                return candidateElement;
        }
        currentNode = currentNode->parentElement();
    } while (currentNode);
    return nullptr;
}

struct AllElementExtractorSelectorQueryTrait {
    typedef Vector<Ref<Element>> OutputType;
    static const bool shouldOnlyMatchFirstElement = false;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); }
};

Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
{
    Vector<Ref<Element>> result;
    execute<AllElementExtractorSelectorQueryTrait>(rootNode, result);
    return StaticElementList::create(WTFMove(result));
}

struct SingleElementExtractorSelectorQueryTrait {
    typedef Element* OutputType;
    static const bool shouldOnlyMatchFirstElement = true;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element)
    {
        ASSERT(element);
        ASSERT(!output);
        output = element;
    }
};

Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const
{
    Element* result = nullptr;
    execute<SingleElementExtractorSelectorQueryTrait>(rootNode, result);
    return result;
}

static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return nullptr;
    if (rootNode.document().inQuirksMode())
        return nullptr;

    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector))
            return selector;
        if (selector->relation() != CSSSelector::Subselector)
            break;
    }

    return nullptr;
}

static inline bool isTreeScopeRoot(const ContainerNode& node)
{
    return node.isDocumentNode() || node.isShadowRoot();
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(idSelector);

    const AtomString& idToMatch = idSelector->value();
    if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
        const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch);
        ASSERT(elements);
        bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode);
        for (auto& element : *elements) {
            if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
        return;
    }

    Element* element = rootNode.treeScope().getElementById(idToMatch);
    if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)))
        return;
    if (selectorMatches(selectorData, *element, rootNode))
        SelectorQueryTrait::appendOutputForElement(output, element);
}

static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return rootNode;
    if (rootNode.document().inQuirksMode())
        return rootNode;

    // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
    // Thus we can skip the rightmost match.
    const CSSSelector* selector = &firstSelector;
    do {
        ASSERT(!canBeUsedForIdFastPath(*selector));
        if (selector->relation() != CSSSelector::Subselector)
            break;
        selector = selector->tagHistory();
    } while (selector);

    bool inAdjacentChain = false;
    for (; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            const AtomString& idToMatch = selector->value();
            if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
                if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
                    if (inAdjacentChain)
                        searchRoot = searchRoot->parentNode();
                    if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode)))
                        return *searchRoot;
                }
            }
        }
        if (selector->relation() == CSSSelector::Subselector)
            continue;
        if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
            inAdjacentChain = true;
        else
            inAdjacentChain = false;
    }
    return rootNode;
}

static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomString& localName, const AtomString& lowercaseLocalName)
{
    if (element.isHTMLElement() && element.document().isHTMLDocument())
        return element.localName() == lowercaseLocalName;
    return element.localName() == localName;

}

template <typename SelectorQueryTrait>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output)
{
    if (localName == lowercaseLocalName) {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.tagQName().localName() == localName) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    } else {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (localNameMatches(element, localName, lowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output)
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        SelectorQueryTrait::appendOutputForElement(output, &element);
        if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
            return;
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleTagNameSelector(*selectorData.selector));

    const QualifiedName& tagQualifiedName = selectorData.selector->tagQName();
    const AtomString& selectorLocalName = tagQualifiedName.localName();
    const AtomString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName();
    const AtomString& selectorNamespaceURI = tagQualifiedName.namespaceURI();

    if (selectorNamespaceURI == starAtom()) {
        if (selectorLocalName != starAtom()) {
            // Common case: name defined, selectorNamespaceURI is a wildcard.
            elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output);
        } else {
            // Other fairly common case: both are wildcards.
            anyElement<SelectorQueryTrait>(rootNode, output);
        }
    } else {
        // Fallback: NamespaceURI is set, selectorLocalName may be starAtom().
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleClassNameSelector(*selectorData.selector));

    const AtomString& className = selectorData.selector->value();
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        if (element.hasClass() && element.classNames().contains(className)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
        if (selectorMatches(selectorData, element, rootNode)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
            if (selectorMatches(selector, element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element, &checkingContext)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
#if CSS_SELECTOR_JIT_PROFILING
            selector.compiledSelectorUsed();
#endif
            bool matched = false;
            void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress();
            if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
                auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element);
            } else {
                ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
                auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element, &checkingContext);
            }
            if (matched) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

static bool isCompiledSelector(SelectorCompilationStatus compilationStatus)
{
    return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext;
}

bool SelectorDataList::compileSelector(const SelectorData& selectorData)
{
    if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
        return isCompiledSelector(selectorData.compilationStatus);

    selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
    return isCompiledSelector(selectorData.compilationStatus);
}

#endif // ENABLE(CSS_SELECTOR_JIT)

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    ContainerNode* searchRootNode = &rootNode;
    switch (m_matchType) {
    case RightMostWithIdMatch:
        {
        const SelectorData& selectorData = m_selectors.first();
        if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
            executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
            break;
        }
#if ENABLE(CSS_SELECTOR_JIT)
        if (compileSelector(selectorData))
            goto CompiledSingleCase;
#endif // ENABLE(CSS_SELECTOR_JIT)
        goto SingleSelectorCase;
        ASSERT_NOT_REACHED();
        }

    case CompilableSingleWithRootFilter:
    case CompilableSingle:
        {
#if ENABLE(CSS_SELECTOR_JIT)
        const SelectorData& selectorData = m_selectors.first();
        ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
        ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter);
        if (compileSelector(selectorData)) {
            if (m_matchType == CompilableSingle) {
                m_matchType = CompiledSingle;
                goto CompiledSingleCase;
            }
            ASSERT(m_matchType == CompilableSingleWithRootFilter);
            m_matchType = CompiledSingleWithRootFilter;
            goto CompiledSingleWithRootFilterCase;
        }
#endif // ENABLE(CSS_SELECTOR_JIT)
        if (m_matchType == CompilableSingle) {
            m_matchType = SingleSelector;
            goto SingleSelectorCase;
        }
        ASSERT(m_matchType == CompilableSingleWithRootFilter);
        m_matchType = SingleSelectorWithRootFilter;
        goto SingleSelectorWithRootFilterCase;
        ASSERT_NOT_REACHED();
        }

#if ENABLE(CSS_SELECTOR_JIT)
    case CompiledSingleWithRootFilter:
        CompiledSingleWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case CompiledSingle:
        {
        CompiledSingleCase:
        const SelectorData& selectorData = m_selectors.first();
        void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
            SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
        } else {
            ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
            SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
        }
        break;
        }
#else
    case CompiledSingleWithRootFilter:
    case CompiledSingle:
        ASSERT_NOT_REACHED();
#if ASSERT_DISABLED
        FALLTHROUGH;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

    case SingleSelectorWithRootFilter:
        SingleSelectorWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case Single

Selector:
        SingleSelectorCase:
        executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output);
        break;

    case TagNameMatch:
        executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case ClassNameMatch:
        executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case CompilableMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        {
        for (auto& selector : m_selectors) {
            if (!compileSelector(selector)) {
                m_matchType = MultipleSelectorMatch;
                goto MultipleSelectorMatch;
            }
        }
        m_matchType = CompiledMultipleSelectorMatch;
        goto CompiledMultipleSelectorMatch;
        }
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case CompiledMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        CompiledMultipleSelectorMatch:
        executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case MultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        MultipleSelectorMatch:
#endif
        executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
    }
}

SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
    : m_selectorList(WTFMove(selectorList))
    , m_selectors(m_selectorList)
{
}

ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document)
{
    if (auto* entry = m_entries.get(selectors))
        return *entry;

    CSSParser parser(document);
    CSSSelectorList selectorList;
    parser.parseSelector(selectors, selectorList);

    if (!selectorList.first() || selectorList.hasInvalidSelector())
        return Exception { SyntaxError };

    if (selectorList.selectorsNeedNamespaceResolution())
        return Exception { SyntaxError };

    const int maximumSelectorQueryCacheSize = 256;
    if (m_entries.size() == maximumSelectorQueryCacheSize)
        m_entries.remove(m_entries.random());

    return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value;
}

}


void ProcessorList::deleteProcessor( AProcessor* processor )

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

{
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


{
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p )

void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


{
	ImageEx output( input );

	//Run trough all the widgets

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const

	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	i

	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );

	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, SLOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


/*
	This file is part of Overmix.

*/

#include "ProcessorList.hpp"
#include "planes/ImageEx.hpp"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QPushButton>

#include <algorithm>

using namespace Overmix;

ProcessorList::ProcessorList( QWidget* parent ) : QWidget( parent ){
	main_layout = new QVBoxLayout;
	main_layout->setContentsMargins( 0,0,0,0 );
	setLayout( main_layout );

	add_processor = new QPushButton( "Add", this );
	processor_selector = new QComboBox( this );
	connect( add_processor, SIGNAL(clicked()), this, SLOT(addProcessor()) );

	//Add all the types of processors to the combobox
	for( int i=0; i<factory.amount(); i++ )
		processor_selector->addItem( factory.name( i ) );

	//Add widgets to layout
	auto adder_layout = new QHBoxLayout( this );
	adder_layout->setContentsMargins( 0,0,0,0 );
	adder_layout->addWidget( processor_selector );
	adder_layout->addWidget( add_processor );

	main_layout->addLayout( adder_layout );
}

ImageEx ProcessorList::process( const ImageEx& input ) const{
	ImageEx output( input );

	//Run trough all the widgets
	for( auto processor : processors )
		if( processor->modifiesImage() )
			output = processor->process( output );
	//TODO: Support caching

	return output;
}

Point<double> ProcessorList::modifyOffset( Point<double> offset ) const{
	for( auto processor : processors )
		offset = processor->modifyOffset( offset );
	return offset;
}

int ProcessorList::indexOf( AProcessor* p ) const{
	auto it = std::find( processors.begin(), processors.end(), p );
	if( it == processors.end() )
		throw std::runtime_error( "AProcessor not in ProcessList" );
	return it - processors.begin();
}

void ProcessorList::addProcessor(){
	auto widget = factory.create( processor_selector->currentIndex(), this ).release();
	processors.push_back( widget );
	layout()->addWidget( widget );
	connect( widget, SIGNAL(closed(  AProcessor*)), this, SLOT(deleteProcessor(  AProcessor*)) );
	connect( widget, SIGNAL(moveUp(  AProcessor*)), this, S

LOT(moveProcessorUp(  AProcessor*)) );
	connect( widget, SIGNAL(moveDown(AProcessor*)), this, SLOT(moveProcessorDown(AProcessor*)) );
}

void ProcessorList::moveProcessorUp( AProcessor* p ){
	int index = indexOf( p );
	if( index > 0 ){
		std::swap( processors.at(index-1), processors.at(index) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index-1+1 ) );
	}
}
void ProcessorList::moveProcessorDown( AProcessor* p ){
	int index = indexOf( p );
	if( unsigned(index) < processors.size()-1 ){
		std::swap( processors.at(index), processors.at(index+1) );
		//NOTE: Layout is one off due to the button-layout is the first index
		main_layout->insertItem( index+1, main_layout->takeAt( index+1+1 ) );
	}
}

void ProcessorList::deleteProcessor( AProcessor* processor ){
	//Remove from our pipeline
	auto new_end = std::remove( processors.begin(), processors.end(), processor );
	processors.erase( new_end, processors.end() );

	//Remove the widget from the UI
	delete processor;
}


std::string FormatMoney(const CAmount& n)

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


{
    return ParseMoney(str.c_str(), nRet);
}

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)

    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;

    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet)

}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


if (!IsDigit(*p))
            return false;

#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;

        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;

    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we do NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;

    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}


#include <util/moneystr.h>

#include <primitives/transaction.h>
#include <tinyformat.h>
#include <util/strencodings.h>

std::string FormatMoney(const CAmount& n)
{
    // Note: not using straight sprintf here because we d

o NOT want
    // localized number formatting.
    int64_t n_abs = (n > 0 ? n : -n);
    int64_t quotient = n_abs/COIN;
    int64_t remainder = n_abs%COIN;
    std::string str = strprintf("%d.%08d", quotient, remainder);

    // Right-trim excess zeros before the decimal point:
    int nTrim = 0;
    for (int i = str.size()-1; (str[i] == '0' && IsDigit(str[i-2])); --i)
        ++nTrim;
    if (nTrim)
        str.erase(str.size()-nTrim, nTrim);

    if (n < 0)
        str.insert((unsigned int)0, 1, '-');
    return str;
}

bool ParseMoney(const std::string& str, CAmount& nRet)
{
    return ParseMoney(str.c_str(), nRet);
}

bool ParseMoney(const char* pszIn, CAmount& nRet)
{
    std::string strWhole;
    int64_t nUnits = 0;
    const char* p = pszIn;
    while (IsSpace(*p))
        p++;
    for (; *p; p++)
    {
        if (*p == '.')
        {
            p++;
            int64_t nMult = COIN / 10;
            while (IsDigit(*p) && (nMult > 0))
            {
                nUnits += nMult * (*p++ - '0');
                nMult /= 10;
            }
            break;
        }
        if (IsSpace(*p))
            break;
        if (!IsDigit(*p))
            return false;
        strWhole.insert(strWhole.end(), *p);
    }
    for (; *p; p++)
        if (!IsSpace(*p))
            return false;
    if (strWhole.size() > 10) // guard against 63 bit overflow
        return false;
    if (nUnits < 0 || nUnits > COIN)
        return false;
    int64_t nWhole = atoi64(strWhole);
    CAmount nValue = nWhole*COIN + nUnits;

    nRet = nValue;
    return true;
}