summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/SelectorQuery.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/SelectorQuery.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/dom/SelectorQuery.cpp')
-rw-r--r--Source/WebCore/dom/SelectorQuery.cpp483
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;
}
}