| OLD | NEW |
| 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 23 matching lines...) Expand all Loading... |
| 34 #include "core/dom/Document.h" | 34 #include "core/dom/Document.h" |
| 35 #include "core/dom/ElementTraversal.h" | 35 #include "core/dom/ElementTraversal.h" |
| 36 #include "core/dom/ExceptionCode.h" | 36 #include "core/dom/ExceptionCode.h" |
| 37 #include "core/dom/Node.h" | 37 #include "core/dom/Node.h" |
| 38 #include "core/dom/NthIndexCache.h" | 38 #include "core/dom/NthIndexCache.h" |
| 39 #include "core/dom/StaticNodeList.h" | 39 #include "core/dom/StaticNodeList.h" |
| 40 #include "core/dom/shadow/ElementShadow.h" | 40 #include "core/dom/shadow/ElementShadow.h" |
| 41 #include "core/dom/shadow/ShadowRoot.h" | 41 #include "core/dom/shadow/ShadowRoot.h" |
| 42 #include "platform/wtf/PtrUtil.h" | 42 #include "platform/wtf/PtrUtil.h" |
| 43 | 43 |
| 44 // Uncomment to run the SelectorQueryTests for stats in a release build. |
| 45 // #define RELEASE_QUERY_STATS |
| 46 |
| 44 namespace blink { | 47 namespace blink { |
| 45 | 48 |
| 46 using namespace HTMLNames; | 49 using namespace HTMLNames; |
| 47 | 50 |
| 48 #if DCHECK_IS_ON() | 51 #if DCHECK_IS_ON() || defined(RELEASE_QUERY_STATS) |
| 49 static SelectorQuery::QueryStats& CurrentQueryStats() { | 52 static SelectorQuery::QueryStats& CurrentQueryStats() { |
| 50 DEFINE_STATIC_LOCAL(SelectorQuery::QueryStats, stats, ()); | 53 DEFINE_STATIC_LOCAL(SelectorQuery::QueryStats, stats, ()); |
| 51 return stats; | 54 return stats; |
| 52 } | 55 } |
| 53 | 56 |
| 54 SelectorQuery::QueryStats SelectorQuery::LastQueryStats() { | 57 SelectorQuery::QueryStats SelectorQuery::LastQueryStats() { |
| 55 return CurrentQueryStats(); | 58 return CurrentQueryStats(); |
| 56 } | 59 } |
| 57 | 60 |
| 58 #define QUERY_STATS_INCREMENT(name) \ | 61 #define QUERY_STATS_INCREMENT(name) \ |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 for (Element& element : ElementTraversal::DescendantsOf(root_node)) { | 193 for (Element& element : ElementTraversal::DescendantsOf(root_node)) { |
| 191 QUERY_STATS_INCREMENT(fast_tag_name); | 194 QUERY_STATS_INCREMENT(fast_tag_name); |
| 192 if (MatchesTagName(tag_name, element)) { | 195 if (MatchesTagName(tag_name, element)) { |
| 193 SelectorQueryTrait::AppendElement(output, element); | 196 SelectorQueryTrait::AppendElement(output, element); |
| 194 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement) | 197 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement) |
| 195 return; | 198 return; |
| 196 } | 199 } |
| 197 } | 200 } |
| 198 } | 201 } |
| 199 | 202 |
| 200 inline bool SelectorQuery::CanUseFastQuery( | |
| 201 const ContainerNode& root_node) const { | |
| 202 if (uses_deep_combinator_or_shadow_pseudo_) | |
| 203 return false; | |
| 204 if (needs_updated_distribution_) | |
| 205 return false; | |
| 206 if (root_node.GetDocument().InQuirksMode()) | |
| 207 return false; | |
| 208 if (!root_node.isConnected()) | |
| 209 return false; | |
| 210 return selectors_.size() == 1; | |
| 211 } | |
| 212 | |
| 213 inline bool AncestorHasClassName(ContainerNode& root_node, | 203 inline bool AncestorHasClassName(ContainerNode& root_node, |
| 214 const AtomicString& class_name) { | 204 const AtomicString& class_name) { |
| 215 if (!root_node.IsElementNode()) | 205 if (!root_node.IsElementNode()) |
| 216 return false; | 206 return false; |
| 217 | 207 |
| 218 for (Element* element = &ToElement(root_node); element; | 208 for (Element* element = &ToElement(root_node); element; |
| 219 element = element->parentElement()) { | 209 element = element->parentElement()) { |
| 220 if (HasClassName(*element, class_name)) | 210 if (HasClassName(*element, class_name)) |
| 221 return true; | 211 return true; |
| 222 } | 212 } |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 380 SelectorQueryTrait::AppendElement(output, *element); | 370 SelectorQueryTrait::AppendElement(output, *element); |
| 381 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement) | 371 if (SelectorQueryTrait::kShouldOnlyMatchFirstElement) |
| 382 return; | 372 return; |
| 383 } | 373 } |
| 384 } | 374 } |
| 385 | 375 |
| 386 template <typename SelectorQueryTrait> | 376 template <typename SelectorQueryTrait> |
| 387 void SelectorQuery::ExecuteWithId( | 377 void SelectorQuery::ExecuteWithId( |
| 388 ContainerNode& root_node, | 378 ContainerNode& root_node, |
| 389 typename SelectorQueryTrait::OutputType& output) const { | 379 typename SelectorQueryTrait::OutputType& output) const { |
| 380 DCHECK_EQ(selectors_.size(), 1u); |
| 381 DCHECK(!root_node.GetDocument().InQuirksMode()); |
| 382 |
| 390 const CSSSelector& first_selector = *selectors_[0]; | 383 const CSSSelector& first_selector = *selectors_[0]; |
| 391 const TreeScope& scope = root_node.ContainingTreeScope(); | 384 const TreeScope& scope = root_node.ContainingTreeScope(); |
| 392 | 385 |
| 393 if (scope.ContainsMultipleElementsWithId(selector_id_)) { | 386 if (scope.ContainsMultipleElementsWithId(selector_id_)) { |
| 394 // We don't currently handle cases where there's multiple elements with the | 387 // We don't currently handle cases where there's multiple elements with the |
| 395 // id and it's not in the rightmost selector. | 388 // id and it's not in the rightmost selector. |
| 396 if (!selector_id_is_rightmost_) { | 389 if (!selector_id_is_rightmost_) { |
| 397 FindTraverseRootsAndExecute<SelectorQueryTrait>(root_node, output); | 390 FindTraverseRootsAndExecute<SelectorQueryTrait>(root_node, output); |
| 398 return; | 391 return; |
| 399 } | 392 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 ExecuteForTraverseRoot<SelectorQueryTrait>(*start, root_node, output); | 426 ExecuteForTraverseRoot<SelectorQueryTrait>(*start, root_node, output); |
| 434 } | 427 } |
| 435 | 428 |
| 436 template <typename SelectorQueryTrait> | 429 template <typename SelectorQueryTrait> |
| 437 void SelectorQuery::Execute( | 430 void SelectorQuery::Execute( |
| 438 ContainerNode& root_node, | 431 ContainerNode& root_node, |
| 439 typename SelectorQueryTrait::OutputType& output) const { | 432 typename SelectorQueryTrait::OutputType& output) const { |
| 440 if (selectors_.IsEmpty()) | 433 if (selectors_.IsEmpty()) |
| 441 return; | 434 return; |
| 442 | 435 |
| 443 if (!CanUseFastQuery(root_node)) { | 436 if (use_slow_scan_) { |
| 444 if (needs_updated_distribution_) | 437 if (needs_updated_distribution_) |
| 445 root_node.UpdateDistribution(); | 438 root_node.UpdateDistribution(); |
| 446 if (uses_deep_combinator_or_shadow_pseudo_) { | 439 if (uses_deep_combinator_or_shadow_pseudo_) { |
| 447 ExecuteSlowTraversingShadowTree<SelectorQueryTrait>(root_node, output); | 440 ExecuteSlowTraversingShadowTree<SelectorQueryTrait>(root_node, output); |
| 448 } else { | 441 } else { |
| 449 ExecuteSlow<SelectorQueryTrait>(root_node, output); | 442 ExecuteSlow<SelectorQueryTrait>(root_node, output); |
| 450 } | 443 } |
| 451 return; | 444 return; |
| 452 } | 445 } |
| 453 | 446 |
| 454 DCHECK_EQ(selectors_.size(), 1u); | 447 DCHECK_EQ(selectors_.size(), 1u); |
| 455 DCHECK(!root_node.GetDocument().InQuirksMode()); | 448 DCHECK(!needs_updated_distribution_); |
| 449 DCHECK(!uses_deep_combinator_or_shadow_pseudo_); |
| 456 | 450 |
| 457 if (selector_id_) { | 451 // In quirks mode getElementById("a") is case sensitive and should only |
| 452 // match elements with lowercase id "a", but querySelector is case-insensitive |
| 453 // so querySelector("#a") == querySelector("#A"), which means we can only use |
| 454 // the id fast path when we're in a standards mode document. |
| 455 if (selector_id_ && root_node.IsInTreeScope() && |
| 456 !root_node.GetDocument().InQuirksMode()) { |
| 458 ExecuteWithId<SelectorQueryTrait>(root_node, output); | 457 ExecuteWithId<SelectorQueryTrait>(root_node, output); |
| 459 return; | 458 return; |
| 460 } | 459 } |
| 461 | 460 |
| 462 const CSSSelector& first_selector = *selectors_[0]; | 461 const CSSSelector& first_selector = *selectors_[0]; |
| 463 if (!first_selector.TagHistory()) { | 462 if (!first_selector.TagHistory()) { |
| 464 // Fast path for querySelector*('.foo'), and querySelector*('div'). | 463 // Fast path for querySelector*('.foo'), and querySelector*('div'). |
| 465 switch (first_selector.Match()) { | 464 switch (first_selector.Match()) { |
| 466 case CSSSelector::kClass: | 465 case CSSSelector::kClass: |
| 467 CollectElementsByClassName<SelectorQueryTrait>( | 466 CollectElementsByClassName<SelectorQueryTrait>( |
| (...skipping 21 matching lines...) Expand all Loading... |
| 489 std::unique_ptr<SelectorQuery> SelectorQuery::Adopt( | 488 std::unique_ptr<SelectorQuery> SelectorQuery::Adopt( |
| 490 CSSSelectorList selector_list) { | 489 CSSSelectorList selector_list) { |
| 491 return WTF::WrapUnique(new SelectorQuery(std::move(selector_list))); | 490 return WTF::WrapUnique(new SelectorQuery(std::move(selector_list))); |
| 492 } | 491 } |
| 493 | 492 |
| 494 SelectorQuery::SelectorQuery(CSSSelectorList selector_list) | 493 SelectorQuery::SelectorQuery(CSSSelectorList selector_list) |
| 495 : selector_list_(std::move(selector_list)), | 494 : selector_list_(std::move(selector_list)), |
| 496 selector_id_is_rightmost_(true), | 495 selector_id_is_rightmost_(true), |
| 497 selector_id_affected_by_sibling_combinator_(false), | 496 selector_id_affected_by_sibling_combinator_(false), |
| 498 uses_deep_combinator_or_shadow_pseudo_(false), | 497 uses_deep_combinator_or_shadow_pseudo_(false), |
| 499 needs_updated_distribution_(false) { | 498 needs_updated_distribution_(false), |
| 499 use_slow_scan_(true) { |
| 500 selectors_.ReserveInitialCapacity(selector_list_.ComputeLength()); | 500 selectors_.ReserveInitialCapacity(selector_list_.ComputeLength()); |
| 501 for (const CSSSelector* selector = selector_list_.First(); selector; | 501 for (const CSSSelector* selector = selector_list_.First(); selector; |
| 502 selector = CSSSelectorList::Next(*selector)) { | 502 selector = CSSSelectorList::Next(*selector)) { |
| 503 if (selector->MatchesPseudoElement()) | 503 if (selector->MatchesPseudoElement()) |
| 504 continue; | 504 continue; |
| 505 selectors_.UncheckedAppend(selector); | 505 selectors_.UncheckedAppend(selector); |
| 506 uses_deep_combinator_or_shadow_pseudo_ |= | 506 uses_deep_combinator_or_shadow_pseudo_ |= |
| 507 selector->HasDeepCombinatorOrShadowPseudo(); | 507 selector->HasDeepCombinatorOrShadowPseudo(); |
| 508 needs_updated_distribution_ |= selector->NeedsUpdatedDistribution(); | 508 needs_updated_distribution_ |= selector->NeedsUpdatedDistribution(); |
| 509 } | 509 } |
| 510 | 510 |
| 511 if (selectors_.size() == 1 && !uses_deep_combinator_or_shadow_pseudo_ && | 511 if (selectors_.size() == 1 && !uses_deep_combinator_or_shadow_pseudo_ && |
| 512 !needs_updated_distribution_) { | 512 !needs_updated_distribution_) { |
| 513 use_slow_scan_ = false; |
| 513 for (const CSSSelector* current = selectors_[0]; current; | 514 for (const CSSSelector* current = selectors_[0]; current; |
| 514 current = current->TagHistory()) { | 515 current = current->TagHistory()) { |
| 515 if (current->Match() == CSSSelector::kId) { | 516 if (current->Match() == CSSSelector::kId) { |
| 516 selector_id_ = current->Value(); | 517 selector_id_ = current->Value(); |
| 517 break; | 518 break; |
| 518 } | 519 } |
| 519 // We only use the fast path when in standards mode where #id selectors | 520 // 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 // are case sensitive, so we need the same behavior for [id=value]. |
| 521 if (current->Match() == CSSSelector::kAttributeExact && | 522 if (current->Match() == CSSSelector::kAttributeExact && |
| 522 current->Attribute() == idAttr && | 523 current->Attribute() == idAttr && |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 567 return entries_ | 568 return entries_ |
| 568 .insert(selectors, SelectorQuery::Adopt(std::move(selector_list))) | 569 .insert(selectors, SelectorQuery::Adopt(std::move(selector_list))) |
| 569 .stored_value->value.get(); | 570 .stored_value->value.get(); |
| 570 } | 571 } |
| 571 | 572 |
| 572 void SelectorQueryCache::Invalidate() { | 573 void SelectorQueryCache::Invalidate() { |
| 573 entries_.clear(); | 574 entries_.clear(); |
| 574 } | 575 } |
| 575 | 576 |
| 576 } // namespace blink | 577 } // namespace blink |
| OLD | NEW |