diff options
Diffstat (limited to 'Source/WebCore/dom/SelectorQuery.cpp')
-rw-r--r-- | Source/WebCore/dom/SelectorQuery.cpp | 483 |
1 files changed, 374 insertions, 109 deletions
diff --git a/Source/WebCore/dom/SelectorQuery.cpp b/Source/WebCore/dom/SelectorQuery.cpp index c8f9dd8cc..f49bc30c5 100644 --- a/Source/WebCore/dom/SelectorQuery.cpp +++ b/Source/WebCore/dom/SelectorQuery.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013, 2014, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,64 +28,152 @@ #include "SelectorQuery.h" #include "CSSParser.h" -#include "ElementIterator.h" +#include "ElementDescendantIterator.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" #include "SelectorChecker.h" -#include "SelectorCheckerFastPath.h" #include "StaticNodeList.h" #include "StyledElement.h" namespace WebCore { -void SelectorDataList::initialize(const CSSSelectorList& selectorList) +#if !ASSERT_DISABLED +static bool isSingleTagNameSelector(const CSSSelector& selector) +{ + return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag; +} + +static bool isSingleClassNameSelector(const CSSSelector& selector) { - ASSERT(m_selectors.isEmpty()); + 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, SelectorCheckerFastPath::canUse(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 { - if (selectorData.isFastCheckable && !element.isSVGElement()) { - SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, &element); - if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled)) - return false; - return selectorCheckerFastPath.matches(); - } + 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); +} - SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); - SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, &element, SelectorChecker::VisitedMatchDisabled); +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; - PseudoId ignoreDynamicPseudo = NOPSEUDO; - return selectorChecker.match(selectorCheckingContext, ignoreDynamicPseudo); + unsigned ignoredSpecificity; + if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity)) + return nullptr; + return &element; } bool SelectorDataList::matches(Element& targetElement) const { - unsigned selectorCount = m_selectors.size(); - for (unsigned i = 0; i < selectorCount; ++i) { - if (selectorMatches(m_selectors[i], targetElement, targetElement)) + 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); } }; -RefPtr<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const +Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const { Vector<Ref<Element>> result; execute<AllElementExtractorSelectorQueryTrait>(rootNode, result); - return StaticElementList::adopt(result); + return StaticElementList::create(WTFMove(result)); } struct SingleElementExtractorSelectorQueryTrait { @@ -107,15 +196,15 @@ Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector) { - if (!rootNode.inDocument()) + if (!rootNode.isConnected()) return nullptr; if (rootNode.document().inQuirksMode()) return nullptr; for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) { - if (selector->m_match == CSSSelector::Id) + if (canBeUsedForIdFastPath(*selector)) return selector; - if (selector->relation() != CSSSelector::SubSelector) + if (selector->relation() != CSSSelector::Subselector) break; } @@ -137,12 +226,10 @@ ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Containe if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) { const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch); ASSERT(elements); - size_t count = elements->size(); bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode); - for (size_t i = 0; i < count; ++i) { - Element& element = *elements->at(i); - if ((rootNodeIsTreeScopeRoot || element.isDescendantOf(&rootNode)) && selectorMatches(selectorData, element, rootNode)) { - SelectorQueryTrait::appendOutputForElement(output, &element); + for (auto& element : *elements) { + if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) { + SelectorQueryTrait::appendOutputForElement(output, element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } @@ -151,25 +238,78 @@ ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Containe } Element* element = rootNode.treeScope().getElementById(idToMatch); - if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) + if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorMatches(selectorData, *element, rootNode)) SelectorQueryTrait::appendOutputForElement(output, element); } -static bool isSingleTagNameSelector(const CSSSelector& selector) +static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector) { - return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Tag; + 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 AtomicString& 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 AtomicString& localName, const AtomicString& 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 AtomicString& localName, typename SelectorQueryTrait::OutputType& output) +static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomicString& localName, const AtomicString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output) { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (element.tagQName().localName() == localName) { - SelectorQueryTrait::appendOutputForElement(output, &element); - if (SelectorQueryTrait::shouldOnlyMatchFirstElement) + 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; + } } } } @@ -177,7 +317,7 @@ static inline void elementsForLocalName(const ContainerNode& rootNode, const Ato template <typename SelectorQueryTrait> static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; @@ -193,20 +333,21 @@ ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const Cont const QualifiedName& tagQualifiedName = selectorData.selector->tagQName(); const AtomicString& selectorLocalName = tagQualifiedName.localName(); + const AtomicString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName(); const AtomicString& selectorNamespaceURI = tagQualifiedName.namespaceURI(); if (selectorNamespaceURI == starAtom) { if (selectorLocalName != starAtom) { // Common case: name defined, selectorNamespaceURI is a wildcard. - elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, output); + 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 : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (element.namespaceURI() == selectorNamespaceURI && (selectorLocalName == starAtom || element.tagQName().localName() == selectorLocalName)) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; @@ -215,11 +356,6 @@ ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const Cont } } -static bool isSingleClassNameSelector(const CSSSelector& selector) -{ - return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Class; -} - template <typename SelectorQueryTrait> ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const { @@ -227,7 +363,7 @@ ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const Co ASSERT(isSingleClassNameSelector(*selectorData.selector)); const AtomicString& className = selectorData.selector->value(); - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { if (element.hasClass() && element.classNames().contains(className)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) @@ -237,11 +373,11 @@ ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const Co } template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const +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 : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) { if (selectorMatches(selectorData, element, rootNode)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) @@ -253,10 +389,9 @@ ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNo template <typename SelectorQueryTrait> ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { - unsigned selectorCount = m_selectors.size(); - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - for (unsigned i = 0; i < selectorCount; ++i) { - if (selectorMatches(m_selectors[i], element, rootNode)) { + 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; @@ -268,9 +403,14 @@ ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const Contai #if ENABLE(CSS_SELECTOR_JIT) template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& rootNode, SelectorCompiler::SimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output) const +ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + 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) @@ -280,98 +420,223 @@ ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const } template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithContext(const ContainerNode& rootNode, SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker, const SelectorCompiler::CheckingContext& context, typename SelectorQueryTrait::OutputType& output) const +ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (selectorChecker(&element, &context)) { + 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, const ContainerNode& rootNode) +{ + if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled) + return isCompiledSelector(selectorData.compilationStatus); + + JSC::VM& vm = rootNode.document().scriptExecutionContext()->vm(); + selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, &vm, 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 { - if (m_selectors.size() == 1) { - const SelectorData& selectorData = m_selectors[0]; - if (const CSSSelector* idSelector = selectorForIdLookup(rootNode, *selectorData.selector)) - executeFastPathForIdSelector<SelectorQueryTrait>(rootNode, selectorData, idSelector, output); - else if (isSingleTagNameSelector(*selectorData.selector)) - executeSingleTagNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); - else if (isSingleClassNameSelector(*selectorData.selector)) - executeSingleClassNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); - else { + 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) - void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress(); - if (!compiledSelectorChecker && selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled) { - JSC::VM* vm = rootNode.document().scriptExecutionContext()->vm(); - selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, vm, selectorData.compiledSelectorCodeRef); - } + if (compileSelector(selectorData, *searchRootNode)) + goto CompiledSingleCase; +#endif // ENABLE(CSS_SELECTOR_JIT) + goto SingleSelectorCase; + ASSERT_NOT_REACHED(); + } - if (compiledSelectorChecker) { - if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) { - SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus); - executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(rootNode, selectorChecker, output); - } else { - ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); - SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus); - - SelectorCompiler::CheckingContext context; - context.elementStyle = nullptr; - context.resolvingMode = SelectorChecker::QueryingRules; - executeCompiledSelectorCheckerWithContext<SelectorQueryTrait>(rootNode, selectorChecker, context, output); - } - return; + 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, *searchRootNode)) { + 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(); + } - executeSingleSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); +#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); } - return; + 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, *searchRootNode)) { + 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; } - executeSingleMultiSelectorData<SelectorQueryTrait>(rootNode, output); } -SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) - : m_selectorList(selectorList) +SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList) + : m_selectorList(WTFMove(selectorList)) + , m_selectors(m_selectorList) { - m_selectors.initialize(m_selectorList); } -SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, Document& document, ExceptionCode& ec) +ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document) { auto it = m_entries.find(selectors); if (it != m_entries.end()) - return it->value.get(); + return *it->value; CSSParser parser(document); CSSSelectorList selectorList; parser.parseSelector(selectors, selectorList); - if (!selectorList.first() || selectorList.hasInvalidSelector()) { - ec = SYNTAX_ERR; - return nullptr; - } + if (!selectorList.first() || selectorList.hasInvalidSelector()) + return Exception { SYNTAX_ERR }; - // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. - if (selectorList.selectorsNeedNamespaceResolution()) { - ec = NAMESPACE_ERR; - return nullptr; - } + if (selectorList.selectorsNeedNamespaceResolution()) + return Exception { SYNTAX_ERR }; const int maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); - - return m_entries.add(selectors, std::make_unique<SelectorQuery>(selectorList)).iterator->value.get(); -} -void SelectorQueryCache::invalidate() -{ - m_entries.clear(); + return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value; } } |