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

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

Issue 2820013002: Separate the id fast path in SelectorQuery. (Closed)
Patch Set: Update tests. Created 3 years, 8 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
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. 2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Samsung Electronics. All rights reserved. 3 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 #else 62 #else
63 63
64 #define QUERY_STATS_INCREMENT(name) 64 #define QUERY_STATS_INCREMENT(name)
65 #define QUERY_STATS_RESET() 65 #define QUERY_STATS_RESET()
66 66
67 #endif 67 #endif
68 68
69 struct SingleElementSelectorQueryTrait { 69 struct SingleElementSelectorQueryTrait {
70 typedef Element* OutputType; 70 typedef Element* OutputType;
71 static const bool kShouldOnlyMatchFirstElement = true; 71 static const bool kShouldOnlyMatchFirstElement = true;
72 ALWAYS_INLINE static bool IsEmpty(const OutputType& output) {
73 return !output;
74 }
72 ALWAYS_INLINE static void AppendElement(OutputType& output, 75 ALWAYS_INLINE static void AppendElement(OutputType& output,
73 Element& element) { 76 Element& element) {
74 DCHECK(!output); 77 DCHECK(!output);
75 output = &element; 78 output = &element;
76 } 79 }
77 }; 80 };
78 81
79 struct AllElementsSelectorQueryTrait { 82 struct AllElementsSelectorQueryTrait {
80 typedef HeapVector<Member<Element>> OutputType; 83 typedef HeapVector<Member<Element>> OutputType;
81 static const bool kShouldOnlyMatchFirstElement = false; 84 static const bool kShouldOnlyMatchFirstElement = false;
85 ALWAYS_INLINE static bool IsEmpty(const OutputType& output) {
86 return output.IsEmpty();
87 }
82 ALWAYS_INLINE static void AppendElement(OutputType& output, 88 ALWAYS_INLINE static void AppendElement(OutputType& output,
83 Element& element) { 89 Element& element) {
84 output.push_back(&element); 90 output.push_back(&element);
85 } 91 }
86 }; 92 };
87 93
88 // TODO(esprehn): Move this to Element and update callers elsewhere. 94 // TODO(esprehn): Move this to Element and update callers elsewhere.
89 inline bool HasClassName(const Element& element, 95 inline bool HasClassName(const Element& element,
90 const AtomicString& class_name) { 96 const AtomicString& class_name) {
91 return element.HasClass() && element.ClassNames().Contains(class_name); 97 return element.HasClass() && element.ClassNames().Contains(class_name);
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 template <typename SelectorQueryTrait> 226 template <typename SelectorQueryTrait>
221 void SelectorQuery::FindTraverseRootsAndExecute( 227 void SelectorQuery::FindTraverseRootsAndExecute(
222 ContainerNode& root_node, 228 ContainerNode& root_node,
223 typename SelectorQueryTrait::OutputType& output) const { 229 typename SelectorQueryTrait::OutputType& output) const {
224 // We need to return the matches in document order. To use id lookup while 230 // We need to return the matches in document order. To use id lookup while
225 // there is possiblity of multiple matches we would need to sort the 231 // there is possiblity of multiple matches we would need to sort the
226 // results. For now, just traverse the document in that case. 232 // results. For now, just traverse the document in that case.
227 DCHECK_EQ(selectors_.size(), 1u); 233 DCHECK_EQ(selectors_.size(), 1u);
228 234
229 bool is_rightmost_selector = true; 235 bool is_rightmost_selector = true;
230 bool start_from_parent = false; 236 bool is_affected_by_sibling_combinator = false;
231 237
232 for (const CSSSelector* selector = selectors_[0]; selector; 238 for (const CSSSelector* selector = selectors_[0]; selector;
233 selector = selector->TagHistory()) { 239 selector = selector->TagHistory()) {
234 if (selector->Match() == CSSSelector::kId && 240 if (!is_affected_by_sibling_combinator &&
235 !root_node.ContainingTreeScope().ContainsMultipleElementsWithId( 241 selector->Match() == CSSSelector::kClass) {
236 selector->Value())) {
237 // Id selectors in the right most selector are handled by the caller,
238 // we should never hit them here.
239 DCHECK(!is_rightmost_selector);
240 Element* element =
241 root_node.ContainingTreeScope().GetElementById(selector->Value());
242 if (!element)
243 return;
244 ContainerNode* start = &root_node;
245 if (element->IsDescendantOf(&root_node))
246 start = element;
247 if (start_from_parent)
248 start = start->parentNode();
249 if (!start)
250 return;
251 ExecuteForTraverseRoot<SelectorQueryTrait>(*start, root_node, output);
252 return;
253 }
254
255 // If we have both CSSSelector::Id and CSSSelector::Class at the same time,
256 // we should use Id to find traverse root.
257 if (!SelectorQueryTrait::kShouldOnlyMatchFirstElement &&
258 !start_from_parent && selector->Match() == CSSSelector::kClass) {
259 if (is_rightmost_selector) { 242 if (is_rightmost_selector) {
260 CollectElementsByClassName<SelectorQueryTrait>( 243 CollectElementsByClassName<SelectorQueryTrait>(
261 root_node, selector->Value(), selectors_[0], output); 244 root_node, selector->Value(), selectors_[0], output);
262 return; 245 return;
263 } 246 }
264 // Since there exists some ancestor element which has the class name, we 247 // Since there exists some ancestor element which has the class name, we
265 // need to see all children of rootNode. 248 // need to see all children of rootNode.
266 if (AncestorHasClassName(root_node, selector->Value())) 249 if (AncestorHasClassName(root_node, selector->Value()))
267 break; 250 break;
268 251
269 const AtomicString& class_name = selector->Value(); 252 const AtomicString& class_name = selector->Value();
270 Element* element = ElementTraversal::FirstWithin(root_node); 253 Element* element = ElementTraversal::FirstWithin(root_node);
271 while (element) { 254 while (element) {
272 QUERY_STATS_INCREMENT(fast_class); 255 QUERY_STATS_INCREMENT(fast_class);
273 if (HasClassName(*element, class_name)) { 256 if (HasClassName(*element, class_name)) {
274 ExecuteForTraverseRoot<SelectorQueryTrait>(*element, root_node, 257 ExecuteForTraverseRoot<SelectorQueryTrait>(*element, root_node,
275 output); 258 output);
259 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement &&
260 !SelectorQueryTrait::IsEmpty(output))
261 return;
276 element = 262 element =
277 ElementTraversal::NextSkippingChildren(*element, &root_node); 263 ElementTraversal::NextSkippingChildren(*element, &root_node);
278 } else { 264 } else {
279 element = ElementTraversal::Next(*element, &root_node); 265 element = ElementTraversal::Next(*element, &root_node);
280 } 266 }
281 } 267 }
282 return; 268 return;
283 } 269 }
284 270
285 if (selector->Relation() == CSSSelector::kSubSelector) 271 if (selector->Relation() == CSSSelector::kSubSelector)
286 continue; 272 continue;
287 is_rightmost_selector = false; 273 is_rightmost_selector = false;
288 if (selector->Relation() == CSSSelector::kDirectAdjacent || 274 is_affected_by_sibling_combinator =
289 selector->Relation() == CSSSelector::kIndirectAdjacent) 275 selector->Relation() == CSSSelector::kDirectAdjacent ||
290 start_from_parent = true; 276 selector->Relation() == CSSSelector::kIndirectAdjacent;
291 else
292 start_from_parent = false;
293 } 277 }
294 278
295 ExecuteForTraverseRoot<SelectorQueryTrait>(root_node, root_node, output); 279 ExecuteForTraverseRoot<SelectorQueryTrait>(root_node, root_node, output);
296 } 280 }
297 281
298 template <typename SelectorQueryTrait> 282 template <typename SelectorQueryTrait>
299 void SelectorQuery::ExecuteForTraverseRoot( 283 void SelectorQuery::ExecuteForTraverseRoot(
300 ContainerNode& traverse_root, 284 ContainerNode& traverse_root,
301 ContainerNode& root_node, 285 ContainerNode& root_node,
302 typename SelectorQueryTrait::OutputType& output) const { 286 typename SelectorQueryTrait::OutputType& output) const {
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 QUERY_STATS_INCREMENT(slow_traversing_shadow_tree_scan); 376 QUERY_STATS_INCREMENT(slow_traversing_shadow_tree_scan);
393 Element* element = ToElement(node); 377 Element* element = ToElement(node);
394 if (!SelectorListMatches(root_node, *element)) 378 if (!SelectorListMatches(root_node, *element))
395 continue; 379 continue;
396 SelectorQueryTrait::AppendElement(output, *element); 380 SelectorQueryTrait::AppendElement(output, *element);
397 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement) 381 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement)
398 return; 382 return;
399 } 383 }
400 } 384 }
401 385
402 static const CSSSelector* SelectorForIdLookup( 386 template <typename SelectorQueryTrait>
403 const CSSSelector& first_selector) { 387 void SelectorQuery::ExecuteWithId(
404 for (const CSSSelector* selector = &first_selector; selector; 388 ContainerNode& root_node,
405 selector = selector->TagHistory()) { 389 typename SelectorQueryTrait::OutputType& output) const {
406 if (selector->Match() == CSSSelector::kId) 390 const CSSSelector& first_selector = *selectors_[0];
407 return selector; 391 const TreeScope& scope = root_node.ContainingTreeScope();
408 // We only use the fast path when in standards mode where #id selectors 392
409 // are case sensitive, so we need the same behavior for [id=value]. 393 if (scope.ContainsMultipleElementsWithId(selector_id_)) {
410 if (selector->Match() == CSSSelector::kAttributeExact && 394 // We don't currently handle cases where there's multiple elements with the
411 selector->Attribute() == idAttr && 395 // id and it's not in the rightmost selector.
412 selector->AttributeMatch() == CSSSelector::kCaseSensitive) 396 if (!selector_id_is_rightmost_) {
413 return selector; 397 FindTraverseRootsAndExecute<SelectorQueryTrait>(root_node, output);
414 if (selector->Relation() != CSSSelector::kSubSelector) 398 return;
415 break; 399 }
400 const auto& elements = scope.GetAllElementsById(selector_id_);
401 for (const auto& element : elements) {
402 if (!element->IsDescendantOf(&root_node))
403 continue;
404 QUERY_STATS_INCREMENT(fast_id);
405 if (SelectorMatches(first_selector, *element, root_node)) {
406 SelectorQueryTrait::AppendElement(output, *element);
407 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement)
408 return;
409 }
410 }
411 return;
416 } 412 }
417 return nullptr; 413
414 Element* element = scope.GetElementById(selector_id_);
415 if (!element)
416 return;
417 if (selector_id_is_rightmost_) {
418 if (!element->IsDescendantOf(&root_node))
419 return;
420 QUERY_STATS_INCREMENT(fast_id);
421 if (SelectorMatches(first_selector, *element, root_node))
422 SelectorQueryTrait::AppendElement(output, *element);
423 return;
424 }
425 ContainerNode* start = &root_node;
426 if (element->IsDescendantOf(&root_node))
427 start = element;
428 if (selector_id_affected_by_sibling_combinator_)
429 start = start->parentNode();
430 if (!start)
431 return;
432 QUERY_STATS_INCREMENT(fast_id);
433 ExecuteForTraverseRoot<SelectorQueryTrait>(*start, root_node, output);
418 } 434 }
419 435
420 template <typename SelectorQueryTrait> 436 template <typename SelectorQueryTrait>
421 void SelectorQuery::Execute( 437 void SelectorQuery::Execute(
422 ContainerNode& root_node, 438 ContainerNode& root_node,
423 typename SelectorQueryTrait::OutputType& output) const { 439 typename SelectorQueryTrait::OutputType& output) const {
424 if (selectors_.IsEmpty()) 440 if (selectors_.IsEmpty())
425 return; 441 return;
426 442
427 if (!CanUseFastQuery(root_node)) { 443 if (!CanUseFastQuery(root_node)) {
428 if (needs_updated_distribution_) 444 if (needs_updated_distribution_)
429 root_node.UpdateDistribution(); 445 root_node.UpdateDistribution();
430 if (uses_deep_combinator_or_shadow_pseudo_) { 446 if (uses_deep_combinator_or_shadow_pseudo_) {
431 ExecuteSlowTraversingShadowTree<SelectorQueryTrait>(root_node, output); 447 ExecuteSlowTraversingShadowTree<SelectorQueryTrait>(root_node, output);
432 } else { 448 } else {
433 ExecuteSlow<SelectorQueryTrait>(root_node, output); 449 ExecuteSlow<SelectorQueryTrait>(root_node, output);
434 } 450 }
435 return; 451 return;
436 } 452 }
437 453
438 DCHECK_EQ(selectors_.size(), 1u); 454 DCHECK_EQ(selectors_.size(), 1u);
439 DCHECK(!root_node.GetDocument().InQuirksMode()); 455 DCHECK(!root_node.GetDocument().InQuirksMode());
440 456
441 const CSSSelector& selector = *selectors_[0]; 457 if (selector_id_) {
442 const CSSSelector& first_selector = selector; 458 ExecuteWithId<SelectorQueryTrait>(root_node, output);
443
444 // Fast path for querySelector*('#id'), querySelector*('tag#id'),
445 // querySelector*('tag[id=example]').
446 if (const CSSSelector* id_selector = SelectorForIdLookup(first_selector)) {
447 const AtomicString& id_to_match = id_selector->Value();
448 if (root_node.GetTreeScope().ContainsMultipleElementsWithId(id_to_match)) {
449 const HeapVector<Member<Element>>& elements =
450 root_node.GetTreeScope().GetAllElementsById(id_to_match);
451 for (const auto& element : elements) {
452 if (!element->IsDescendantOf(&root_node))
453 continue;
454 QUERY_STATS_INCREMENT(fast_id);
455 if (SelectorMatches(selector, *element, root_node)) {
456 SelectorQueryTrait::AppendElement(output, *element);
457 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement)
458 return;
459 }
460 }
461 return;
462 }
463 Element* element = root_node.GetTreeScope().GetElementById(id_to_match);
464 if (!element)
465 return;
466 if (!element->IsDescendantOf(&root_node))
467 return;
468 QUERY_STATS_INCREMENT(fast_id);
469 if (SelectorMatches(selector, *element, root_node))
470 SelectorQueryTrait::AppendElement(output, *element);
471 return; 459 return;
472 } 460 }
473 461
462 const CSSSelector& first_selector = *selectors_[0];
474 if (!first_selector.TagHistory()) { 463 if (!first_selector.TagHistory()) {
475 // Fast path for querySelector*('.foo'), and querySelector*('div'). 464 // Fast path for querySelector*('.foo'), and querySelector*('div').
476 switch (first_selector.Match()) { 465 switch (first_selector.Match()) {
477 case CSSSelector::kClass: 466 case CSSSelector::kClass:
478 CollectElementsByClassName<SelectorQueryTrait>( 467 CollectElementsByClassName<SelectorQueryTrait>(
479 root_node, first_selector.Value(), nullptr, output); 468 root_node, first_selector.Value(), nullptr, output);
480 return; 469 return;
481 case CSSSelector::kTag: 470 case CSSSelector::kTag:
482 if (first_selector.TagQName().NamespaceURI() == g_star_atom) { 471 if (first_selector.TagQName().NamespaceURI() == g_star_atom) {
483 CollectElementsByTagName<SelectorQueryTrait>( 472 CollectElementsByTagName<SelectorQueryTrait>(
(...skipping 13 matching lines...) Expand all
497 FindTraverseRootsAndExecute<SelectorQueryTrait>(root_node, output); 486 FindTraverseRootsAndExecute<SelectorQueryTrait>(root_node, output);
498 } 487 }
499 488
500 std::unique_ptr<SelectorQuery> SelectorQuery::Adopt( 489 std::unique_ptr<SelectorQuery> SelectorQuery::Adopt(
501 CSSSelectorList selector_list) { 490 CSSSelectorList selector_list) {
502 return WTF::WrapUnique(new SelectorQuery(std::move(selector_list))); 491 return WTF::WrapUnique(new SelectorQuery(std::move(selector_list)));
503 } 492 }
504 493
505 SelectorQuery::SelectorQuery(CSSSelectorList selector_list) 494 SelectorQuery::SelectorQuery(CSSSelectorList selector_list)
506 : selector_list_(std::move(selector_list)), 495 : selector_list_(std::move(selector_list)),
496 selector_id_is_rightmost_(true),
497 selector_id_affected_by_sibling_combinator_(false),
507 uses_deep_combinator_or_shadow_pseudo_(false), 498 uses_deep_combinator_or_shadow_pseudo_(false),
508 needs_updated_distribution_(false) { 499 needs_updated_distribution_(false) {
509 selectors_.ReserveInitialCapacity(selector_list_.ComputeLength()); 500 selectors_.ReserveInitialCapacity(selector_list_.ComputeLength());
510 for (const CSSSelector* selector = selector_list_.First(); selector; 501 for (const CSSSelector* selector = selector_list_.First(); selector;
511 selector = CSSSelectorList::Next(*selector)) { 502 selector = CSSSelectorList::Next(*selector)) {
512 if (selector->MatchesPseudoElement()) 503 if (selector->MatchesPseudoElement())
513 continue; 504 continue;
514 selectors_.UncheckedAppend(selector); 505 selectors_.UncheckedAppend(selector);
515 uses_deep_combinator_or_shadow_pseudo_ |= 506 uses_deep_combinator_or_shadow_pseudo_ |=
516 selector->HasDeepCombinatorOrShadowPseudo(); 507 selector->HasDeepCombinatorOrShadowPseudo();
517 needs_updated_distribution_ |= selector->NeedsUpdatedDistribution(); 508 needs_updated_distribution_ |= selector->NeedsUpdatedDistribution();
518 } 509 }
510
511 if (selectors_.size() == 1 && !uses_deep_combinator_or_shadow_pseudo_ &&
512 !needs_updated_distribution_) {
513 for (const CSSSelector* current = selectors_[0]; current;
514 current = current->TagHistory()) {
515 if (current->Match() == CSSSelector::kId) {
516 selector_id_ = current->Value();
517 break;
518 }
519 // We only use the fast path when in standards mode where #id selectors
520 // are case sensitive, so we need the same behavior for [id=value].
521 if (current->Match() == CSSSelector::kAttributeExact &&
522 current->Attribute() == idAttr &&
523 current->AttributeMatch() == CSSSelector::kCaseSensitive) {
524 selector_id_ = current->Value();
525 break;
526 }
527 if (current->Relation() == CSSSelector::kSubSelector)
528 continue;
529 selector_id_is_rightmost_ = false;
530 selector_id_affected_by_sibling_combinator_ =
531 current->Relation() == CSSSelector::kDirectAdjacent ||
532 current->Relation() == CSSSelector::kIndirectAdjacent;
533 }
534 }
519 } 535 }
520 536
521 SelectorQuery* SelectorQueryCache::Add(const AtomicString& selectors, 537 SelectorQuery* SelectorQueryCache::Add(const AtomicString& selectors,
522 const Document& document, 538 const Document& document,
523 ExceptionState& exception_state) { 539 ExceptionState& exception_state) {
524 if (selectors.IsEmpty()) { 540 if (selectors.IsEmpty()) {
525 exception_state.ThrowDOMException(kSyntaxError, 541 exception_state.ThrowDOMException(kSyntaxError,
526 "The provided selector is empty."); 542 "The provided selector is empty.");
527 return nullptr; 543 return nullptr;
528 } 544 }
(...skipping 22 matching lines...) Expand all
551 return entries_ 567 return entries_
552 .insert(selectors, SelectorQuery::Adopt(std::move(selector_list))) 568 .insert(selectors, SelectorQuery::Adopt(std::move(selector_list)))
553 .stored_value->value.get(); 569 .stored_value->value.get();
554 } 570 }
555 571
556 void SelectorQueryCache::Invalidate() { 572 void SelectorQueryCache::Invalidate() {
557 entries_.Clear(); 573 entries_.Clear();
558 } 574 }
559 575
560 } // namespace blink 576 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/dom/SelectorQuery.h ('k') | third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698