1/*
2 * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "SelectorQuery.h"
29
30#include "CSSParser.h"
31#include "ElementDescendantIterator.h"
32#include "HTMLNames.h"
33#include "SelectorChecker.h"
34#include "StaticNodeList.h"
35#include "StyledElement.h"
36
37namespace WebCore {
38
39#if !ASSERT_DISABLED
40static bool isSingleTagNameSelector(const CSSSelector& selector)
41{
42 return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag;
43}
44
45static bool isSingleClassNameSelector(const CSSSelector& selector)
46{
47 return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class;
48}
49#endif
50
51enum class IdMatchingType : uint8_t {
52 None,
53 Rightmost,
54 Filter
55};
56
57static bool canBeUsedForIdFastPath(const CSSSelector& selector)
58{
59 return selector.match() == CSSSelector::Id
60 || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive());
61}
62
63static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
64{
65 bool inRightmost = true;
66 for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
67 if (canBeUsedForIdFastPath(*selector)) {
68 if (inRightmost)
69 return IdMatchingType::Rightmost;
70 return IdMatchingType::Filter;
71 }
72 if (selector->relation() != CSSSelector::Subselector)
73 inRightmost = false;
74 }
75 return IdMatchingType::None;
76}
77
78SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
79{
80 unsigned selectorCount = 0;
81 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
82 selectorCount++;
83
84 m_selectors.reserveInitialCapacity(selectorCount);
85 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
86 m_selectors.uncheckedAppend(SelectorData(selector));
87
88 if (selectorCount == 1) {
89 const CSSSelector& selector = *m_selectors.first().selector;
90 if (selector.isLastInTagHistory()) {
91 switch (selector.match()) {
92 case CSSSelector::Tag:
93 m_matchType = TagNameMatch;
94 break;
95 case CSSSelector::Class:
96 m_matchType = ClassNameMatch;
97 break;
98 default:
99 if (canBeUsedForIdFastPath(selector))
100 m_matchType = RightMostWithIdMatch;
101 else
102 m_matchType = CompilableSingle;
103 break;
104 }
105 } else {
106 switch (findIdMatchingType(selector)) {
107 case IdMatchingType::None:
108 m_matchType = CompilableSingle;
109 break;
110 case IdMatchingType::Rightmost:
111 m_matchType = RightMostWithIdMatch;
112 break;
113 case IdMatchingType::Filter:
114 m_matchType = CompilableSingleWithRootFilter;
115 break;
116 }
117 }
118 } else
119 m_matchType = CompilableMultipleSelectorMatch;
120}
121
122inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
123{
124 SelectorChecker selectorChecker(element.document());
125 SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
126 selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
127 unsigned ignoredSpecificity;
128 return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity);
129}
130
131inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
132{
133 SelectorChecker selectorChecker(element.document());
134 SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
135 selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
136 unsigned ignoredSpecificity;
137 if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity))
138 return nullptr;
139 return &element;
140}
141
142bool SelectorDataList::matches(Element& targetElement) const
143{
144 for (auto& selctor : m_selectors) {
145 if (selectorMatches(selctor, targetElement, targetElement))
146 return true;
147 }
148 return false;
149}
150
151Element* SelectorDataList::closest(Element& targetElement) const
152{
153 Element* currentNode = &targetElement;
154 do {
155 for (auto& selector : m_selectors) {
156 Element* candidateElement = selectorClosest(selector, *currentNode, targetElement);
157 if (candidateElement)
158 return candidateElement;
159 }
160 currentNode = currentNode->parentElement();
161 } while (currentNode);
162 return nullptr;
163}
164
165struct AllElementExtractorSelectorQueryTrait {
166 typedef Vector<Ref<Element>> OutputType;
167 static const bool shouldOnlyMatchFirstElement = false;
168 ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); }
169};
170
171Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
172{
173 Vector<Ref<Element>> result;
174 execute<AllElementExtractorSelectorQueryTrait>(rootNode, result);
175 return StaticElementList::create(WTFMove(result));
176}
177
178struct SingleElementExtractorSelectorQueryTrait {
179 typedef Element* OutputType;
180 static const bool shouldOnlyMatchFirstElement = true;
181 ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element)
182 {
183 ASSERT(element);
184 ASSERT(!output);
185 output = element;
186 }
187};
188
189Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const
190{
191 Element* result = nullptr;
192 execute<SingleElementExtractorSelectorQueryTrait>(rootNode, result);
193 return result;
194}
195
196static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector)
197{
198 if (!rootNode.isConnected())
199 return nullptr;
200 if (rootNode.document().inQuirksMode())
201 return nullptr;
202
203 for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
204 if (canBeUsedForIdFastPath(*selector))
205 return selector;
206 if (selector->relation() != CSSSelector::Subselector)
207 break;
208 }
209
210 return nullptr;
211}
212
213static inline bool isTreeScopeRoot(const ContainerNode& node)
214{
215 return node.isDocumentNode() || node.isShadowRoot();
216}
217
218template <typename SelectorQueryTrait>
219ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const
220{
221 ASSERT(m_selectors.size() == 1);
222 ASSERT(idSelector);
223
224 const AtomString& idToMatch = idSelector->value();
225 if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
226 const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch);
227 ASSERT(elements);
228 bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode);
229 for (auto& element : *elements) {
230 if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) {
231 SelectorQueryTrait::appendOutputForElement(output, element);
232 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
233 return;
234 }
235 }
236 return;
237 }
238
239 Element* element = rootNode.treeScope().getElementById(idToMatch);
240 if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)))
241 return;
242 if (selectorMatches(selectorData, *element, rootNode))
243 SelectorQueryTrait::appendOutputForElement(output, element);
244}
245
246static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
247{
248 if (!rootNode.isConnected())
249 return rootNode;
250 if (rootNode.document().inQuirksMode())
251 return rootNode;
252
253 // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
254 // Thus we can skip the rightmost match.
255 const CSSSelector* selector = &firstSelector;
256 do {
257 ASSERT(!canBeUsedForIdFastPath(*selector));
258 if (selector->relation() != CSSSelector::Subselector)
259 break;
260 selector = selector->tagHistory();
261 } while (selector);
262
263 bool inAdjacentChain = false;
264 for (; selector; selector = selector->tagHistory()) {
265 if (canBeUsedForIdFastPath(*selector)) {
266 const AtomString& idToMatch = selector->value();
267 if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
268 if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
269 if (inAdjacentChain)
270 searchRoot = searchRoot->parentNode();
271 if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode)))
272 return *searchRoot;
273 }
274 }
275 }
276 if (selector->relation() == CSSSelector::Subselector)
277 continue;
278 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
279 inAdjacentChain = true;
280 else
281 inAdjacentChain = false;
282 }
283 return rootNode;
284}
285
286static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomString& localName, const AtomString& lowercaseLocalName)
287{
288 if (element.isHTMLElement() && element.document().isHTMLDocument())
289 return element.localName() == lowercaseLocalName;
290 return element.localName() == localName;
291
292}
293
294template <typename SelectorQueryTrait>
295static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output)
296{
297 if (localName == lowercaseLocalName) {
298 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
299 if (element.tagQName().localName() == localName) {
300 SelectorQueryTrait::appendOutputForElement(output, &element);
301 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
302 return;
303 }
304 }
305 } else {
306 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
307 if (localNameMatches(element, localName, lowercaseLocalName)) {
308 SelectorQueryTrait::appendOutputForElement(output, &element);
309 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
310 return;
311 }
312 }
313 }
314}
315
316template <typename SelectorQueryTrait>
317static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output)
318{
319 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
320 SelectorQueryTrait::appendOutputForElement(output, &element);
321 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
322 return;
323 }
324}
325
326
327template <typename SelectorQueryTrait>
328ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
329{
330 ASSERT(m_selectors.size() == 1);
331 ASSERT(isSingleTagNameSelector(*selectorData.selector));
332
333 const QualifiedName& tagQualifiedName = selectorData.selector->tagQName();
334 const AtomString& selectorLocalName = tagQualifiedName.localName();
335 const AtomString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName();
336 const AtomString& selectorNamespaceURI = tagQualifiedName.namespaceURI();
337
338 if (selectorNamespaceURI == starAtom()) {
339 if (selectorLocalName != starAtom()) {
340 // Common case: name defined, selectorNamespaceURI is a wildcard.
341 elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output);
342 } else {
343 // Other fairly common case: both are wildcards.
344 anyElement<SelectorQueryTrait>(rootNode, output);
345 }
346 } else {
347 // Fallback: NamespaceURI is set, selectorLocalName may be starAtom().
348 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
349 if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) {
350 SelectorQueryTrait::appendOutputForElement(output, &element);
351 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
352 return;
353 }
354 }
355 }
356}
357
358template <typename SelectorQueryTrait>
359ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
360{
361 ASSERT(m_selectors.size() == 1);
362 ASSERT(isSingleClassNameSelector(*selectorData.selector));
363
364 const AtomString& className = selectorData.selector->value();
365 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
366 if (element.hasClass() && element.classNames().contains(className)) {
367 SelectorQueryTrait::appendOutputForElement(output, &element);
368 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
369 return;
370 }
371 }
372}
373
374template <typename SelectorQueryTrait>
375ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
376{
377 ASSERT(m_selectors.size() == 1);
378
379 for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
380 if (selectorMatches(selectorData, element, rootNode)) {
381 SelectorQueryTrait::appendOutputForElement(output, &element);
382 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
383 return;
384 }
385 }
386}
387
388template <typename SelectorQueryTrait>
389ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
390{
391 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
392 for (auto& selector : m_selectors) {
393 if (selectorMatches(selector, element, rootNode)) {
394 SelectorQueryTrait::appendOutputForElement(output, &element);
395 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
396 return;
397 break;
398 }
399 }
400 }
401}
402
403#if ENABLE(CSS_SELECTOR_JIT)
404template <typename SelectorQueryTrait>
405ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
406{
407 for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
408#if CSS_SELECTOR_JIT_PROFILING
409 selectorData.compiledSelectorUsed();
410#else
411 UNUSED_PARAM(selectorData);
412#endif
413 if (selectorChecker(&element)) {
414 SelectorQueryTrait::appendOutputForElement(output, &element);
415 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
416 return;
417 }
418 }
419}
420
421template <typename SelectorQueryTrait>
422ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
423{
424 SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
425 checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
426
427 for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
428#if CSS_SELECTOR_JIT_PROFILING
429 selectorData.compiledSelectorUsed();
430#else
431 UNUSED_PARAM(selectorData);
432#endif
433 if (selectorChecker(&element, &checkingContext)) {
434 SelectorQueryTrait::appendOutputForElement(output, &element);
435 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
436 return;
437 }
438 }
439}
440
441template <typename SelectorQueryTrait>
442ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
443{
444 SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
445 checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
446 for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
447 for (auto& selector : m_selectors) {
448#if CSS_SELECTOR_JIT_PROFILING
449 selector.compiledSelectorUsed();
450#endif
451 bool matched = false;
452 void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress();
453 if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
454 auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus);
455 matched = selectorChecker(&element);
456 } else {
457 ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
458 auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus);
459 matched = selectorChecker(&element, &checkingContext);
460 }
461 if (matched) {
462 SelectorQueryTrait::appendOutputForElement(output, &element);
463 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
464 return;
465 break;
466 }
467 }
468 }
469}
470
471static bool isCompiledSelector(SelectorCompilationStatus compilationStatus)
472{
473 return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext;
474}
475
476bool SelectorDataList::compileSelector(const SelectorData& selectorData)
477{
478 if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
479 return isCompiledSelector(selectorData.compilationStatus);
480
481 selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
482 return isCompiledSelector(selectorData.compilationStatus);
483}
484
485
486#endif // ENABLE(CSS_SELECTOR_JIT)
487
488template <typename SelectorQueryTrait>
489ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
490{
491 ContainerNode* searchRootNode = &rootNode;
492 switch (m_matchType) {
493 case RightMostWithIdMatch:
494 {
495 const SelectorData& selectorData = m_selectors.first();
496 if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
497 executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
498 break;
499 }
500#if ENABLE(CSS_SELECTOR_JIT)
501 if (compileSelector(selectorData))
502 goto CompiledSingleCase;
503#endif // ENABLE(CSS_SELECTOR_JIT)
504 goto SingleSelectorCase;
505 ASSERT_NOT_REACHED();
506 }
507
508 case CompilableSingleWithRootFilter:
509 case CompilableSingle:
510 {
511#if ENABLE(CSS_SELECTOR_JIT)
512 const SelectorData& selectorData = m_selectors.first();
513 ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
514 ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter);
515 if (compileSelector(selectorData)) {
516 if (m_matchType == CompilableSingle) {
517 m_matchType = CompiledSingle;
518 goto CompiledSingleCase;
519 }
520 ASSERT(m_matchType == CompilableSingleWithRootFilter);
521 m_matchType = CompiledSingleWithRootFilter;
522 goto CompiledSingleWithRootFilterCase;
523 }
524#endif // ENABLE(CSS_SELECTOR_JIT)
525 if (m_matchType == CompilableSingle) {
526 m_matchType = SingleSelector;
527 goto SingleSelectorCase;
528 }
529 ASSERT(m_matchType == CompilableSingleWithRootFilter);
530 m_matchType = SingleSelectorWithRootFilter;
531 goto SingleSelectorWithRootFilterCase;
532 ASSERT_NOT_REACHED();
533 }
534
535#if ENABLE(CSS_SELECTOR_JIT)
536 case CompiledSingleWithRootFilter:
537 CompiledSingleWithRootFilterCase:
538 searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
539 FALLTHROUGH;
540 case CompiledSingle:
541 {
542 CompiledSingleCase:
543 const SelectorData& selectorData = m_selectors.first();
544 void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
545 if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
546 SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
547 executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
548 } else {
549 ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
550 SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
551 executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
552 }
553 break;
554 }
555#else
556 case CompiledSingleWithRootFilter:
557 case CompiledSingle:
558 ASSERT_NOT_REACHED();
559#if ASSERT_DISABLED
560 FALLTHROUGH;
561#endif
562#endif // ENABLE(CSS_SELECTOR_JIT)
563
564 case SingleSelectorWithRootFilter:
565 SingleSelectorWithRootFilterCase:
566 searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
567 FALLTHROUGH;
568 case SingleSelector:
569 SingleSelectorCase:
570 executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output);
571 break;
572
573 case TagNameMatch:
574 executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
575 break;
576 case ClassNameMatch:
577 executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
578 break;
579 case CompilableMultipleSelectorMatch:
580#if ENABLE(CSS_SELECTOR_JIT)
581 {
582 for (auto& selector : m_selectors) {
583 if (!compileSelector(selector)) {
584 m_matchType = MultipleSelectorMatch;
585 goto MultipleSelectorMatch;
586 }
587 }
588 m_matchType = CompiledMultipleSelectorMatch;
589 goto CompiledMultipleSelectorMatch;
590 }
591#else
592 FALLTHROUGH;
593#endif // ENABLE(CSS_SELECTOR_JIT)
594 case CompiledMultipleSelectorMatch:
595#if ENABLE(CSS_SELECTOR_JIT)
596 CompiledMultipleSelectorMatch:
597 executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
598 break;
599#else
600 FALLTHROUGH;
601#endif // ENABLE(CSS_SELECTOR_JIT)
602 case MultipleSelectorMatch:
603#if ENABLE(CSS_SELECTOR_JIT)
604 MultipleSelectorMatch:
605#endif
606 executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
607 break;
608 }
609}
610
611SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
612 : m_selectorList(WTFMove(selectorList))
613 , m_selectors(m_selectorList)
614{
615}
616
617ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document)
618{
619 if (auto* entry = m_entries.get(selectors))
620 return *entry;
621
622 CSSParser parser(document);
623 CSSSelectorList selectorList;
624 parser.parseSelector(selectors, selectorList);
625
626 if (!selectorList.first() || selectorList.hasInvalidSelector())
627 return Exception { SyntaxError };
628
629 if (selectorList.selectorsNeedNamespaceResolution())
630 return Exception { SyntaxError };
631
632 const int maximumSelectorQueryCacheSize = 256;
633 if (m_entries.size() == maximumSelectorQueryCacheSize)
634 m_entries.remove(m_entries.random());
635
636 return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value;
637}
638
639}
640