| 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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |