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

Unified Diff: third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversalTest.cpp

Issue 2432293002: Fix focus navigation for nested slot case (Closed)
Patch Set: More references Created 4 years, 1 month 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
Index: third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversalTest.cpp
diff --git a/third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversalTest.cpp b/third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversalTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa3e4d6703ac0bf92e00b35f47242cd65ea13d5b
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversalTest.cpp
@@ -0,0 +1,411 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/dom/shadow/SlotScopedTraversal.h"
+
+#include "bindings/core/v8/ExceptionStatePlaceholder.h"
+#include "core/dom/Document.h"
+#include "core/dom/Element.h"
+#include "core/dom/Node.h"
+#include "core/dom/NodeTraversal.h"
+#include "core/dom/shadow/ShadowRoot.h"
+#include "core/html/HTMLElement.h"
+#include "core/html/HTMLSlotElement.h"
+#include "core/testing/DummyPageHolder.h"
+#include "platform/geometry/IntSize.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/Vector.h"
+#include <memory>
+
+namespace blink {
+
+class SlotScopedTraversalTest : public ::testing::Test {
+ protected:
+ Document& document() const;
+
+ void setupSampleHTML(const char* mainHTML, const char* shadowHTML, unsigned);
+ void attachOpenShadowRoot(Element& shadowHost, const char* shadowInnerHTML);
+ void testExpectedSequence(const Vector<Element*>& list);
+
+ private:
+ void SetUp() override;
+
+ Persistent<Document> m_document;
+ std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
+};
+
+void SlotScopedTraversalTest::SetUp() {
+ m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
+ m_document = &m_dummyPageHolder->document();
+ DCHECK(m_document);
+}
+
+Document& SlotScopedTraversalTest::document() const {
+ return *m_document;
+}
+
+void SlotScopedTraversalTest::setupSampleHTML(const char* mainHTML,
+ const char* shadowHTML,
+ unsigned index) {
+ Element* body = document().body();
+ body->setInnerHTML(String::fromUTF8(mainHTML), ASSERT_NO_EXCEPTION);
+ if (shadowHTML) {
+ Element* shadowHost = toElement(NodeTraversal::childAt(*body, index));
+ attachOpenShadowRoot(*shadowHost, shadowHTML);
+ }
+}
+
+void SlotScopedTraversalTest::attachOpenShadowRoot(
+ Element& shadowHost,
+ const char* shadowInnerHTML) {
+ ShadowRoot* shadowRoot = shadowHost.createShadowRootInternal(
+ ShadowRootType::Open, ASSERT_NO_EXCEPTION);
+ shadowRoot->setInnerHTML(String::fromUTF8(shadowInnerHTML),
+ ASSERT_NO_EXCEPTION);
+ document().body()->updateDistribution();
+}
+
+TEST_F(SlotScopedTraversalTest, emptySlot) {
+ const char* mainHTML = "<div id='host'></div>";
+ const char* shadowHTML = "<slot></slot>";
+ setupSampleHTML(mainHTML, shadowHTML, 0);
+
+ Element* host = document().querySelector("#host");
+ ShadowRoot* shadowRoot = host->openShadowRoot();
+ Element* slotElement = shadowRoot->querySelector("slot");
+ DCHECK(isHTMLSlotElement(slotElement));
+ HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::lastAssignedToSlot(*slot));
+}
+
+void SlotScopedTraversalTest::testExpectedSequence(
+ const Vector<Element*>& list) {
+ for (size_t i = 0; i < list.size(); ++i) {
+ const Element* expected = i == list.size() - 1 ? nullptr : list[i + 1];
+ EXPECT_EQ(expected, SlotScopedTraversal::next(*list[i]));
+ }
+
+ for (size_t i = list.size(); i > 0; --i) {
+ const Element* expected = i == 1 ? nullptr : list[i - 2];
+ EXPECT_EQ(expected, SlotScopedTraversal::previous(*list[i - 1]));
+ }
+}
+
+TEST_F(SlotScopedTraversalTest, simpleSlot) {
+ const char* mainHTML =
+ "<div id='host'>"
+ "<div id='inner1'></div>"
+ "<div id='inner2'></div>"
+ "</div>";
+
+ const char* shadowHTML = "<slot></slot>";
+
+ setupSampleHTML(mainHTML, shadowHTML, 0);
+
+ Element* host = document().querySelector("#host");
+ Element* inner1 = document().querySelector("#inner1");
+ Element* inner2 = document().querySelector("#inner2");
+ ShadowRoot* shadowRoot = host->openShadowRoot();
+ Element* slotElement = shadowRoot->querySelector("slot");
+ DCHECK(isHTMLSlotElement(slotElement));
+ HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
+
+ EXPECT_EQ(inner1, SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(inner2, SlotScopedTraversal::lastAssignedToSlot(*slot));
+ EXPECT_FALSE(SlotScopedTraversal::isSlotScoped(*host));
+ EXPECT_FALSE(SlotScopedTraversal::isSlotScoped(*slot));
+ EXPECT_TRUE(SlotScopedTraversal::isSlotScoped(*inner1));
+ EXPECT_TRUE(SlotScopedTraversal::isSlotScoped(*inner2));
+ EXPECT_EQ(inner1, SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(
+ *inner1));
+ EXPECT_EQ(inner2, SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(
+ *inner2));
+ EXPECT_EQ(slot, SlotScopedTraversal::findScopeOwnerSlot(*inner1));
+ EXPECT_EQ(slot, SlotScopedTraversal::findScopeOwnerSlot(*inner2));
+
+ Vector<Element*> expectedSequence({inner1, inner2});
+ testExpectedSequence(expectedSequence);
+}
+
+TEST_F(SlotScopedTraversalTest, multipleSlots) {
+ const char* mainHTML =
+ "<div id='host'>"
+ "<div id='inner0' slot='slot0'></div>"
+ "<div id='inner1' slot='slot1'></div>"
+ "<div id='inner2'></div>"
+ "<div id='inner3' slot='slot1'></div>"
+ "<div id='inner4' slot='slot0'></div>"
+ "<div id='inner5'></div>"
+ "</div>";
+
+ const char* shadowHTML =
+ "<slot id='unnamedslot'></slot>"
+ "<slot id='slot0' name='slot0'></slot>"
+ "<slot id='slot1' name='slot1'></slot>";
+
+ setupSampleHTML(mainHTML, shadowHTML, 0);
+
+ Element* host = document().querySelector("#host");
+ Element* inner[6];
+ inner[0] = document().querySelector("#inner0");
+ inner[1] = document().querySelector("#inner1");
+ inner[2] = document().querySelector("#inner2");
+ inner[3] = document().querySelector("#inner3");
+ inner[4] = document().querySelector("#inner4");
+ inner[5] = document().querySelector("#inner5");
+
+ ShadowRoot* shadowRoot = host->openShadowRoot();
+ Element* slotElement[3];
+ slotElement[0] = shadowRoot->querySelector("#slot0");
+ slotElement[1] = shadowRoot->querySelector("#slot1");
+ slotElement[2] = shadowRoot->querySelector("#unnamedslot");
+
+ HTMLSlotElement* slot[3];
+ slot[0] = toHTMLSlotElement(slotElement[0]);
+ slot[1] = toHTMLSlotElement(slotElement[1]);
+ slot[2] = toHTMLSlotElement(slotElement[2]);
+
+ {
+ // <slot id='slot0'> : Expected assigned nodes: inner0, inner4
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot[0]));
+ EXPECT_EQ(inner[4], SlotScopedTraversal::lastAssignedToSlot(*slot[0]));
+ Vector<Element*> expectedSequence({inner[0], inner[4]});
+ testExpectedSequence(expectedSequence);
+ }
+
+ {
+ // <slot name='slot1'> : Expected assigned nodes: inner1, inner3
+ EXPECT_EQ(inner[1], SlotScopedTraversal::firstAssignedToSlot(*slot[1]));
+ EXPECT_EQ(inner[3], SlotScopedTraversal::lastAssignedToSlot(*slot[1]));
+ Vector<Element*> expectedSequence({inner[1], inner[3]});
+ testExpectedSequence(expectedSequence);
+ }
+
+ {
+ // <slot id='unnamedslot'> : Expected assigned nodes: inner2, inner5
+ EXPECT_EQ(inner[2], SlotScopedTraversal::firstAssignedToSlot(*slot[2]));
+ EXPECT_EQ(inner[5], SlotScopedTraversal::lastAssignedToSlot(*slot[2]));
+ Vector<Element*> expectedSequence({inner[2], inner[5]});
+ testExpectedSequence(expectedSequence);
+ }
+}
+
+TEST_F(SlotScopedTraversalTest, shadowHostAtTopLevel) {
+ // This covers a shadow host is directly assigned to a slot.
+ //
+ // We build the following tree:
+ // host
+ // |
+ // ShadowRoot
+ // |
+ // ____<slot>____
+ // | | |
+ // inner0 inner1 inner2
+ // | | |
+ // child0 child1 child2
+ //
+ // And iterate on inner0, inner1, and inner2 to attach shadow on each of
+ // them, and check if elements that are slotted to another slot are skipped
+ // in traversal.
+
+ const char* mainHTML =
+ "<div id='host'>"
+ "<div id='inner0'><div id='child0'></div></div>"
+ "<div id='inner1'><div id='child1'></div></div>"
+ "<div id='inner2'><div id='child2'></div></div>"
+ "</div>";
+
+ const char* shadowHTML = "<slot></slot>";
+
+ for (int i = 0; i < 3; ++i) {
+ setupSampleHTML(mainHTML, shadowHTML, 0);
+
+ Element* host = document().querySelector("#host");
+ Element* inner[3];
+ inner[0] = document().querySelector("#inner0");
+ inner[1] = document().querySelector("#inner1");
+ inner[2] = document().querySelector("#inner2");
+ Element* child[3];
+ child[0] = document().querySelector("#child0");
+ child[1] = document().querySelector("#child1");
+ child[2] = document().querySelector("#child2");
+
+ attachOpenShadowRoot(*inner[i], shadowHTML);
+
+ ShadowRoot* shadowRoot = host->openShadowRoot();
+ Element* slotElement = shadowRoot->querySelector("slot");
+ DCHECK(isHTMLSlotElement(slotElement));
+ HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
+
+ switch (i) {
+ case 0: {
+ // inner0 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(child[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[0]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[0]));
+
+ Vector<Element*> expectedSequence(
+ {inner[0], inner[1], child[1], inner[2], child[2]});
+ testExpectedSequence(expectedSequence);
+ } break;
+
+ case 1: {
+ // inner1 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(child[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[1]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[1]));
+
+ Vector<Element*> expectedSequence(
+ {inner[0], child[0], inner[1], inner[2], child[2]});
+ testExpectedSequence(expectedSequence);
+ } break;
+
+ case 2: {
+ // inner2 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(inner[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[2]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[2]));
+
+ Vector<Element*> expectedSequence(
+ {inner[0], child[0], inner[1], child[1], inner[2]});
+ testExpectedSequence(expectedSequence);
+ } break;
+ }
+ }
+}
+
+TEST_F(SlotScopedTraversalTest, shadowHostAtSecondLevel) {
+ // This covers cases where a shadow host exists in a child of a slotted
+ // element.
+ //
+ // We build the following tree:
+ // host
+ // |
+ // ShadowRoot
+ // |
+ // ____<slot>____
+ // | |
+ // _inner0_ _inner1_
+ // | | | |
+ // child0 child1 child2 child3
+ // | | | |
+ // span0 span1 span2 span3
+ //
+ // And iterate on child0, child1, child2, and child3 to attach shadow on each
+ // of them, and check if elements that are slotted to another slot are skipped
+ // in traversal.
+
+ const char* mainHTML =
+ "<div id='host'>"
+ "<div id='inner0'>"
+ "<div id='child0'><span id='span0'></span></div>"
+ "<div id='child1'><span id='span1'></span></div>"
+ "</div>"
+ "<div id='inner1'>"
+ "<div id='child2'><span id='span2'></span></div>"
+ "<div id='child3'><span id='span3'></span></div>"
+ "</div>"
+ "</div>";
+
+ const char* shadowHTML = "<slot></slot>";
+
+ for (int i = 0; i < 4; ++i) {
+ setupSampleHTML(mainHTML, shadowHTML, 0);
+
+ Element* host = document().querySelector("#host");
+ Element* inner[2];
+ inner[0] = document().querySelector("#inner0");
+ inner[1] = document().querySelector("#inner1");
+ Element* child[4];
+ child[0] = document().querySelector("#child0");
+ child[1] = document().querySelector("#child1");
+ child[2] = document().querySelector("#child2");
+ child[3] = document().querySelector("#child3");
+ Element* span[4];
+ span[0] = document().querySelector("#span0");
+ span[1] = document().querySelector("#span1");
+ span[2] = document().querySelector("#span2");
+ span[3] = document().querySelector("#span3");
+
+ attachOpenShadowRoot(*child[i], shadowHTML);
+
+ for (int j = 0; j < 4; ++j) {
+ DCHECK(child[i]);
+ DCHECK(span[i]);
+ }
+
+ ShadowRoot* shadowRoot = host->openShadowRoot();
+ Element* slotElement = shadowRoot->querySelector("slot");
+ DCHECK(isHTMLSlotElement(slotElement));
+ HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
+
+ switch (i) {
+ case 0: {
+ // child0 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[0]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[0]));
+
+ const Vector<Element*> expectedSequence({inner[0], child[0], child[1],
+ span[1], inner[1], child[2],
+ span[2], child[3], span[3]});
+ testExpectedSequence(expectedSequence);
+ } break;
+
+ case 1: {
+ // child1 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[1]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[1]));
+
+ const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
+ child[1], inner[1], child[2],
+ span[2], child[3], span[3]});
+ testExpectedSequence(expectedSequence);
+ } break;
+
+ case 2: {
+ // child2 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[2]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[2]));
+
+ const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
+ child[1], span[1], inner[1],
+ child[2], child[3], span[3]});
+ testExpectedSequence(expectedSequence);
+ } break;
+
+ case 3: {
+ // child3 is a shadow host.
+ EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
+ EXPECT_EQ(child[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
+
+ EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[3]));
+ EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[3]));
+
+ const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
+ child[1], span[1], inner[1],
+ child[2], span[2], child[3]});
+ testExpectedSequence(expectedSequence);
+ } break;
+ }
+ }
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698