1 /*
2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
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 "core/dom/SelectorQuery.h"
29
30 #include "bindings/core/v8/ExceptionState.h"
31 #include "core/css/SelectorChecker.h"
32 #include "core/css/SiblingTraversalStrategies.h"
33 #include "core/css/parser/CSSParser.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/Node.h"
37 #include "core/dom/StaticNodeList.h"
38 #include "core/dom/shadow/ElementShadow.h"
39 #include "core/dom/shadow/ShadowRoot.h"
40
41 namespace blink {
42
43 struct SingleElementSelectorQueryTrait {
44 typedef Element* OutputType;
45 static const bool shouldOnlyMatchFirstElement = true;
appendElementblink::SingleElementSelectorQueryTrait46 ALWAYS_INLINE static void appendElement(OutputType& output, Element& element)
47 {
48 ASSERT(!output);
49 output = &element;
50 }
51 };
52
53 struct AllElementsSelectorQueryTrait {
54 typedef WillBeHeapVector<RefPtrWillBeMember<Element> > OutputType;
55 static const bool shouldOnlyMatchFirstElement = false;
appendElementblink::AllElementsSelectorQueryTrait56 ALWAYS_INLINE static void appendElement(OutputType& output, Element& element)
57 {
58 output.append(&element);
59 }
60 };
61
62 enum ClassElementListBehavior { AllElements, OnlyRoots };
63
64 template <ClassElementListBehavior onlyRoots>
65 class ClassElementList {
66 STACK_ALLOCATED();
67 public:
ClassElementList(ContainerNode & rootNode,const AtomicString & className)68 ClassElementList(ContainerNode& rootNode, const AtomicString& className)
69 : m_className(className)
70 , m_rootNode(&rootNode)
71 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode))) { }
72
isEmpty() const73 bool isEmpty() const { return !m_currentElement; }
74
next()75 Element* next()
76 {
77 Element* current = m_currentElement;
78 ASSERT(current);
79 if (onlyRoots)
80 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, m_rootNode));
81 else
82 m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, m_rootNode));
83 return current;
84 }
85
86 private:
nextInternal(Element * element)87 Element* nextInternal(Element* element)
88 {
89 for (; element; element = ElementTraversal::next(*element, m_rootNode)) {
90 if (element->hasClass() && element->classNames().contains(m_className))
91 return element;
92 }
93 return 0;
94 }
95
96 const AtomicString& m_className;
97 RawPtrWillBeMember<ContainerNode> m_rootNode;
98 RawPtrWillBeMember<Element> m_currentElement;
99 };
100
initialize(const CSSSelectorList & selectorList)101 void SelectorDataList::initialize(const CSSSelectorList& selectorList)
102 {
103 ASSERT(m_selectors.isEmpty());
104
105 unsigned selectorCount = 0;
106 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
107 selectorCount++;
108
109 m_crossesTreeBoundary = false;
110 m_selectors.reserveInitialCapacity(selectorCount);
111 unsigned index = 0;
112 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector), ++index) {
113 m_selectors.uncheckedAppend(selector);
114 m_crossesTreeBoundary |= selectorList.selectorCrossesTreeScopes(index);
115 }
116 }
117
selectorMatches(const CSSSelector & selector,Element & element,const ContainerNode & rootNode) const118 inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Element& element, const ContainerNode& rootNode) const
119 {
120 SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules);
121 SelectorChecker::SelectorCheckingContext selectorCheckingContext(selector, &element, SelectorChecker::VisitedMatchDisabled);
122 selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : 0;
123 if (selectorCheckingContext.scope)
124 selectorCheckingContext.contextFlags = SelectorChecker::ScopeContainsLastMatchedElement;
125 return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches;
126 }
127
matches(Element & targetElement) const128 bool SelectorDataList::matches(Element& targetElement) const
129 {
130 unsigned selectorCount = m_selectors.size();
131 for (unsigned i = 0; i < selectorCount; ++i) {
132 if (selectorMatches(*m_selectors[i], targetElement, targetElement))
133 return true;
134 }
135
136 return false;
137 }
138
queryAll(ContainerNode & rootNode) const139 PassRefPtrWillBeRawPtr<StaticElementList> SelectorDataList::queryAll(ContainerNode& rootNode) const
140 {
141 WillBeHeapVector<RefPtrWillBeMember<Element> > result;
142 execute<AllElementsSelectorQueryTrait>(rootNode, result);
143 return StaticElementList::adopt(result);
144 }
145
queryFirst(ContainerNode & rootNode) const146 PassRefPtrWillBeRawPtr<Element> SelectorDataList::queryFirst(ContainerNode& rootNode) const
147 {
148 Element* matchedElement = 0;
149 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement);
150 return matchedElement;
151 }
152
153 template <typename SelectorQueryTrait>
collectElementsByClassName(ContainerNode & rootNode,const AtomicString & className,typename SelectorQueryTrait::OutputType & output) const154 void SelectorDataList::collectElementsByClassName(ContainerNode& rootNode, const AtomicString& className, typename SelectorQueryTrait::OutputType& output) const
155 {
156 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
157 if (element->hasClass() && element->classNames().contains(className)) {
158 SelectorQueryTrait::appendElement(output, *element);
159 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
160 return;
161 }
162 }
163 }
164
165 template <typename SelectorQueryTrait>
collectElementsByTagName(ContainerNode & rootNode,const QualifiedName & tagName,typename SelectorQueryTrait::OutputType & output) const166 void SelectorDataList::collectElementsByTagName(ContainerNode& rootNode, const QualifiedName& tagName, typename SelectorQueryTrait::OutputType& output) const
167 {
168 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
169 if (SelectorChecker::tagMatches(*element, tagName)) {
170 SelectorQueryTrait::appendElement(output, *element);
171 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
172 return;
173 }
174 }
175 }
176
canUseFastQuery(const ContainerNode & rootNode) const177 inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const
178 {
179 return m_selectors.size() == 1 && !m_crossesTreeBoundary && rootNode.inDocument() && !rootNode.document().inQuirksMode();
180 }
181
ancestorHasClassName(ContainerNode & rootNode,const AtomicString & className)182 inline bool ancestorHasClassName(ContainerNode& rootNode, const AtomicString& className)
183 {
184 if (!rootNode.isElementNode())
185 return false;
186
187 for (Element* element = &toElement(rootNode); element; element = element->parentElement()) {
188 if (element->hasClass() && element->classNames().contains(className))
189 return true;
190 }
191 return false;
192 }
193
194
195 // If returns true, traversalRoots has the elements that may match the selector query.
196 //
197 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing
198 // the subtree for which we can limit the querySelector traversal.
199 //
200 // The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't
201 // match any element.
202 template <typename SelectorQueryTrait>
findTraverseRootsAndExecute(ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const203 void SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
204 {
205 // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches
206 // we would need to sort the results. For now, just traverse the document in that case.
207 ASSERT(m_selectors.size() == 1);
208
209 bool isRightmostSelector = true;
210 bool startFromParent = false;
211
212 for (const CSSSelector* selector = m_selectors[0]; selector; selector = selector->tagHistory()) {
213 if (selector->match() == CSSSelector::Id && !rootNode.document().containsMultipleElementsWithId(selector->value())) {
214 Element* element = rootNode.treeScope().getElementById(selector->value());
215 ContainerNode* adjustedNode = &rootNode;
216 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
217 adjustedNode = element;
218 else if (!element || isRightmostSelector)
219 adjustedNode = 0;
220 if (isRightmostSelector) {
221 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output);
222 return;
223 }
224
225 if (startFromParent && adjustedNode)
226 adjustedNode = adjustedNode->parentNode();
227
228 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, DoesNotMatchTraverseRoots, rootNode, output);
229 return;
230 }
231
232 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id
233 // to find traverse root.
234 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->match() == CSSSelector::Class) {
235 if (isRightmostSelector) {
236 ClassElementList<AllElements> traverseRoots(rootNode, selector->value());
237 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output);
238 return;
239 }
240 // Since there exists some ancestor element which has the class name, we need to see all children of rootNode.
241 if (ancestorHasClassName(rootNode, selector->value())) {
242 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
243 return;
244 }
245
246 ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value());
247 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output);
248 return;
249 }
250
251 if (selector->relation() == CSSSelector::SubSelector)
252 continue;
253 isRightmostSelector = false;
254 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
255 startFromParent = true;
256 else
257 startFromParent = false;
258 }
259
260 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
261 }
262
263 template <typename SelectorQueryTrait>
executeForTraverseRoot(const CSSSelector & selector,ContainerNode * traverseRoot,MatchTraverseRootState matchTraverseRoot,ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const264 void SelectorDataList::executeForTraverseRoot(const CSSSelector& selector, ContainerNode* traverseRoot, MatchTraverseRootState matchTraverseRoot, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
265 {
266 if (!traverseRoot)
267 return;
268
269 if (matchTraverseRoot) {
270 if (selectorMatches(selector, toElement(*traverseRoot), rootNode))
271 SelectorQueryTrait::appendElement(output, toElement(*traverseRoot));
272 return;
273 }
274
275 for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) {
276 if (selectorMatches(selector, *element, rootNode)) {
277 SelectorQueryTrait::appendElement(output, *element);
278 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
279 return;
280 }
281 }
282 }
283
284 template <typename SelectorQueryTrait, typename SimpleElementListType>
executeForTraverseRoots(const CSSSelector & selector,SimpleElementListType & traverseRoots,MatchTraverseRootState matchTraverseRoots,ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const285 void SelectorDataList::executeForTraverseRoots(const CSSSelector& selector, SimpleElementListType& traverseRoots, MatchTraverseRootState matchTraverseRoots, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
286 {
287 if (traverseRoots.isEmpty())
288 return;
289
290 if (matchTraverseRoots) {
291 while (!traverseRoots.isEmpty()) {
292 Element& element = *traverseRoots.next();
293 if (selectorMatches(selector, element, rootNode)) {
294 SelectorQueryTrait::appendElement(output, element);
295 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
296 return;
297 }
298 }
299 return;
300 }
301
302 while (!traverseRoots.isEmpty()) {
303 Element& traverseRoot = *traverseRoots.next();
304 for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(*element, &traverseRoot)) {
305 if (selectorMatches(selector, *element, rootNode)) {
306 SelectorQueryTrait::appendElement(output, *element);
307 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
308 return;
309 }
310 }
311 }
312 }
313
314 template <typename SelectorQueryTrait>
selectorListMatches(ContainerNode & rootNode,Element & element,typename SelectorQueryTrait::OutputType & output) const315 bool SelectorDataList::selectorListMatches(ContainerNode& rootNode, Element& element, typename SelectorQueryTrait::OutputType& output) const
316 {
317 for (unsigned i = 0; i < m_selectors.size(); ++i) {
318 if (selectorMatches(*m_selectors[i], element, rootNode)) {
319 SelectorQueryTrait::appendElement(output, element);
320 return true;
321 }
322 }
323 return false;
324 }
325
326 template <typename SelectorQueryTrait>
executeSlow(ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const327 void SelectorDataList::executeSlow(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
328 {
329 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
330 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement)
331 return;
332 }
333 }
334
335 // FIXME: Move the following helper functions, authorShadowRootOf, firstWithinTraversingShadowTree,
336 // nextTraversingShadowTree to the best place, e.g. NodeTraversal.
authorShadowRootOf(const ContainerNode & node)337 static ShadowRoot* authorShadowRootOf(const ContainerNode& node)
338 {
339 if (!node.isElementNode() || !isShadowHost(&node))
340 return 0;
341
342 ElementShadow* shadow = toElement(node).shadow();
343 ASSERT(shadow);
344 for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) {
345 if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot)
346 return shadowRoot;
347 }
348 return 0;
349 }
350
firstWithinTraversingShadowTree(const ContainerNode & rootNode)351 static ContainerNode* firstWithinTraversingShadowTree(const ContainerNode& rootNode)
352 {
353 if (ShadowRoot* shadowRoot = authorShadowRootOf(rootNode))
354 return shadowRoot;
355 return ElementTraversal::firstWithin(rootNode);
356 }
357
nextTraversingShadowTree(const ContainerNode & node,const ContainerNode * rootNode)358 static ContainerNode* nextTraversingShadowTree(const ContainerNode& node, const ContainerNode* rootNode)
359 {
360 if (ShadowRoot* shadowRoot = authorShadowRootOf(node))
361 return shadowRoot;
362
363 const ContainerNode* current = &node;
364 while (current) {
365 if (Element* next = ElementTraversal::next(*current, rootNode))
366 return next;
367
368 if (!current->isInShadowTree())
369 return 0;
370
371 ShadowRoot* shadowRoot = current->containingShadowRoot();
372 if (shadowRoot == rootNode)
373 return 0;
374 if (ShadowRoot* youngerShadowRoot = shadowRoot->youngerShadowRoot()) {
375 // Should not obtain any elements in user-agent shadow root.
376 ASSERT(youngerShadowRoot->type() == ShadowRoot::AuthorShadowRoot);
377 return youngerShadowRoot;
378 }
379
380 current = shadowRoot->host();
381 }
382 return 0;
383 }
384
385 template <typename SelectorQueryTrait>
executeSlowTraversingShadowTree(ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const386 void SelectorDataList::executeSlowTraversingShadowTree(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
387 {
388 for (ContainerNode* node = firstWithinTraversingShadowTree(rootNode); node; node = nextTraversingShadowTree(*node, &rootNode)) {
389 if (!node->isElementNode())
390 continue;
391 Element* element = toElement(node);
392 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement)
393 return;
394 }
395 }
396
selectorForIdLookup(const CSSSelector & firstSelector) const397 const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector& firstSelector) const
398 {
399 for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
400 if (selector->match() == CSSSelector::Id)
401 return selector;
402 if (selector->relation() != CSSSelector::SubSelector)
403 break;
404 }
405 return 0;
406 }
407
408 template <typename SelectorQueryTrait>
execute(ContainerNode & rootNode,typename SelectorQueryTrait::OutputType & output) const409 void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
410 {
411 if (!canUseFastQuery(rootNode)) {
412 if (m_crossesTreeBoundary) {
413 rootNode.document().updateDistributionForNodeIfNeeded(&rootNode);
414 executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output);
415 } else {
416 executeSlow<SelectorQueryTrait>(rootNode, output);
417 }
418 return;
419 }
420
421 ASSERT(m_selectors.size() == 1);
422
423 const CSSSelector& selector = *m_selectors[0];
424 const CSSSelector& firstSelector = selector;
425
426 // Fast path for querySelector*('#id'), querySelector*('tag#id').
427 if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) {
428 const AtomicString& idToMatch = idSelector->value();
429 if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) {
430 const WillBeHeapVector<RawPtrWillBeMember<Element> >& elements = rootNode.treeScope().getAllElementsById(idToMatch);
431 size_t count = elements.size();
432 for (size_t i = 0; i < count; ++i) {
433 Element& element = *elements[i];
434 if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode)))
435 continue;
436 if (selectorMatches(selector, element, rootNode)) {
437 SelectorQueryTrait::appendElement(output, element);
438 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
439 return;
440 }
441 }
442 return;
443 }
444 Element* element = rootNode.treeScope().getElementById(idToMatch);
445 if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
446 return;
447 if (selectorMatches(selector, *element, rootNode))
448 SelectorQueryTrait::appendElement(output, *element);
449 return;
450 }
451
452 if (!firstSelector.tagHistory()) {
453 // Fast path for querySelector*('.foo'), and querySelector*('div').
454 switch (firstSelector.match()) {
455 case CSSSelector::Class:
456 collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output);
457 return;
458 case CSSSelector::Tag:
459 collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output);
460 return;
461 default:
462 break; // If we need another fast path, add here.
463 }
464 }
465
466 findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output);
467 }
468
adopt(CSSSelectorList & selectorList)469 PassOwnPtr<SelectorQuery> SelectorQuery::adopt(CSSSelectorList& selectorList)
470 {
471 return adoptPtr(new SelectorQuery(selectorList));
472 }
473
SelectorQuery(CSSSelectorList & selectorList)474 SelectorQuery::SelectorQuery(CSSSelectorList& selectorList)
475 {
476 m_selectorList.adopt(selectorList);
477 m_selectors.initialize(m_selectorList);
478 }
479
matches(Element & element) const480 bool SelectorQuery::matches(Element& element) const
481 {
482 return m_selectors.matches(element);
483 }
484
queryAll(ContainerNode & rootNode) const485 PassRefPtrWillBeRawPtr<StaticElementList> SelectorQuery::queryAll(ContainerNode& rootNode) const
486 {
487 return m_selectors.queryAll(rootNode);
488 }
489
queryFirst(ContainerNode & rootNode) const490 PassRefPtrWillBeRawPtr<Element> SelectorQuery::queryFirst(ContainerNode& rootNode) const
491 {
492 return m_selectors.queryFirst(rootNode);
493 }
494
add(const AtomicString & selectors,const Document & document,ExceptionState & exceptionState)495 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState)
496 {
497 HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors);
498 if (it != m_entries.end())
499 return it->value.get();
500
501 CSSParser parser(CSSParserContext(document, 0));
502 CSSSelectorList selectorList;
503 parser.parseSelector(selectors, selectorList);
504
505 if (!selectorList.first()) {
506 exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector.");
507 return 0;
508 }
509
510 // throw a NamespaceError if the selector includes any namespace prefixes.
511 if (selectorList.selectorsNeedNamespaceResolution()) {
512 exceptionState.throwDOMException(NamespaceError, "'" + selectors + "' contains namespaces, which are not supported.");
513 return 0;
514 }
515
516 const unsigned maximumSelectorQueryCacheSize = 256;
517 if (m_entries.size() == maximumSelectorQueryCacheSize)
518 m_entries.remove(m_entries.begin());
519
520 return m_entries.add(selectors, SelectorQuery::adopt(selectorList)).storedValue->value.get();
521 }
522
invalidate()523 void SelectorQueryCache::invalidate()
524 {
525 m_entries.clear();
526 }
527
528 }
529