• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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