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

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

Powered by Google App Engine
This is Rietveld 408576698