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 "third_party/blink/renderer/core/html/custom/custom_element_upgrade_sorter.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/renderer/bindings/core/v8/string_or_element_creation_options.h"
9 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
10 #include "third_party/blink/renderer/bindings/core/v8/v8_shadow_root_init.h"
11 #include "third_party/blink/renderer/core/dom/document.h"
12 #include "third_party/blink/renderer/core/dom/element.h"
13 #include "third_party/blink/renderer/core/dom/shadow_root.h"
14 #include "third_party/blink/renderer/core/html/html_document.h"
15 #include "third_party/blink/renderer/core/html_names.h"
16 #include "third_party/blink/renderer/core/testing/page_test_base.h"
17 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
18 #include "third_party/blink/renderer/platform/heap/handle.h"
19 #include "third_party/blink/renderer/platform/heap/heap.h"
20 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
21 
22 namespace blink {
23 
24 class CustomElementUpgradeSorterTest : public PageTestBase {
25  protected:
SetUp()26   void SetUp() override { PageTestBase::SetUp(IntSize(1, 1)); }
27 
CreateElementWithId(const char * local_name,const char * id)28   Element* CreateElementWithId(const char* local_name, const char* id) {
29     NonThrowableExceptionState no_exceptions;
30     Element* element = GetDocument().CreateElementForBinding(
31         local_name, StringOrElementCreationOptions(), no_exceptions);
32     element->setAttribute(html_names::kIdAttr, id);
33     return element;
34   }
35 
GetScriptState()36   ScriptState* GetScriptState() {
37     return ToScriptStateForMainWorld(&GetFrame());
38   }
39 };
40 
TEST_F(CustomElementUpgradeSorterTest,inOtherDocument_notInSet)41 TEST_F(CustomElementUpgradeSorterTest, inOtherDocument_notInSet) {
42   NonThrowableExceptionState no_exceptions;
43   Element* element = GetDocument().CreateElementForBinding(
44       "a-a", StringOrElementCreationOptions(), no_exceptions);
45 
46   auto* other_document = MakeGarbageCollected<HTMLDocument>();
47   other_document->AppendChild(element);
48   EXPECT_EQ(other_document, element->ownerDocument())
49       << "sanity: another document should have adopted an element on append";
50 
51   CustomElementUpgradeSorter sorter;
52   sorter.Add(element);
53 
54   HeapVector<Member<Element>> elements;
55   sorter.Sorted(&elements, &GetDocument());
56   EXPECT_EQ(0u, elements.size())
57       << "the adopted-away candidate should not have been included";
58 }
59 
TEST_F(CustomElementUpgradeSorterTest,oneCandidate)60 TEST_F(CustomElementUpgradeSorterTest, oneCandidate) {
61   NonThrowableExceptionState no_exceptions;
62   Element* element = GetDocument().CreateElementForBinding(
63       "a-a", StringOrElementCreationOptions(), no_exceptions);
64   GetDocument().documentElement()->AppendChild(element);
65 
66   CustomElementUpgradeSorter sorter;
67   sorter.Add(element);
68 
69   HeapVector<Member<Element>> elements;
70   sorter.Sorted(&elements, &GetDocument());
71   EXPECT_EQ(1u, elements.size())
72       << "exactly one candidate should be in the result set";
73   EXPECT_TRUE(elements.Contains(element))
74       << "the candidate should be the element that was added";
75 }
76 
TEST_F(CustomElementUpgradeSorterTest,candidatesInDocumentOrder)77 TEST_F(CustomElementUpgradeSorterTest, candidatesInDocumentOrder) {
78   Element* a = CreateElementWithId("a-a", "a");
79   Element* b = CreateElementWithId("a-a", "b");
80   Element* c = CreateElementWithId("a-a", "c");
81 
82   GetDocument().documentElement()->AppendChild(a);
83   a->AppendChild(b);
84   GetDocument().documentElement()->AppendChild(c);
85 
86   CustomElementUpgradeSorter sorter;
87   sorter.Add(b);
88   sorter.Add(a);
89   sorter.Add(c);
90 
91   HeapVector<Member<Element>> elements;
92   sorter.Sorted(&elements, &GetDocument());
93   EXPECT_EQ(3u, elements.size());
94   EXPECT_EQ(a, elements[0].Get());
95   EXPECT_EQ(b, elements[1].Get());
96   EXPECT_EQ(c, elements[2].Get());
97 }
98 
TEST_F(CustomElementUpgradeSorterTest,sorter_ancestorInSet)99 TEST_F(CustomElementUpgradeSorterTest, sorter_ancestorInSet) {
100   // A*
101   // + B
102   //   + C*
103   Element* a = CreateElementWithId("a-a", "a");
104   Element* b = CreateElementWithId("a-a", "b");
105   Element* c = CreateElementWithId("a-a", "c");
106 
107   GetDocument().documentElement()->AppendChild(a);
108   a->AppendChild(b);
109   b->AppendChild(c);
110 
111   CustomElementUpgradeSorter sort;
112   sort.Add(c);
113   sort.Add(a);
114 
115   HeapVector<Member<Element>> elements;
116   sort.Sorted(&elements, &GetDocument());
117   EXPECT_EQ(2u, elements.size());
118   EXPECT_EQ(a, elements[0].Get());
119   EXPECT_EQ(c, elements[1].Get());
120 }
121 
TEST_F(CustomElementUpgradeSorterTest,sorter_deepShallow)122 TEST_F(CustomElementUpgradeSorterTest, sorter_deepShallow) {
123   // A
124   // + B*
125   // C*
126   Element* a = CreateElementWithId("a-a", "a");
127   Element* b = CreateElementWithId("a-a", "b");
128   Element* c = CreateElementWithId("a-a", "c");
129 
130   GetDocument().documentElement()->AppendChild(a);
131   a->AppendChild(b);
132   GetDocument().documentElement()->AppendChild(c);
133 
134   CustomElementUpgradeSorter sort;
135   sort.Add(b);
136   sort.Add(c);
137 
138   HeapVector<Member<Element>> elements;
139   sort.Sorted(&elements, &GetDocument());
140   EXPECT_EQ(2u, elements.size());
141   EXPECT_EQ(b, elements[0].Get());
142   EXPECT_EQ(c, elements[1].Get());
143 }
144 
TEST_F(CustomElementUpgradeSorterTest,sorter_shallowDeep)145 TEST_F(CustomElementUpgradeSorterTest, sorter_shallowDeep) {
146   // A*
147   // B
148   // + C*
149   Element* a = CreateElementWithId("a-a", "a");
150   Element* b = CreateElementWithId("a-a", "b");
151   Element* c = CreateElementWithId("a-a", "c");
152 
153   GetDocument().documentElement()->AppendChild(a);
154   GetDocument().documentElement()->AppendChild(b);
155   b->AppendChild(c);
156 
157   CustomElementUpgradeSorter sort;
158   sort.Add(a);
159   sort.Add(c);
160 
161   HeapVector<Member<Element>> elements;
162   sort.Sorted(&elements, &GetDocument());
163   EXPECT_EQ(2u, elements.size());
164   EXPECT_EQ(a, elements[0].Get());
165   EXPECT_EQ(c, elements[1].Get());
166 }
167 
TEST_F(CustomElementUpgradeSorterTest,sorter_shadow)168 TEST_F(CustomElementUpgradeSorterTest, sorter_shadow) {
169   // A*
170   // + {ShadowRoot}
171   // | + B
172   // |   + C*
173   // + D*
174   Element* a = CreateElementWithId("a-a", "a");
175   Element* b = CreateElementWithId("a-a", "b");
176   Element* c = CreateElementWithId("a-a", "c");
177   Element* d = CreateElementWithId("a-a", "d");
178 
179   GetDocument().documentElement()->AppendChild(a);
180   ShadowRoot* s = &a->AttachShadowRootInternal(ShadowRootType::kOpen);
181   a->AppendChild(d);
182 
183   s->AppendChild(b);
184   b->AppendChild(c);
185 
186   CustomElementUpgradeSorter sort;
187   sort.Add(a);
188   sort.Add(c);
189   sort.Add(d);
190 
191   HeapVector<Member<Element>> elements;
192   sort.Sorted(&elements, &GetDocument());
193   EXPECT_EQ(3u, elements.size());
194   EXPECT_EQ(a, elements[0].Get());
195   EXPECT_EQ(c, elements[1].Get());
196   EXPECT_EQ(d, elements[2].Get());
197 }
198 
199 }  // namespace blink
200