1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "core/dom/SelectorQuery.h"
28
29 #include "bindings/v8/ExceptionState.h"
30 #include "core/css/CSSParser.h"
31 #include "core/css/SelectorChecker.h"
32 #include "core/css/SelectorCheckerFastPath.h"
33 #include "core/css/SiblingTraversalStrategies.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
39 namespace WebCore {
40
41 class SimpleNodeList {
42 public:
~SimpleNodeList()43 virtual ~SimpleNodeList() { }
44 virtual bool isEmpty() const = 0;
45 virtual Node* next() = 0;
46 };
47
48 class SingleNodeList : public SimpleNodeList {
49 public:
SingleNodeList(Node * rootNode)50 explicit SingleNodeList(Node* rootNode) : m_currentNode(rootNode) { }
51
isEmpty() const52 bool isEmpty() const { return !m_currentNode; }
53
next()54 Node* next()
55 {
56 Node* current = m_currentNode;
57 m_currentNode = 0;
58 return current;
59 }
60
61 private:
62 Node* m_currentNode;
63 };
64
65 class ClassRootNodeList : public SimpleNodeList {
66 public:
ClassRootNodeList(Node & rootNode,const AtomicString & className)67 ClassRootNodeList(Node& rootNode, const AtomicString& className)
68 : m_className(className)
69 , m_rootNode(rootNode)
70 , m_currentElement(nextInternal(ElementTraversal::firstWithin(m_rootNode))) { }
71
isEmpty() const72 bool isEmpty() const { return !m_currentElement; }
73
next()74 Node* next()
75 {
76 Node* current = m_currentElement;
77 ASSERT(current);
78 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, &m_rootNode));
79 return current;
80 }
81
82 private:
nextInternal(Element * element)83 Element* nextInternal(Element* element)
84 {
85 for (; element; element = ElementTraversal::next(*element, &m_rootNode)) {
86 if (element->hasClass() && element->classNames().contains(m_className))
87 return element;
88 }
89 return 0;
90 }
91
92 const AtomicString& m_className;
93 Node& m_rootNode;
94 Element* m_currentElement;
95 };
96
97 class ClassElementList : public SimpleNodeList {
98 public:
ClassElementList(Node & rootNode,const AtomicString & className)99 ClassElementList(Node& rootNode, const AtomicString& className)
100 : m_className(className)
101 , m_rootNode(rootNode)
102 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode))) { }
103
isEmpty() const104 bool isEmpty() const { return !m_currentElement; }
105
next()106 Node* next()
107 {
108 Node* current = m_currentElement;
109 ASSERT(current);
110 m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, &m_rootNode));
111 return current;
112 }
113
114 private:
nextInternal(Element * element)115 Element* nextInternal(Element* element)
116 {
117 for (; element; element = ElementTraversal::next(*element, &m_rootNode)) {
118 if (element->hasClass() && element->classNames().contains(m_className))
119 return element;
120 }
121 return 0;
122 }
123
124 const AtomicString& m_className;
125 Node& m_rootNode;
126 Element* m_currentElement;
127 };
128
initialize(const CSSSelectorList & selectorList)129 void SelectorDataList::initialize(const CSSSelectorList& selectorList)
130 {
131 ASSERT(m_selectors.isEmpty());
132
133 unsigned selectorCount = 0;
134 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
135 selectorCount++;
136
137 m_selectors.reserveInitialCapacity(selectorCount);
138 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
139 m_selectors.uncheckedAppend(SelectorData(selector, SelectorCheckerFastPath::canUse(selector)));
140 }
141
selectorMatches(const SelectorData & selectorData,Element & element,const Node & rootNode) const142 inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const Node& rootNode) const
143 {
144 if (selectorData.isFastCheckable && !element.isSVGElement()) {
145 SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, element);
146 if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled))
147 return false;
148 return selectorCheckerFastPath.matches();
149 }
150
151 SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules);
152 SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, &element, SelectorChecker::VisitedMatchDisabled);
153 selectorCheckingContext.behaviorAtBoundary = SelectorChecker::StaysWithinTreeScope;
154 selectorCheckingContext.scope = !rootNode.isDocumentNode() && rootNode.isContainerNode() ? &toContainerNode(rootNode) : 0;
155 return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches;
156 }
157
matches(Element & targetElement) const158 bool SelectorDataList::matches(Element& targetElement) const
159 {
160 unsigned selectorCount = m_selectors.size();
161 for (unsigned i = 0; i < selectorCount; ++i) {
162 if (selectorMatches(m_selectors[i], targetElement, targetElement))
163 return true;
164 }
165
166 return false;
167 }
168
queryAll(Node & rootNode) const169 PassRefPtr<NodeList> SelectorDataList::queryAll(Node& rootNode) const
170 {
171 Vector<RefPtr<Node> > result;
172 executeQueryAll(rootNode, result);
173 return StaticNodeList::adopt(result);
174 }
175
queryFirst(Node & rootNode) const176 PassRefPtr<Element> SelectorDataList::queryFirst(Node& rootNode) const
177 {
178 return executeQueryFirst(rootNode);
179 }
180
collectElementsByClassName(Node & rootNode,const AtomicString & className,Vector<RefPtr<Node>> & traversalRoots) const181 void SelectorDataList::collectElementsByClassName(Node& rootNode, const AtomicString& className, Vector<RefPtr<Node> >& traversalRoots) const
182 {
183 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
184 if (element->hasClass() && element->classNames().contains(className))
185 traversalRoots.append(element);
186 }
187 }
188
collectElementsByTagName(Node & rootNode,const QualifiedName & tagName,Vector<RefPtr<Node>> & traversalRoots) const189 void SelectorDataList::collectElementsByTagName(Node& rootNode, const QualifiedName& tagName, Vector<RefPtr<Node> >& traversalRoots) const
190 {
191 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
192 if (SelectorChecker::tagMatches(*element, tagName))
193 traversalRoots.append(element);
194 }
195 }
196
findElementByClassName(Node & rootNode,const AtomicString & className) const197 Element* SelectorDataList::findElementByClassName(Node& rootNode, const AtomicString& className) const
198 {
199 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
200 if (element->hasClass() && element->classNames().contains(className))
201 return element;
202 }
203 return 0;
204 }
205
findElementByTagName(Node & rootNode,const QualifiedName & tagName) const206 Element* SelectorDataList::findElementByTagName(Node& rootNode, const QualifiedName& tagName) const
207 {
208 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
209 if (SelectorChecker::tagMatches(*element, tagName))
210 return element;
211 }
212 return 0;
213 }
214
canUseFastQuery(const Node & rootNode) const215 inline bool SelectorDataList::canUseFastQuery(const Node& rootNode) const
216 {
217 return m_selectors.size() == 1 && rootNode.inDocument() && !rootNode.document().inQuirksMode();
218 }
219
ancestorHasClassName(Node & rootNode,const AtomicString & className)220 inline bool ancestorHasClassName(Node& rootNode, const AtomicString& className)
221 {
222 if (!rootNode.isElementNode())
223 return false;
224
225 for (Element* element = &toElement(rootNode); element; element = element->parentElement()) {
226 if (element->hasClass() && element->classNames().contains(className))
227 return true;
228 }
229 return false;
230 }
231
232
233 // If returns true, traversalRoots has the elements that may match the selector query.
234 //
235 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing
236 // the subtree for which we can limit the querySelector traversal.
237 //
238 // The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't
239 // match any element.
findTraverseRoots(Node & rootNode,bool & matchTraverseRoots) const240 PassOwnPtr<SimpleNodeList> SelectorDataList::findTraverseRoots(Node& rootNode, bool& matchTraverseRoots) const
241 {
242 // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches
243 // we would need to sort the results. For now, just traverse the document in that case.
244 ASSERT(m_selectors.size() == 1);
245 ASSERT(m_selectors[0].selector);
246
247 bool isRightmostSelector = true;
248 bool startFromParent = false;
249
250 for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) {
251 if (selector->m_match == CSSSelector::Id && !rootNode.document().containsMultipleElementsWithId(selector->value())) {
252 Element* element = rootNode.treeScope().getElementById(selector->value());
253 Node* adjustedNode = &rootNode;
254 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
255 adjustedNode = element;
256 else if (!element || isRightmostSelector)
257 adjustedNode = 0;
258 if (isRightmostSelector) {
259 matchTraverseRoots = true;
260 return adoptPtr(new SingleNodeList(adjustedNode));
261 }
262 if (startFromParent && adjustedNode)
263 adjustedNode = adjustedNode->parentNode();
264
265 matchTraverseRoots = false;
266 return adoptPtr(new SingleNodeList(adjustedNode));
267 }
268
269 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id
270 // to find traverse root.
271 if (!startFromParent && selector->m_match == CSSSelector::Class) {
272 if (isRightmostSelector) {
273 matchTraverseRoots = true;
274 return adoptPtr(new ClassElementList(rootNode, selector->value()));
275 }
276 matchTraverseRoots = false;
277 // Since there exists some ancestor element which has the class name, we need to see all children of rootNode.
278 if (ancestorHasClassName(rootNode, selector->value()))
279 return adoptPtr(new SingleNodeList(&rootNode));
280
281 return adoptPtr(new ClassRootNodeList(rootNode, selector->value()));
282 }
283
284 if (selector->relation() == CSSSelector::SubSelector)
285 continue;
286 isRightmostSelector = false;
287 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
288 startFromParent = true;
289 else
290 startFromParent = false;
291 }
292
293 matchTraverseRoots = false;
294 return adoptPtr(new SingleNodeList(&rootNode));
295 }
296
executeSlowQueryAll(Node & rootNode,Vector<RefPtr<Node>> & matchedElements) const297 void SelectorDataList::executeSlowQueryAll(Node& rootNode, Vector<RefPtr<Node> >& matchedElements) const
298 {
299 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
300 for (unsigned i = 0; i < m_selectors.size(); ++i) {
301 if (selectorMatches(m_selectors[i], *element, rootNode)) {
302 matchedElements.append(element);
303 break;
304 }
305 }
306 }
307 }
308
executeQueryAll(Node & rootNode,Vector<RefPtr<Node>> & matchedElements) const309 void SelectorDataList::executeQueryAll(Node& rootNode, Vector<RefPtr<Node> >& matchedElements) const
310 {
311 if (!canUseFastQuery(rootNode))
312 return executeSlowQueryAll(rootNode, matchedElements);
313
314 ASSERT(m_selectors.size() == 1);
315 ASSERT(m_selectors[0].selector);
316
317 const CSSSelector* firstSelector = m_selectors[0].selector;
318
319 if (!firstSelector->tagHistory()) {
320 // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and querySelectorAll('div').
321 switch (firstSelector->m_match) {
322 case CSSSelector::Id:
323 {
324 if (rootNode.document().containsMultipleElementsWithId(firstSelector->value()))
325 break;
326
327 // Just the same as getElementById.
328 Element* element = rootNode.treeScope().getElementById(firstSelector->value());
329 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
330 matchedElements.append(element);
331 return;
332 }
333 case CSSSelector::Class:
334 return collectElementsByClassName(rootNode, firstSelector->value(), matchedElements);
335 case CSSSelector::Tag:
336 return collectElementsByTagName(rootNode, firstSelector->tagQName(), matchedElements);
337 default:
338 break; // If we need another fast path, add here.
339 }
340 }
341
342 bool matchTraverseRoots;
343 OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(rootNode, matchTraverseRoots);
344 if (traverseRoots->isEmpty())
345 return;
346
347 const SelectorData& selector = m_selectors[0];
348 if (matchTraverseRoots) {
349 while (!traverseRoots->isEmpty()) {
350 Node& node = *traverseRoots->next();
351 Element& element = toElement(node);
352 if (selectorMatches(selector, element, rootNode))
353 matchedElements.append(&element);
354 }
355 return;
356 }
357
358 while (!traverseRoots->isEmpty()) {
359 Node* traverseRoot = traverseRoots->next();
360 ASSERT(traverseRoot);
361 for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) {
362 if (selectorMatches(selector, *element, rootNode))
363 matchedElements.append(element);
364 }
365 }
366 }
367
368 // If matchTraverseRoot is true, the returned Node is the single Element that may match the selector query.
369 //
370 // If matchTraverseRoot is false, the returned Node is the rootNode parameter or a descendant of rootNode representing
371 // the subtree for which we can limit the querySelector traversal.
372 //
373 // The returned Node may be 0, regardless of matchTraverseRoot, if this method finds that the selectors won't
374 // match any element.
findTraverseRoot(Node & rootNode,bool & matchTraverseRoot) const375 Node* SelectorDataList::findTraverseRoot(Node& rootNode, bool& matchTraverseRoot) const
376 {
377 // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches
378 // we would need to sort the results. For now, just traverse the document in that case.
379 ASSERT(m_selectors.size() == 1);
380 ASSERT(m_selectors[0].selector);
381
382 bool matchSingleNode = true;
383 bool startFromParent = false;
384 for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) {
385 if (selector->m_match == CSSSelector::Id && !rootNode.document().containsMultipleElementsWithId(selector->value())) {
386 Element* element = rootNode.treeScope().getElementById(selector->value());
387 Node* adjustedRootNode = &rootNode;
388 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
389 adjustedRootNode = element;
390 else if (!element || matchSingleNode)
391 adjustedRootNode = 0;
392 if (matchSingleNode) {
393 matchTraverseRoot = true;
394 return adjustedRootNode;
395 }
396 if (startFromParent && adjustedRootNode)
397 adjustedRootNode = adjustedRootNode->parentNode();
398 matchTraverseRoot = false;
399 return adjustedRootNode;
400 }
401 if (selector->relation() == CSSSelector::SubSelector)
402 continue;
403 matchSingleNode = false;
404 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
405 startFromParent = true;
406 else
407 startFromParent = false;
408 }
409 matchTraverseRoot = false;
410 return &rootNode;
411 }
412
executeSlowQueryFirst(Node & rootNode) const413 Element* SelectorDataList::executeSlowQueryFirst(Node& rootNode) const
414 {
415 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
416 for (unsigned i = 0; i < m_selectors.size(); ++i) {
417 if (selectorMatches(m_selectors[i], *element, rootNode))
418 return element;
419 }
420 }
421 return 0;
422 }
423
executeQueryFirst(Node & rootNode) const424 Element* SelectorDataList::executeQueryFirst(Node& rootNode) const
425 {
426 if (!canUseFastQuery(rootNode))
427 return executeSlowQueryFirst(rootNode);
428
429
430 const CSSSelector* selector = m_selectors[0].selector;
431 ASSERT(selector);
432
433 if (!selector->tagHistory()) {
434 // Fast path for querySelector('#id'), querySelector('.foo'), and querySelector('div').
435 // Many web developers uses querySelector with these simple selectors.
436 switch (selector->m_match) {
437 case CSSSelector::Id:
438 {
439 if (rootNode.document().containsMultipleElementsWithId(selector->value()))
440 break;
441 Element* element = rootNode.treeScope().getElementById(selector->value());
442 return element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)) ? element : 0;
443 }
444 case CSSSelector::Class:
445 return findElementByClassName(rootNode, selector->value());
446 case CSSSelector::Tag:
447 return findElementByTagName(rootNode, selector->tagQName());
448 default:
449 break; // If we need another fast path, add here.
450 }
451 }
452
453 bool matchTraverseRoot;
454 Node* traverseRootNode = findTraverseRoot(rootNode, matchTraverseRoot);
455 if (!traverseRootNode)
456 return 0;
457 if (matchTraverseRoot) {
458 ASSERT(m_selectors.size() == 1);
459 ASSERT(traverseRootNode->isElementNode());
460 Element& element = toElement(*traverseRootNode);
461 return selectorMatches(m_selectors[0], element, rootNode) ? &element : 0;
462 }
463
464 for (Element* element = ElementTraversal::firstWithin(*traverseRootNode); element; element = ElementTraversal::next(*element, traverseRootNode)) {
465 if (selectorMatches(m_selectors[0], *element, rootNode))
466 return element;
467 }
468 return 0;
469 }
470
SelectorQuery(const CSSSelectorList & selectorList)471 SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList)
472 : m_selectorList(selectorList)
473 {
474 m_selectors.initialize(m_selectorList);
475 }
476
matches(Element & element) const477 bool SelectorQuery::matches(Element& element) const
478 {
479 return m_selectors.matches(element);
480 }
481
queryAll(Node & rootNode) const482 PassRefPtr<NodeList> SelectorQuery::queryAll(Node& rootNode) const
483 {
484 return m_selectors.queryAll(rootNode);
485 }
486
queryFirst(Node & rootNode) const487 PassRefPtr<Element> SelectorQuery::queryFirst(Node& rootNode) const
488 {
489 return m_selectors.queryFirst(rootNode);
490 }
491
add(const AtomicString & selectors,const Document & document,ExceptionState & exceptionState)492 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState)
493 {
494 HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors);
495 if (it != m_entries.end())
496 return it->value.get();
497
498 CSSParser parser(document);
499 CSSSelectorList selectorList;
500 parser.parseSelector(selectors, selectorList);
501
502 if (!selectorList.first()) {
503 exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector.");
504 return 0;
505 }
506
507 // throw a NamespaceError if the selector includes any namespace prefixes.
508 if (selectorList.selectorsNeedNamespaceResolution()) {
509 exceptionState.throwDOMException(NamespaceError, "'" + selectors + "' contains namespaces, which are not supported.");
510 return 0;
511 }
512
513 const unsigned maximumSelectorQueryCacheSize = 256;
514 if (m_entries.size() == maximumSelectorQueryCacheSize)
515 m_entries.remove(m_entries.begin());
516
517 OwnPtr<SelectorQuery> selectorQuery = adoptPtr(new SelectorQuery(selectorList));
518 SelectorQuery* rawSelectorQuery = selectorQuery.get();
519 m_entries.add(selectors, selectorQuery.release());
520 return rawSelectorQuery;
521 }
522
invalidate()523 void SelectorQueryCache::invalidate()
524 {
525 m_entries.clear();
526 }
527
528 }
529