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

Unified Diff: third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp

Issue 2803103002: Add lots more tests for SelectorQuery fast paths. (Closed)
Patch Set: refs woops. 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/core/dom/SelectorQuery.cpp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
diff --git a/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp b/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
index ceb4db70937aef3e613aab4e96c25031834f25a1..d085bf4bb9384523916569cb26a5e80fa8f79b7e 100644
--- a/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
+++ b/third_party/WebKit/Source/core/dom/SelectorQueryTest.cpp
@@ -8,7 +8,9 @@
#include "core/css/parser/CSSParser.h"
#include "core/css/parser/CSSParserContext.h"
#include "core/dom/Document.h"
+#include "core/dom/ElementTraversal.h"
#include "core/dom/StaticNodeList.h"
+#include "core/dom/shadow/ElementShadow.h"
#include "core/html/HTMLDocument.h"
#include "core/html/HTMLHtmlElement.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -26,7 +28,7 @@ struct QueryTest {
};
template <unsigned length>
-void RunTests(Document& document, const QueryTest (&test_cases)[length]) {
+void RunTests(ContainerNode& scope, const QueryTest (&test_cases)[length]) {
for (const auto& test_case : test_cases) {
const char* selector = test_case.selector;
SCOPED_TRACE(testing::Message()
@@ -34,10 +36,10 @@ void RunTests(Document& document, const QueryTest (&test_cases)[length]) {
: "querySelector('")
<< selector << "')");
if (test_case.query_all) {
- StaticElementList* match_all = document.QuerySelectorAll(selector);
+ StaticElementList* match_all = scope.QuerySelectorAll(selector);
EXPECT_EQ(test_case.matches, match_all->length());
} else {
- Element* match = document.QuerySelector(selector);
+ Element* match = scope.QuerySelector(selector);
EXPECT_EQ(test_case.matches, match ? 1u : 0u);
}
#if DCHECK_IS_ON()
@@ -163,6 +165,36 @@ TEST(SelectorQueryTest, StandardsModeFastPaths) {
// a fastClass scan. It could have looked at 4 elements instead.
{"#second .two", true, 2, {14, 0, 14, 0, 0, 0, 0}},
+ // We combine the class fast path with the fast scan mode when possible.
+ {".B span", false, 1, {11, 0, 0, 0, 11, 0, 0}},
+ {".B span", true, 4, {14, 0, 10, 0, 4, 0, 0}},
+
+ // We expand the scope of id selectors when affected by an adjectent
+ // combinator.
+ {"#c + :last-child", false, 1, {4, 0, 0, 0, 4, 0, 0}},
+ {"#a ~ :last-child", false, 1, {4, 0, 0, 0, 4, 0, 0}},
+ {"#c + div", true, 1, {4, 0, 0, 0, 4, 0, 0}},
+ {"#a ~ span", true, 2, {4, 0, 0, 0, 4, 0, 0}},
+
+ // We only expand the scope for id selectors if they're directly affected
+ // the adjacent combinator.
+ {"#first span + span", false, 1, {2, 0, 0, 0, 2, 0, 0}},
+ {"#first span ~ span", false, 1, {2, 0, 0, 0, 2, 0, 0}},
+ {"#second span + span", true, 3, {4, 0, 0, 0, 4, 0, 0}},
+ {"#second span ~ span", true, 3, {4, 0, 0, 0, 4, 0, 0}},
+
+ // We disable the fast path for class selectors when affected by adjacent
+ // combinator.
+ {".one + :last-child", false, 1, {8, 0, 0, 0, 8, 0, 0}},
+ {".A ~ :last-child", false, 1, {9, 0, 0, 0, 9, 0, 0}},
+ {".A + div", true, 1, {14, 0, 0, 0, 14, 0, 0}},
+ {".one ~ span", true, 5, {14, 0, 0, 0, 14, 0, 0}},
+
+ // We re-enable the fast path for classes once past the selector directly
+ // affected by the adjacent combinator.
+ {".B span + span", true, 3, {14, 0, 10, 0, 4, 0, 0}},
+ {".B span ~ span", true, 3, {14, 0, 10, 0, 4, 0, 0}},
+
// Selectors with no classes or ids use the fast scan.
{":scope", false, 1, {1, 0, 0, 0, 1, 0, 0}},
{":scope", true, 1, {14, 0, 0, 0, 14, 0, 0}},
@@ -185,6 +217,75 @@ TEST(SelectorQueryTest, StandardsModeFastPaths) {
RunTests(*document, kTestCases);
}
+TEST(SelectorQueryTest, FastPathScoped) {
+ Document* document = HTMLDocument::Create();
+ document->write(
+ "<!DOCTYPE html>"
+ "<html id=root-id class=root-class>"
+ " <head></head>"
+ " <body>"
+ " <span id=first>"
+ " <span id=A class='a child'></span>"
+ " <span id=B class='a child'>"
+ " <a class=first></a>"
+ " <a class=second></a>"
+ " <a class=third></a>"
+ " </span>"
+ " <span id=multiple class='b child'></span>"
+ " <span id=multiple class='c child'></span>"
+ " </span>"
+ " </body>"
+ "</html>");
+ // TODO(esprehn): Element::attachShadow() should not require a ScriptState,
+ // it should handle the use counting in the bindings layer instead of in the
+ // C++.
+ Element* scope = document->getElementById("first");
+ ASSERT_NE(nullptr, scope);
+ ShadowRoot& shadowRoot =
+ scope->EnsureShadow().AddShadowRoot(*scope, ShadowRootType::kOpen);
+ // Make the inside the shadow root be identical to that of the outer document.
+ shadowRoot.appendChild(
+ document->documentElement()->CloneElementWithChildren());
+ static const struct QueryTest kTestCases[] = {
+ // Id in the right most selector.
+ {"#first", false, 0, {0, 0, 0, 0, 0, 0, 0}},
+
+ {"#B", false, 1, {1, 1, 0, 0, 0, 0, 0}},
+ {"#multiple", false, 1, {1, 1, 0, 0, 0, 0, 0}},
+ {"#multiple.c", false, 1, {2, 2, 0, 0, 0, 0, 0}},
+
+ // Class in the right most selector.
+ {".child", false, 1, {1, 0, 1, 0, 0, 0, 0}},
+ {".child", true, 4, {7, 0, 7, 0, 0, 0, 0}},
+
+ // If an ancestor has the class name we fast scan all the descendants of
+ // the scope.
+ {".root-class span", true, 4, {7, 0, 0, 0, 7, 0, 0}},
+
+ // If an ancestor has the class name in the middle of the selector we fast
+ // scan all the descendants of the scope.
+ {".root-class span:nth-child(2)", false, 1, {2, 0, 0, 0, 2, 0, 0}},
+ {".root-class span:nth-child(2)", true, 1, {7, 0, 0, 0, 7, 0, 0}},
+
+ // If the id is an ancestor we scan all the descendants.
+ {"#root-id span", true, 4, {7, 0, 0, 0, 7, 0, 0}},
+ };
+
+ {
+ SCOPED_TRACE("Inside document");
+ RunTests(*scope, kTestCases);
+ }
+
+ {
+ // Run all the tests a second time but with a scope inside a shadow root,
+ // all the fast paths should behave the same.
+ SCOPED_TRACE("Inside shadow root");
+ scope = shadowRoot.getElementById("first");
+ ASSERT_NE(nullptr, scope);
+ RunTests(*scope, kTestCases);
+ }
+}
+
TEST(SelectorQueryTest, QuirksModeSlowPath) {
Document* document = HTMLDocument::Create();
document->write(
@@ -213,4 +314,50 @@ TEST(SelectorQueryTest, QuirksModeSlowPath) {
RunTests(*document, kTestCases);
}
+TEST(SelectorQueryTest, DisconnectedSubtreeSlowPath) {
+ Document* document = HTMLDocument::Create();
+ Element* scope = document->createElement("div");
+ scope->setInnerHTML(
+ "<section>"
+ " <span id=first>"
+ " <span id=A class=A></span>"
+ " <span id=B class=child></span>"
+ " <span id=multiple class=child></span>"
+ " <span id=multiple class=B></span>"
+ " </span>"
+ "</section>");
+ ShadowRoot& shadowRoot =
+ scope->EnsureShadow().AddShadowRoot(*scope, ShadowRootType::kOpen);
+ // Make the inside the ShadowRoot look identical to the outer document.
+ shadowRoot.appendChild(
+ ElementTraversal::FirstChild(*scope)->CloneElementWithChildren());
+ static const struct QueryTest kTestCases[] = {
+ // TODO(esprehn): Disconnected subtrees always uses the slow path, but
+ // we can actually use it in a number of cases, for example using the id
+ // map for things inside a tree scope, or using the fast class scanning
+ // always.
+ {"#A", false, 1, {3, 0, 0, 0, 0, 3, 0}},
+ {"#B", false, 1, {4, 0, 0, 0, 0, 4, 0}},
+ {"#B", true, 1, {6, 0, 0, 0, 0, 6, 0}},
+ {"#multiple", true, 2, {6, 0, 0, 0, 0, 6, 0}},
+ {".child", false, 1, {4, 0, 0, 0, 0, 4, 0}},
+ {".child", true, 2, {6, 0, 0, 0, 0, 6, 0}},
+ {"#first span", false, 1, {3, 0, 0, 0, 0, 3, 0}},
+ {"#first span", true, 4, {6, 0, 0, 0, 0, 6, 0}},
+ };
+
+ {
+ SCOPED_TRACE("Inside disconnected subtree");
+ RunTests(*scope, kTestCases);
+ }
+
+ {
+ // Run all the tests a second time but with a scope inside a shadow root,
+ // this tests for cases where we could have used the id map the ShadowRoot
+ // is keeping track of.
+ SCOPED_TRACE("Inside disconnected shadow root subtree");
+ RunTests(shadowRoot, kTestCases);
+ }
+}
+
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/core/dom/SelectorQuery.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698