Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(110)

Side by Side Diff: Source/core/dom/SelectorQuery.cpp

Issue 18732004: Add fast path for querySelector(All) (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved. 2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 19 matching lines...) Expand all
30 #include "core/css/CSSSelectorList.h" 30 #include "core/css/CSSSelectorList.h"
31 #include "core/css/SelectorChecker.h" 31 #include "core/css/SelectorChecker.h"
32 #include "core/css/SelectorCheckerFastPath.h" 32 #include "core/css/SelectorCheckerFastPath.h"
33 #include "core/css/SiblingTraversalStrategies.h" 33 #include "core/css/SiblingTraversalStrategies.h"
34 #include "core/dom/Document.h" 34 #include "core/dom/Document.h"
35 #include "core/dom/NodeTraversal.h" 35 #include "core/dom/NodeTraversal.h"
36 #include "core/dom/StaticNodeList.h" 36 #include "core/dom/StaticNodeList.h"
37 37
38 namespace WebCore { 38 namespace WebCore {
39 39
40 class SimpleNodeList {
41 public:
42 virtual bool isEmpty() const = 0;
43 virtual Node* next() = 0;
44 };
45
46 class SingleNodeList : public SimpleNodeList {
47 public:
48 SingleNodeList(Node* rootNode) : m_currentNode(rootNode) { }
49
50 bool isEmpty() const { return !m_currentNode; }
51
52 Node* next()
53 {
54 Node* current = m_currentNode;
55 m_currentNode = 0;
56 return current;
57 }
58
59 private:
60 Node* m_currentNode;
61 };
62
63 class ClassRootNodeList : public SimpleNodeList {
64 public:
65 ClassRootNodeList(Node* rootNode, const AtomicString& className)
66 : m_className(className)
67 , m_rootNode(rootNode)
68 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode)) ) { }
69
70 bool isEmpty() const { return !m_currentElement; }
71
72 Node* next()
73 {
74 Node* current = m_currentElement;
75 ASSERT(current);
76 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(m _currentElement, m_rootNode));
77 return current;
78 }
79
80 private:
81 Element* nextInternal(Element* element)
82 {
83 for (; element; element = ElementTraversal::next(element, m_rootNode)) {
84 if (element->hasClass() && element->classNames().contains(m_classNam e))
85 return element;
86 }
87 return 0;
88 }
89
90 const AtomicString& m_className;
91 Node* m_rootNode;
92 Element* m_currentElement;
93 };
94
95 class ClassElementList : public SimpleNodeList {
96 public:
97 ClassElementList(Node* rootNode, const AtomicString& className)
98 : m_className(className)
99 , m_rootNode(rootNode)
100 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode)) ) { }
101
102 bool isEmpty() const { return !m_currentElement; }
103
104 Node* next()
105 {
106 Node* current = m_currentElement;
107 ASSERT(current);
108 m_currentElement = nextInternal(ElementTraversal::next(m_currentElement, m_rootNode));
109 return current;
110 }
111
112 private:
113 Element* nextInternal(Element* element)
114 {
115 for (; element; element = ElementTraversal::next(element, m_rootNode)) {
116 if (element->hasClass() && element->classNames().contains(m_classNam e))
117 return element;
118 }
119 return 0;
120 }
121
122 const AtomicString& m_className;
123 Node* m_rootNode;
124 Element* m_currentElement;
125 };
126
40 void SelectorDataList::initialize(const CSSSelectorList& selectorList) 127 void SelectorDataList::initialize(const CSSSelectorList& selectorList)
41 { 128 {
42 ASSERT(m_selectors.isEmpty()); 129 ASSERT(m_selectors.isEmpty());
43 130
44 unsigned selectorCount = 0; 131 unsigned selectorCount = 0;
45 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) 132 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
46 selectorCount++; 133 selectorCount++;
47 134
48 m_selectors.reserveInitialCapacity(selectorCount); 135 m_selectors.reserveInitialCapacity(selectorCount);
49 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) 136 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
(...skipping 26 matching lines...) Expand all
76 if (selectorMatches(m_selectors[i], targetElement, targetElement)) 163 if (selectorMatches(m_selectors[i], targetElement, targetElement))
77 return true; 164 return true;
78 } 165 }
79 166
80 return false; 167 return false;
81 } 168 }
82 169
83 PassRefPtr<NodeList> SelectorDataList::queryAll(Node* rootNode) const 170 PassRefPtr<NodeList> SelectorDataList::queryAll(Node* rootNode) const
84 { 171 {
85 Vector<RefPtr<Node> > result; 172 Vector<RefPtr<Node> > result;
86 execute<false>(rootNode, result); 173 executeQueryAll(rootNode, result);
87 return StaticNodeList::adopt(result); 174 return StaticNodeList::adopt(result);
88 } 175 }
89 176
90 PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const 177 PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const
91 { 178 {
92 Vector<RefPtr<Node> > result; 179 return executeQueryFirst(rootNode);
93 execute<true>(rootNode, result);
94 if (result.isEmpty())
95 return 0;
96 ASSERT(result.size() == 1);
97 ASSERT(result.first()->isElementNode());
98 return toElement(result.first().get());
99 } 180 }
100 181
101 static inline bool isTreeScopeRoot(Node* node) 182 static inline bool isTreeScopeRoot(Node* node)
102 { 183 {
103 ASSERT(node); 184 ASSERT(node);
104 return node->isDocumentNode() || node->isShadowRoot(); 185 return node->isDocumentNode() || node->isShadowRoot();
105 } 186 }
106 187
107 // If the first pair value is true, the returned Node is the single Element that may match the selector query. 188 void SelectorDataList::collectElementsByClassName(Node* rootNode, const AtomicSt ring& className, Vector<RefPtr<Node> >& traversalRoots) const
108 // 189 {
109 // If the first value is false, the returned Node is the rootNode parameter or a descendant of rootNode representing 190 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
191 if (element->hasClass() && element->classNames().contains(className))
192 traversalRoots.append(element);
193 }
194 }
195
196 void SelectorDataList::collectElementsByTagName(Node* rootNode, const QualifiedN ame& tagName, Vector<RefPtr<Node> >& traversalRoots) const
197 {
198 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
199 if (SelectorChecker::tagMatches(element, tagName))
200 traversalRoots.append(element);
201 }
202 }
203
204 Element* SelectorDataList::findElementByClassName(Node* rootNode, const AtomicSt ring& className) const
205 {
206 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
207 if (element->hasClass() && element->classNames().contains(className))
208 return element;
209 }
210 return 0;
211 }
212
213 Element* SelectorDataList::findElementByTagName(Node* rootNode, const QualifiedN ame& tagName) const
214 {
215 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
216 if (SelectorChecker::tagMatches(element, tagName))
217 return element;
218 }
219 return 0;
220 }
221
222 inline bool SelectorDataList::canUseFastQuery(Node* rootNode) const
223 {
224 return m_selectors.size() == 1 && rootNode->inDocument() && !rootNode->docum ent()->inQuirksMode();
225 }
226
227 // If returns true, traversalRoots has the elements that may match the selector query.
228 //
229 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing
110 // the subtree for which we can limit the querySelector traversal. 230 // the subtree for which we can limit the querySelector traversal.
111 // 231 //
112 // The returned Node may be 0, regardless of the returned bool value, if this me thod finds that the selectors won't 232 // The travseralRoots may be empty, regardless of the returned bool value, if th is method finds that the selectors won't
113 // match any element. 233 // match any element.
114 std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) const 234 PassOwnPtr<SimpleNodeList> SelectorDataList::findTraverseRoots(Node* rootNode, b ool& matchTraverseRoots) const
115 { 235 {
116 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches 236 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches
117 // we would need to sort the results. For now, just traverse the document in that case. 237 // we would need to sort the results. For now, just traverse the document in that case.
118 if (m_selectors.size() != 1) 238 ASSERT(rootNode);
119 return std::make_pair(false, rootNode); 239 ASSERT(m_selectors.size() == 1);
120 if (!rootNode->inDocument()) 240 ASSERT(m_selectors[0].selector);
121 return std::make_pair(false, rootNode); 241
122 if (rootNode->document()->inQuirksMode()) 242 bool isRightmostSelector = true;
123 return std::make_pair(false, rootNode); 243 bool startFromParent = false;
244
245 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) {
246 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) {
247 Element* element = rootNode->treeScope()->getElementById(selector->v alue());
248 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode)))
249 rootNode = element;
250 else if (!element || isRightmostSelector)
251 rootNode = 0;
252 if (isRightmostSelector) {
253 matchTraverseRoots = true;
254 return adoptPtr(new SingleNodeList(rootNode));
255 }
256 if (startFromParent && rootNode)
257 rootNode = rootNode->parentNode();
258
259 matchTraverseRoots = false;
260 return adoptPtr(new SingleNodeList(rootNode));
261 }
262
263 // If we have both CSSSelector::Id and CSSSelector::Class at the same ti me, we should use Id
264 // to find traverse root.
265 if (!startFromParent && selector->m_match == CSSSelector::Class) {
266 if (isRightmostSelector) {
267 matchTraverseRoots = true;
268 return adoptPtr(new ClassElementList(rootNode, selector->value() ));
269 }
270 matchTraverseRoots = false;
271 return adoptPtr(new ClassRootNodeList(rootNode, selector->value()));
272 }
273
274 if (selector->relation() == CSSSelector::SubSelector)
275 continue;
276 isRightmostSelector = false;
277 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent)
278 startFromParent = true;
279 else
280 startFromParent = false;
281 }
282
283 matchTraverseRoots = false;
284 return adoptPtr(new SingleNodeList(rootNode));
285 }
286
287 void SelectorDataList::executeSlowQueryAll(Node* rootNode, Vector<RefPtr<Node> > & matchedElements) const
288 {
289 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
290 for (unsigned i = 0; i < m_selectors.size(); ++i) {
291 if (selectorMatches(m_selectors[i], element, rootNode)) {
292 matchedElements.append(element);
293 break;
294 }
295 }
296 }
297 }
298
299 void SelectorDataList::executeQueryAll(Node* rootNode, Vector<RefPtr<Node> >& ma tchedElements) const
300 {
301 if (!canUseFastQuery(rootNode))
302 return executeSlowQueryAll(rootNode, matchedElements);
303
304 ASSERT(m_selectors.size() == 1);
305 ASSERT(m_selectors[0].selector);
306
307 const CSSSelector* firstSelector = m_selectors[0].selector;
308
309 if (!firstSelector->tagHistory()) {
310 // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and q uerySelectorAll('div').
311 switch (firstSelector->m_match) {
312 case CSSSelector::Id:
313 {
314 if (rootNode->document()->containsMultipleElementsWithId(firstSe lector->value()))
315 break;
316
317 // Just the same as getElementById.
318 Element* element = rootNode->treeScope()->getElementById(firstSe lector->value());
319 if (element && (isTreeScopeRoot(rootNode) || element->isDescenda ntOf(rootNode)))
320 matchedElements.append(element);
321 return;
322 }
323 case CSSSelector::Class:
324 return collectElementsByClassName(rootNode, firstSelector->value(), matchedElements);
325 case CSSSelector::Tag:
326 return collectElementsByTagName(rootNode, firstSelector->tagQName(), matchedElements);
327 default:
328 break; // If we need another fast path, add here.
329 }
330 }
331
332 bool matchTraverseRoots;
333 OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(rootNode, matchTrav erseRoots);
334 if (traverseRoots->isEmpty())
335 return;
336
337 const SelectorData& selector = m_selectors[0];
338 if (matchTraverseRoots) {
339 while (!traverseRoots->isEmpty()) {
340 Node* node = traverseRoots->next();
341 Element* element = toElement(node);
342 if (selectorMatches(selector, element, rootNode))
343 matchedElements.append(element);
344 }
345 return;
346 }
347
348 while (!traverseRoots->isEmpty()) {
349 Node* traverseRoot = traverseRoots->next();
350 for (Element* element = ElementTraversal::firstWithin(traverseRoot); ele ment; element = ElementTraversal::next(element, traverseRoot)) {
351 if (selectorMatches(selector, element, rootNode))
352 matchedElements.append(element);
353 }
354 }
355 }
356
357 // If matchTraverseRoot is true, the returned Node is the single Element that ma y match the selector query.
358 //
359 // If matchTraverseRoot is false, the returned Node is the rootNode parameter or a descendant of rootNode representing
360 // the subtree for which we can limit the querySelector traversal.
361 //
362 // The returned Node may be 0, regardless of matchTraverseRoot, if this method f inds that the selectors won't
363 // match any element.
364 Node* SelectorDataList::findTraverseRoot(Node* rootNode, bool& matchTraverseRoot ) const
365 {
366 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches
367 // we would need to sort the results. For now, just traverse the document in that case.
368 ASSERT(rootNode);
369 ASSERT(m_selectors.size() == 1);
370 ASSERT(m_selectors[0].selector);
124 371
125 bool matchSingleNode = true; 372 bool matchSingleNode = true;
126 bool startFromParent = false; 373 bool startFromParent = false;
127 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) { 374 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) {
128 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) { 375 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) {
129 Element* element = rootNode->treeScope()->getElementById(selector->v alue()); 376 Element* element = rootNode->treeScope()->getElementById(selector->v alue());
130 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode))) 377 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode)))
131 rootNode = element; 378 rootNode = element;
132 else if (!element || matchSingleNode) 379 else if (!element || matchSingleNode)
133 rootNode = 0; 380 rootNode = 0;
134 if (matchSingleNode) 381 if (matchSingleNode) {
135 return std::make_pair(true, rootNode); 382 matchTraverseRoot = true;
383 return rootNode;
384 }
136 if (startFromParent && rootNode) 385 if (startFromParent && rootNode)
137 rootNode = rootNode->parentNode(); 386 rootNode = rootNode->parentNode();
138 return std::make_pair(false, rootNode); 387 matchTraverseRoot = false;
388 return rootNode;
139 } 389 }
140 if (selector->relation() == CSSSelector::SubSelector) 390 if (selector->relation() == CSSSelector::SubSelector)
141 continue; 391 continue;
142 matchSingleNode = false; 392 matchSingleNode = false;
143 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent) 393 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent)
144 startFromParent = true; 394 startFromParent = true;
145 else 395 else
146 startFromParent = false; 396 startFromParent = false;
147 } 397 }
148 return std::make_pair(false, rootNode); 398 matchTraverseRoot = false;
149 } 399 return rootNode;
150 400 }
151 template <bool firstMatchOnly> 401
152 void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedEle ments) const 402 Element* SelectorDataList::executeSlowQueryFirst(Node* rootNode) const
153 { 403 {
154 std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); 404 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
155 if (!traverseRoot.second) 405 for (unsigned i = 0; i < m_selectors.size(); ++i) {
156 return; 406 if (selectorMatches(m_selectors[i], element, rootNode))
157 Node* traverseRootNode = traverseRoot.second; 407 return element;
158 if (traverseRoot.first) { 408 }
409 }
410 return 0;
411 }
412
413 Element* SelectorDataList::executeQueryFirst(Node* rootNode) const
414 {
415 if (!canUseFastQuery(rootNode))
416 return executeSlowQueryFirst(rootNode);
417
418
419 const CSSSelector* selector = m_selectors[0].selector;
420 ASSERT(selector);
421
422 if (!selector->tagHistory()) {
423 // Fast path for querySelector('#id'), querySelector('.foo'), and queryS elector('div').
424 // Many web developers uses querySelector with these simple selectors.
425 switch (selector->m_match) {
426 case CSSSelector::Id:
427 {
428 if (rootNode->document()->containsMultipleElementsWithId(selecto r->value()))
429 break;
430 Element* element = rootNode->treeScope()->getElementById(selecto r->value());
431 return element && (isTreeScopeRoot(rootNode) || element->isDesce ndantOf(rootNode)) ? element : 0;
432 }
433 case CSSSelector::Class:
434 return findElementByClassName(rootNode, selector->value());
435 case CSSSelector::Tag:
436 return findElementByTagName(rootNode, selector->tagQName());
437 default:
438 break; // If we need another fast path, add here.
439 }
440 }
441
442 bool matchTraverseRoot;
443 Node* traverseRootNode = findTraverseRoot(rootNode, matchTraverseRoot);
444 if (!traverseRootNode)
445 return 0;
446 if (matchTraverseRoot) {
159 ASSERT(m_selectors.size() == 1); 447 ASSERT(m_selectors.size() == 1);
160 ASSERT(traverseRootNode->isElementNode()); 448 ASSERT(traverseRootNode->isElementNode());
161 Element* element = toElement(traverseRootNode); 449 Element* element = toElement(traverseRootNode);
450 return selectorMatches(m_selectors[0], element, rootNode) ? element : 0;
451 }
452
453 for (Element* element = ElementTraversal::firstWithin(traverseRootNode); ele ment; element = ElementTraversal::next(element, traverseRootNode)) {
162 if (selectorMatches(m_selectors[0], element, rootNode)) 454 if (selectorMatches(m_selectors[0], element, rootNode))
163 matchedElements.append(element); 455 return element;
164 return; 456 }
165 } 457 return 0;
166
167 unsigned selectorCount = m_selectors.size();
168 if (selectorCount == 1) {
169 const SelectorData& selector = m_selectors[0];
170 for (Element* element = ElementTraversal::firstWithin(rootNode); element ; element = ElementTraversal::next(element, rootNode)) {
171 if (selectorMatches(selector, element, rootNode)) {
172 matchedElements.append(element);
173 if (firstMatchOnly)
174 return;
175 }
176 }
177 return;
178 }
179 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) {
180 for (unsigned i = 0; i < selectorCount; ++i) {
181 if (selectorMatches(m_selectors[i], element, rootNode)) {
182 matchedElements.append(element);
183 if (firstMatchOnly)
184 return;
185 break;
186 }
187 }
188 }
189 } 458 }
190 459
191 SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) 460 SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList)
192 : m_selectorList(selectorList) 461 : m_selectorList(selectorList)
193 { 462 {
194 m_selectors.initialize(m_selectorList); 463 m_selectors.initialize(m_selectorList);
195 } 464 }
196 465
197 bool SelectorQuery::matches(Element* element) const 466 bool SelectorQuery::matches(Element* element) const
198 { 467 {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 m_entries.add(selectors, selectorQuery.release()); 508 m_entries.add(selectors, selectorQuery.release());
240 return rawSelectorQuery; 509 return rawSelectorQuery;
241 } 510 }
242 511
243 void SelectorQueryCache::invalidate() 512 void SelectorQueryCache::invalidate()
244 { 513 {
245 m_entries.clear(); 514 m_entries.clear();
246 } 515 }
247 516
248 } 517 }
OLDNEW
« LayoutTests/fast/dom/SelectorAPI/dumpNodeList-2.html ('K') | « Source/core/dom/SelectorQuery.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698