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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/dom/shadow/SlotScopedTraversal.h"
6
7 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
8 #include "core/dom/Document.h"
9 #include "core/dom/Element.h"
10 #include "core/dom/Node.h"
11 #include "core/dom/NodeTraversal.h"
12 #include "core/dom/shadow/ShadowRoot.h"
13 #include "core/html/HTMLElement.h"
14 #include "core/html/HTMLSlotElement.h"
15 #include "core/testing/DummyPageHolder.h"
16 #include "platform/geometry/IntSize.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "wtf/Vector.h"
19 #include <memory>
20
21 namespace blink {
22
23 class SlotScopedTraversalTest : public ::testing::Test {
24 protected:
25 Document& document() const;
26
27 void setupSampleHTML(const char* mainHTML, const char* shadowHTML, unsigned);
28 void attachOpenShadowRoot(Element& shadowHost, const char* shadowInnerHTML);
29 void testExpectedSequence(const Vector<Element*>& list);
30
31 private:
32 void SetUp() override;
33
34 Persistent<Document> m_document;
35 std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
36 };
37
38 void SlotScopedTraversalTest::SetUp() {
39 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
40 m_document = &m_dummyPageHolder->document();
41 DCHECK(m_document);
42 }
43
44 Document& SlotScopedTraversalTest::document() const {
45 return *m_document;
46 }
47
48 void SlotScopedTraversalTest::setupSampleHTML(const char* mainHTML,
49 const char* shadowHTML,
50 unsigned index) {
51 Element* body = document().body();
52 body->setInnerHTML(String::fromUTF8(mainHTML), ASSERT_NO_EXCEPTION);
53 if (shadowHTML) {
54 Element* shadowHost = toElement(NodeTraversal::childAt(*body, index));
55 attachOpenShadowRoot(*shadowHost, shadowHTML);
56 }
57 }
58
59 void SlotScopedTraversalTest::attachOpenShadowRoot(
60 Element& shadowHost,
61 const char* shadowInnerHTML) {
62 ShadowRoot* shadowRoot = shadowHost.createShadowRootInternal(
63 ShadowRootType::Open, ASSERT_NO_EXCEPTION);
64 shadowRoot->setInnerHTML(String::fromUTF8(shadowInnerHTML),
65 ASSERT_NO_EXCEPTION);
66 document().body()->updateDistribution();
67 }
68
69 TEST_F(SlotScopedTraversalTest, emptySlot) {
70 const char* mainHTML = "<div id='host'></div>";
71 const char* shadowHTML = "<slot></slot>";
72 setupSampleHTML(mainHTML, shadowHTML, 0);
73
74 Element* host = document().querySelector("#host");
75 ShadowRoot* shadowRoot = host->openShadowRoot();
76 Element* slotElement = shadowRoot->querySelector("slot");
77 DCHECK(isHTMLSlotElement(slotElement));
78 HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
79
80 EXPECT_EQ(nullptr, SlotScopedTraversal::firstAssignedToSlot(*slot));
81 EXPECT_EQ(nullptr, SlotScopedTraversal::lastAssignedToSlot(*slot));
82 }
83
84 void SlotScopedTraversalTest::testExpectedSequence(
85 const Vector<Element*>& list) {
86 for (size_t i = 0; i < list.size(); ++i) {
87 const Element* expected = i == list.size() - 1 ? nullptr : list[i + 1];
88 EXPECT_EQ(expected, SlotScopedTraversal::next(*list[i]));
89 }
90
91 for (size_t i = list.size(); i > 0; --i) {
92 const Element* expected = i == 1 ? nullptr : list[i - 2];
93 EXPECT_EQ(expected, SlotScopedTraversal::previous(*list[i - 1]));
94 }
95 }
96
97 TEST_F(SlotScopedTraversalTest, simpleSlot) {
98 const char* mainHTML =
99 "<div id='host'>"
100 "<div id='inner1'></div>"
101 "<div id='inner2'></div>"
102 "</div>";
103
104 const char* shadowHTML = "<slot></slot>";
105
106 setupSampleHTML(mainHTML, shadowHTML, 0);
107
108 Element* host = document().querySelector("#host");
109 Element* inner1 = document().querySelector("#inner1");
110 Element* inner2 = document().querySelector("#inner2");
111 ShadowRoot* shadowRoot = host->openShadowRoot();
112 Element* slotElement = shadowRoot->querySelector("slot");
113 DCHECK(isHTMLSlotElement(slotElement));
114 HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
115
116 EXPECT_EQ(inner1, SlotScopedTraversal::firstAssignedToSlot(*slot));
117 EXPECT_EQ(inner2, SlotScopedTraversal::lastAssignedToSlot(*slot));
118 EXPECT_FALSE(SlotScopedTraversal::isSlotScoped(*host));
119 EXPECT_FALSE(SlotScopedTraversal::isSlotScoped(*slot));
120 EXPECT_TRUE(SlotScopedTraversal::isSlotScoped(*inner1));
121 EXPECT_TRUE(SlotScopedTraversal::isSlotScoped(*inner2));
122 EXPECT_EQ(inner1, SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(
123 *inner1));
124 EXPECT_EQ(inner2, SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(
125 *inner2));
126 EXPECT_EQ(slot, SlotScopedTraversal::findScopeOwnerSlot(*inner1));
127 EXPECT_EQ(slot, SlotScopedTraversal::findScopeOwnerSlot(*inner2));
128
129 Vector<Element*> expectedSequence({inner1, inner2});
130 testExpectedSequence(expectedSequence);
131 }
132
133 TEST_F(SlotScopedTraversalTest, multipleSlots) {
134 const char* mainHTML =
135 "<div id='host'>"
136 "<div id='inner0' slot='slot0'></div>"
137 "<div id='inner1' slot='slot1'></div>"
138 "<div id='inner2'></div>"
139 "<div id='inner3' slot='slot1'></div>"
140 "<div id='inner4' slot='slot0'></div>"
141 "<div id='inner5'></div>"
142 "</div>";
143
144 const char* shadowHTML =
145 "<slot id='unnamedslot'></slot>"
146 "<slot id='slot0' name='slot0'></slot>"
147 "<slot id='slot1' name='slot1'></slot>";
148
149 setupSampleHTML(mainHTML, shadowHTML, 0);
150
151 Element* host = document().querySelector("#host");
152 Element* inner[6];
153 inner[0] = document().querySelector("#inner0");
154 inner[1] = document().querySelector("#inner1");
155 inner[2] = document().querySelector("#inner2");
156 inner[3] = document().querySelector("#inner3");
157 inner[4] = document().querySelector("#inner4");
158 inner[5] = document().querySelector("#inner5");
159
160 ShadowRoot* shadowRoot = host->openShadowRoot();
161 Element* slotElement[3];
162 slotElement[0] = shadowRoot->querySelector("#slot0");
163 slotElement[1] = shadowRoot->querySelector("#slot1");
164 slotElement[2] = shadowRoot->querySelector("#unnamedslot");
165
166 HTMLSlotElement* slot[3];
167 slot[0] = toHTMLSlotElement(slotElement[0]);
168 slot[1] = toHTMLSlotElement(slotElement[1]);
169 slot[2] = toHTMLSlotElement(slotElement[2]);
170
171 {
172 // <slot id='slot0'> : Expected assigned nodes: inner0, inner4
173 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot[0]));
174 EXPECT_EQ(inner[4], SlotScopedTraversal::lastAssignedToSlot(*slot[0]));
175 Vector<Element*> expectedSequence({inner[0], inner[4]});
176 testExpectedSequence(expectedSequence);
177 }
178
179 {
180 // <slot name='slot1'> : Expected assigned nodes: inner1, inner3
181 EXPECT_EQ(inner[1], SlotScopedTraversal::firstAssignedToSlot(*slot[1]));
182 EXPECT_EQ(inner[3], SlotScopedTraversal::lastAssignedToSlot(*slot[1]));
183 Vector<Element*> expectedSequence({inner[1], inner[3]});
184 testExpectedSequence(expectedSequence);
185 }
186
187 {
188 // <slot id='unnamedslot'> : Expected assigned nodes: inner2, inner5
189 EXPECT_EQ(inner[2], SlotScopedTraversal::firstAssignedToSlot(*slot[2]));
190 EXPECT_EQ(inner[5], SlotScopedTraversal::lastAssignedToSlot(*slot[2]));
191 Vector<Element*> expectedSequence({inner[2], inner[5]});
192 testExpectedSequence(expectedSequence);
193 }
194 }
195
196 TEST_F(SlotScopedTraversalTest, shadowHostAtTopLevel) {
197 // This covers a shadow host is directly assigned to a slot.
198 //
199 // We build the following tree:
200 // host
201 // |
202 // ShadowRoot
203 // |
204 // ____<slot>____
205 // | | |
206 // inner0 inner1 inner2
207 // | | |
208 // child0 child1 child2
209 //
210 // And iterate on inner0, inner1, and inner2 to attach shadow on each of
211 // them, and check if elements that are slotted to another slot are skipped
212 // in traversal.
213
214 const char* mainHTML =
215 "<div id='host'>"
216 "<div id='inner0'><div id='child0'></div></div>"
217 "<div id='inner1'><div id='child1'></div></div>"
218 "<div id='inner2'><div id='child2'></div></div>"
219 "</div>";
220
221 const char* shadowHTML = "<slot></slot>";
222
223 for (int i = 0; i < 3; ++i) {
224 setupSampleHTML(mainHTML, shadowHTML, 0);
225
226 Element* host = document().querySelector("#host");
227 Element* inner[3];
228 inner[0] = document().querySelector("#inner0");
229 inner[1] = document().querySelector("#inner1");
230 inner[2] = document().querySelector("#inner2");
231 Element* child[3];
232 child[0] = document().querySelector("#child0");
233 child[1] = document().querySelector("#child1");
234 child[2] = document().querySelector("#child2");
235
236 attachOpenShadowRoot(*inner[i], shadowHTML);
237
238 ShadowRoot* shadowRoot = host->openShadowRoot();
239 Element* slotElement = shadowRoot->querySelector("slot");
240 DCHECK(isHTMLSlotElement(slotElement));
241 HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
242
243 switch (i) {
244 case 0: {
245 // inner0 is a shadow host.
246 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
247 EXPECT_EQ(child[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
248
249 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[0]));
250 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[0]));
251
252 Vector<Element*> expectedSequence(
253 {inner[0], inner[1], child[1], inner[2], child[2]});
254 testExpectedSequence(expectedSequence);
255 } break;
256
257 case 1: {
258 // inner1 is a shadow host.
259 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
260 EXPECT_EQ(child[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
261
262 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[1]));
263 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[1]));
264
265 Vector<Element*> expectedSequence(
266 {inner[0], child[0], inner[1], inner[2], child[2]});
267 testExpectedSequence(expectedSequence);
268 } break;
269
270 case 2: {
271 // inner2 is a shadow host.
272 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
273 EXPECT_EQ(inner[2], SlotScopedTraversal::lastAssignedToSlot(*slot));
274
275 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*child[2]));
276 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*child[2]));
277
278 Vector<Element*> expectedSequence(
279 {inner[0], child[0], inner[1], child[1], inner[2]});
280 testExpectedSequence(expectedSequence);
281 } break;
282 }
283 }
284 }
285
286 TEST_F(SlotScopedTraversalTest, shadowHostAtSecondLevel) {
287 // This covers cases where a shadow host exists in a child of a slotted
288 // element.
289 //
290 // We build the following tree:
291 // host
292 // |
293 // ShadowRoot
294 // |
295 // ____<slot>____
296 // | |
297 // _inner0_ _inner1_
298 // | | | |
299 // child0 child1 child2 child3
300 // | | | |
301 // span0 span1 span2 span3
302 //
303 // And iterate on child0, child1, child2, and child3 to attach shadow on each
304 // of them, and check if elements that are slotted to another slot are skipped
305 // in traversal.
306
307 const char* mainHTML =
308 "<div id='host'>"
309 "<div id='inner0'>"
310 "<div id='child0'><span id='span0'></span></div>"
311 "<div id='child1'><span id='span1'></span></div>"
312 "</div>"
313 "<div id='inner1'>"
314 "<div id='child2'><span id='span2'></span></div>"
315 "<div id='child3'><span id='span3'></span></div>"
316 "</div>"
317 "</div>";
318
319 const char* shadowHTML = "<slot></slot>";
320
321 for (int i = 0; i < 4; ++i) {
322 setupSampleHTML(mainHTML, shadowHTML, 0);
323
324 Element* host = document().querySelector("#host");
325 Element* inner[2];
326 inner[0] = document().querySelector("#inner0");
327 inner[1] = document().querySelector("#inner1");
328 Element* child[4];
329 child[0] = document().querySelector("#child0");
330 child[1] = document().querySelector("#child1");
331 child[2] = document().querySelector("#child2");
332 child[3] = document().querySelector("#child3");
333 Element* span[4];
334 span[0] = document().querySelector("#span0");
335 span[1] = document().querySelector("#span1");
336 span[2] = document().querySelector("#span2");
337 span[3] = document().querySelector("#span3");
338
339 attachOpenShadowRoot(*child[i], shadowHTML);
340
341 for (int j = 0; j < 4; ++j) {
342 DCHECK(child[i]);
343 DCHECK(span[i]);
344 }
345
346 ShadowRoot* shadowRoot = host->openShadowRoot();
347 Element* slotElement = shadowRoot->querySelector("slot");
348 DCHECK(isHTMLSlotElement(slotElement));
349 HTMLSlotElement* slot = toHTMLSlotElement(slotElement);
350
351 switch (i) {
352 case 0: {
353 // child0 is a shadow host.
354 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
355 EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
356
357 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[0]));
358 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[0]));
359
360 const Vector<Element*> expectedSequence({inner[0], child[0], child[1],
361 span[1], inner[1], child[2],
362 span[2], child[3], span[3]});
363 testExpectedSequence(expectedSequence);
364 } break;
365
366 case 1: {
367 // child1 is a shadow host.
368 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
369 EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
370
371 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[1]));
372 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[1]));
373
374 const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
375 child[1], inner[1], child[2],
376 span[2], child[3], span[3]});
377 testExpectedSequence(expectedSequence);
378 } break;
379
380 case 2: {
381 // child2 is a shadow host.
382 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
383 EXPECT_EQ(span[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
384
385 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[2]));
386 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[2]));
387
388 const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
389 child[1], span[1], inner[1],
390 child[2], child[3], span[3]});
391 testExpectedSequence(expectedSequence);
392 } break;
393
394 case 3: {
395 // child3 is a shadow host.
396 EXPECT_EQ(inner[0], SlotScopedTraversal::firstAssignedToSlot(*slot));
397 EXPECT_EQ(child[3], SlotScopedTraversal::lastAssignedToSlot(*slot));
398
399 EXPECT_EQ(nullptr, SlotScopedTraversal::next(*span[3]));
400 EXPECT_EQ(nullptr, SlotScopedTraversal::previous(*span[3]));
401
402 const Vector<Element*> expectedSequence({inner[0], child[0], span[0],
403 child[1], span[1], inner[1],
404 child[2], span[2], child[3]});
405 testExpectedSequence(expectedSequence);
406 } break;
407 }
408 }
409 }
410
411 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698