| // Copyright 2015 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/FlatTreeTraversal.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/frame/FrameView.h" |
| #include "core/html/HTMLElement.h" |
| #include "core/testing/DummyPageHolder.h" |
| #include "platform/geometry/IntSize.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "wtf/Compiler.h" |
| #include "wtf/StdLibExtras.h" |
| #include "wtf/Vector.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| class FlatTreeTraversalTest : public ::testing::Test { |
| protected: |
| Document& document() const; |
| |
| // Sets |mainHTML| to BODY element with |innerHTML| property and attaches |
| // shadow root to child with |shadowHTML|, then update distribution for |
| // calling member functions in |FlatTreeTraversal|. |
| void setupSampleHTML(const char* mainHTML, const char* shadowHTML, unsigned); |
| |
| void setupDocumentTree(const char* mainHTML); |
| |
| void attachV0ShadowRoot(Element& shadowHost, const char* shadowInnerHTML); |
| void attachOpenShadowRoot(Element& shadowHost, const char* shadowInnerHTML); |
| |
| private: |
| void SetUp() override; |
| |
| Persistent<Document> m_document; |
| std::unique_ptr<DummyPageHolder> m_dummyPageHolder; |
| }; |
| |
| void FlatTreeTraversalTest::SetUp() |
| { |
| m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600)); |
| m_document = &m_dummyPageHolder->document(); |
| DCHECK(m_document); |
| } |
| |
| Document& FlatTreeTraversalTest::document() const |
| { |
| return *m_document; |
| } |
| |
| void FlatTreeTraversalTest::setupSampleHTML(const char* mainHTML, const char* shadowHTML, unsigned index) |
| { |
| Element* body = document().body(); |
| body->setInnerHTML(String::fromUTF8(mainHTML), ASSERT_NO_EXCEPTION); |
| Element* shadowHost = toElement(NodeTraversal::childAt(*body, index)); |
| ShadowRoot* shadowRoot = shadowHost->createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION); |
| shadowRoot->setInnerHTML(String::fromUTF8(shadowHTML), ASSERT_NO_EXCEPTION); |
| body->updateDistribution(); |
| } |
| |
| void FlatTreeTraversalTest::setupDocumentTree(const char* mainHTML) |
| { |
| Element* body = document().body(); |
| body->setInnerHTML(String::fromUTF8(mainHTML), ASSERT_NO_EXCEPTION); |
| } |
| |
| void FlatTreeTraversalTest::attachV0ShadowRoot(Element& shadowHost, const char* shadowInnerHTML) |
| { |
| ShadowRoot* shadowRoot = shadowHost.createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION); |
| shadowRoot->setInnerHTML(String::fromUTF8(shadowInnerHTML), ASSERT_NO_EXCEPTION); |
| document().body()->updateDistribution(); |
| } |
| |
| void FlatTreeTraversalTest::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(); |
| } |
| |
| void testCommonAncestor(Node* expectedResult, const Node& nodeA, const Node& nodeB) |
| { |
| Node* result1 = FlatTreeTraversal::commonAncestor(nodeA, nodeB); |
| EXPECT_EQ(expectedResult, result1) << "commonAncestor(" << nodeA.textContent() << "," << nodeB.textContent() << ")"; |
| Node* result2 = FlatTreeTraversal::commonAncestor(nodeB, nodeA); |
| EXPECT_EQ(expectedResult, result2) << "commonAncestor(" << nodeB.textContent() << "," << nodeA.textContent() << ")"; |
| } |
| |
| // Test case for |
| // - childAt |
| // - countChildren |
| // - hasChildren |
| // - index |
| // - isDescendantOf |
| TEST_F(FlatTreeTraversalTest, childAt) |
| { |
| const char* mainHTML = |
| "<div id='m0'>" |
| "<span id='m00'>m00</span>" |
| "<span id='m01'>m01</span>" |
| "</div>"; |
| const char* shadowHTML = |
| "<a id='s00'>s00</a>" |
| "<content select='#m01'></content>" |
| "<a id='s02'>s02</a>" |
| "<a id='s03'><content select='#m00'></content></a>" |
| "<a id='s04'>s04</a>"; |
| setupSampleHTML(mainHTML, shadowHTML, 0); |
| |
| Element* body = document().body(); |
| Element* m0 = body->querySelector("#m0"); |
| Element* m00 = m0->querySelector("#m00"); |
| Element* m01 = m0->querySelector("#m01"); |
| |
| Element* shadowHost = m0; |
| ShadowRoot* shadowRoot = shadowHost->openShadowRoot(); |
| Element* s00 = shadowRoot->querySelector("#s00"); |
| Element* s02 = shadowRoot->querySelector("#s02"); |
| Element* s03 = shadowRoot->querySelector("#s03"); |
| Element* s04 = shadowRoot->querySelector("#s04"); |
| |
| const unsigned numberOfChildNodes = 5; |
| Node* expectedChildNodes[5] = { s00, m01, s02, s03, s04 }; |
| |
| ASSERT_EQ(numberOfChildNodes, FlatTreeTraversal::countChildren(*shadowHost)); |
| EXPECT_TRUE(FlatTreeTraversal::hasChildren(*shadowHost)); |
| |
| for (unsigned index = 0; index < numberOfChildNodes; ++index) { |
| Node* child = FlatTreeTraversal::childAt(*shadowHost, index); |
| EXPECT_EQ(expectedChildNodes[index], child) |
| << "FlatTreeTraversal::childAt(*shadowHost, " << index << ")"; |
| EXPECT_EQ(index, FlatTreeTraversal::index(*child)) |
| << "FlatTreeTraversal::index(FlatTreeTraversal(*shadowHost, " << index << "))"; |
| EXPECT_TRUE(FlatTreeTraversal::isDescendantOf(*child, *shadowHost)) |
| << "FlatTreeTraversal::isDescendantOf(*FlatTreeTraversal(*shadowHost, " << index << "), *shadowHost)"; |
| } |
| EXPECT_EQ(nullptr, FlatTreeTraversal::childAt(*shadowHost, numberOfChildNodes + 1)) |
| << "Out of bounds childAt() returns nullptr."; |
| |
| // Distribute node |m00| is child of node in shadow tree |s03|. |
| EXPECT_EQ(m00, FlatTreeTraversal::childAt(*s03, 0)); |
| } |
| |
| // Test case for |
| // - commonAncestor |
| // - isDescendantOf |
| TEST_F(FlatTreeTraversalTest, commonAncestor) |
| { |
| // We build following flat tree: |
| // ____BODY___ |
| // | | | |
| // m0 m1 m2 m1 is shadow host having m10, m11, m12. |
| // _|_ | __|__ |
| // | | | | | |
| // m00 m01 | m20 m21 |
| // _____|_____________ |
| // | | | | | |
| // s10 s11 s12 s13 s14 |
| // | |
| // __|__ |
| // | | | |
| // m12 m10 m11 <-- distributed |
| // where: each symbol consists with prefix, child index, child-child index. |
| // prefix "m" means node in main tree, |
| // prefix "d" means node in main tree and distributed |
| // prefix "s" means node in shadow tree |
| const char* mainHTML = |
| "<a id='m0'><b id='m00'>m00</b><b id='m01'>m01</b></a>" |
| "<a id='m1'>" |
| "<b id='m10'>m10</b>" |
| "<b id='m11'>m11</b>" |
| "<b id='m12'>m12</b>" |
| "</a>" |
| "<a id='m2'><b id='m20'>m20</b><b id='m21'>m21</b></a>"; |
| const char* shadowHTML = |
| "<a id='s10'>s10</a>" |
| "<a id='s11'><content select='#m12'></content></a>" |
| "<a id='s12'>s12</a>" |
| "<a id='s13'>" |
| "<content select='#m10'></content>" |
| "<content select='#m11'></content>" |
| "</a>" |
| "<a id='s14'>s14</a>"; |
| setupSampleHTML(mainHTML, shadowHTML, 1); |
| Element* body = document().body(); |
| Element* m0 = body->querySelector("#m0"); |
| Element* m1 = body->querySelector("#m1"); |
| Element* m2 = body->querySelector("#m2"); |
| |
| Element* m00 = body->querySelector("#m00"); |
| Element* m01 = body->querySelector("#m01"); |
| Element* m10 = body->querySelector("#m10"); |
| Element* m11 = body->querySelector("#m11"); |
| Element* m12 = body->querySelector("#m12"); |
| Element* m20 = body->querySelector("#m20"); |
| Element* m21 = body->querySelector("#m21"); |
| |
| ShadowRoot* shadowRoot = m1->openShadowRoot(); |
| Element* s10 = shadowRoot->querySelector("#s10"); |
| Element* s11 = shadowRoot->querySelector("#s11"); |
| Element* s12 = shadowRoot->querySelector("#s12"); |
| Element* s13 = shadowRoot->querySelector("#s13"); |
| Element* s14 = shadowRoot->querySelector("#s14"); |
| |
| testCommonAncestor(body, *m0, *m1); |
| testCommonAncestor(body, *m1, *m2); |
| testCommonAncestor(body, *m1, *m20); |
| testCommonAncestor(body, *s14, *m21); |
| |
| testCommonAncestor(m0, *m0, *m0); |
| testCommonAncestor(m0, *m00, *m01); |
| |
| testCommonAncestor(m1, *m1, *m1); |
| testCommonAncestor(m1, *s10, *s14); |
| testCommonAncestor(m1, *s10, *m12); |
| testCommonAncestor(m1, *s12, *m12); |
| testCommonAncestor(m1, *m10, *m12); |
| |
| testCommonAncestor(m01, *m01, *m01); |
| testCommonAncestor(s11, *s11, *m12); |
| testCommonAncestor(s13, *m10, *m11); |
| |
| s12->remove(ASSERT_NO_EXCEPTION); |
| testCommonAncestor(s12, *s12, *s12); |
| testCommonAncestor(nullptr, *s12, *s11); |
| testCommonAncestor(nullptr, *s12, *m01); |
| testCommonAncestor(nullptr, *s12, *m20); |
| |
| m20->remove(ASSERT_NO_EXCEPTION); |
| testCommonAncestor(m20, *m20, *m20); |
| testCommonAncestor(nullptr, *m20, *s12); |
| testCommonAncestor(nullptr, *m20, *m1); |
| } |
| |
| // Test case for |
| // - nextSkippingChildren |
| // - previousSkippingChildren |
| TEST_F(FlatTreeTraversalTest, nextSkippingChildren) |
| { |
| const char* mainHTML = |
| "<div id='m0'>m0</div>" |
| "<div id='m1'>" |
| "<span id='m10'>m10</span>" |
| "<span id='m11'>m11</span>" |
| "</div>" |
| "<div id='m2'>m2</div>"; |
| const char* shadowHTML = |
| "<content select='#m11'></content>" |
| "<a id='s11'>s11</a>" |
| "<a id='s12'>" |
| "<b id='s120'>s120</b>" |
| "<content select='#m10'></content>" |
| "</a>"; |
| setupSampleHTML(mainHTML, shadowHTML, 1); |
| |
| Element* body = document().body(); |
| Element* m0 = body->querySelector("#m0"); |
| Element* m1 = body->querySelector("#m1"); |
| Element* m2 = body->querySelector("#m2"); |
| |
| Element* m10 = body->querySelector("#m10"); |
| Element* m11 = body->querySelector("#m11"); |
| |
| ShadowRoot* shadowRoot = m1->openShadowRoot(); |
| Element* s11 = shadowRoot->querySelector("#s11"); |
| Element* s12 = shadowRoot->querySelector("#s12"); |
| Element* s120 = shadowRoot->querySelector("#s120"); |
| |
| // Main tree node to main tree node |
| EXPECT_EQ(*m1, FlatTreeTraversal::nextSkippingChildren(*m0)); |
| EXPECT_EQ(*m0, FlatTreeTraversal::previousSkippingChildren(*m1)); |
| |
| // Distribute node to main tree node |
| EXPECT_EQ(*m2, FlatTreeTraversal::nextSkippingChildren(*m10)); |
| EXPECT_EQ(*m1, FlatTreeTraversal::previousSkippingChildren(*m2)); |
| |
| // Distribute node to node in shadow tree |
| EXPECT_EQ(*s11, FlatTreeTraversal::nextSkippingChildren(*m11)); |
| EXPECT_EQ(*m11, FlatTreeTraversal::previousSkippingChildren(*s11)); |
| |
| // Node in shadow tree to distributed node |
| EXPECT_EQ(*s11, FlatTreeTraversal::nextSkippingChildren(*m11)); |
| EXPECT_EQ(*m11, FlatTreeTraversal::previousSkippingChildren(*s11)); |
| |
| EXPECT_EQ(*m10, FlatTreeTraversal::nextSkippingChildren(*s120)); |
| EXPECT_EQ(*s120, FlatTreeTraversal::previousSkippingChildren(*m10)); |
| |
| // Node in shadow tree to main tree |
| EXPECT_EQ(*m2, FlatTreeTraversal::nextSkippingChildren(*s12)); |
| EXPECT_EQ(*m1, FlatTreeTraversal::previousSkippingChildren(*m2)); |
| } |
| |
| // Test case for |
| // - lastWithin |
| // - lastWithinOrSelf |
| TEST_F(FlatTreeTraversalTest, lastWithin) |
| { |
| const char* mainHTML = |
| "<div id='m0'>m0</div>" |
| "<div id='m1'>" |
| "<span id='m10'>m10</span>" |
| "<span id='m11'>m11</span>" |
| "<span id='m12'>m12</span>" // #m12 is not distributed. |
| "</div>" |
| "<div id='m2'></div>"; |
| const char* shadowHTML = |
| "<content select='#m11'></content>" |
| "<a id='s11'>s11</a>" |
| "<a id='s12'>" |
| "<content select='#m10'></content>" |
| "</a>"; |
| setupSampleHTML(mainHTML, shadowHTML, 1); |
| |
| Element* body = document().body(); |
| Element* m0 = body->querySelector("#m0"); |
| Element* m1 = body->querySelector("#m1"); |
| Element* m2 = body->querySelector("#m2"); |
| |
| Element* m10 = body->querySelector("#m10"); |
| |
| ShadowRoot* shadowRoot = m1->openShadowRoot(); |
| Element* s11 = shadowRoot->querySelector("#s11"); |
| Element* s12 = shadowRoot->querySelector("#s12"); |
| |
| EXPECT_EQ(m0->firstChild(), FlatTreeTraversal::lastWithin(*m0)); |
| EXPECT_EQ(*m0->firstChild(), FlatTreeTraversal::lastWithinOrSelf(*m0)); |
| |
| EXPECT_EQ(m10->firstChild(), FlatTreeTraversal::lastWithin(*m1)); |
| EXPECT_EQ(*m10->firstChild(), FlatTreeTraversal::lastWithinOrSelf(*m1)); |
| |
| EXPECT_EQ(nullptr, FlatTreeTraversal::lastWithin(*m2)); |
| EXPECT_EQ(*m2, FlatTreeTraversal::lastWithinOrSelf(*m2)); |
| |
| EXPECT_EQ(s11->firstChild(), FlatTreeTraversal::lastWithin(*s11)); |
| EXPECT_EQ(*s11->firstChild(), FlatTreeTraversal::lastWithinOrSelf(*s11)); |
| |
| EXPECT_EQ(m10->firstChild(), FlatTreeTraversal::lastWithin(*s12)); |
| EXPECT_EQ(*m10->firstChild(), FlatTreeTraversal::lastWithinOrSelf(*s12)); |
| } |
| |
| TEST_F(FlatTreeTraversalTest, previousPostOrder) |
| { |
| const char* mainHTML = |
| "<div id='m0'>m0</div>" |
| "<div id='m1'>" |
| "<span id='m10'>m10</span>" |
| "<span id='m11'>m11</span>" |
| "</div>" |
| "<div id='m2'>m2</div>"; |
| const char* shadowHTML = |
| "<content select='#m11'></content>" |
| "<a id='s11'>s11</a>" |
| "<a id='s12'>" |
| "<b id='s120'>s120</b>" |
| "<content select='#m10'></content>" |
| "</a>"; |
| setupSampleHTML(mainHTML, shadowHTML, 1); |
| |
| Element* body = document().body(); |
| Element* m0 = body->querySelector("#m0"); |
| Element* m1 = body->querySelector("#m1"); |
| Element* m2 = body->querySelector("#m2"); |
| |
| Element* m10 = body->querySelector("#m10"); |
| Element* m11 = body->querySelector("#m11"); |
| |
| ShadowRoot* shadowRoot = m1->openShadowRoot(); |
| Element* s11 = shadowRoot->querySelector("#s11"); |
| Element* s12 = shadowRoot->querySelector("#s12"); |
| Element* s120 = shadowRoot->querySelector("#s120"); |
| |
| EXPECT_EQ(*m0->firstChild(), FlatTreeTraversal::previousPostOrder(*m0)); |
| EXPECT_EQ(*s12, FlatTreeTraversal::previousPostOrder(*m1)); |
| EXPECT_EQ(*m10->firstChild(), FlatTreeTraversal::previousPostOrder(*m10)); |
| EXPECT_EQ(*s120, FlatTreeTraversal::previousPostOrder(*m10->firstChild())); |
| EXPECT_EQ(*s120, FlatTreeTraversal::previousPostOrder(*m10->firstChild(), s12)); |
| EXPECT_EQ(*m11->firstChild(), FlatTreeTraversal::previousPostOrder(*m11)); |
| EXPECT_EQ(*m0, FlatTreeTraversal::previousPostOrder(*m11->firstChild())); |
| EXPECT_EQ(nullptr, FlatTreeTraversal::previousPostOrder(*m11->firstChild(), m11)); |
| EXPECT_EQ(*m2->firstChild(), FlatTreeTraversal::previousPostOrder(*m2)); |
| |
| EXPECT_EQ(*s11->firstChild(), FlatTreeTraversal::previousPostOrder(*s11)); |
| EXPECT_EQ(*m10, FlatTreeTraversal::previousPostOrder(*s12)); |
| EXPECT_EQ(*s120->firstChild(), FlatTreeTraversal::previousPostOrder(*s120)); |
| EXPECT_EQ(*s11, FlatTreeTraversal::previousPostOrder(*s120->firstChild())); |
| EXPECT_EQ(nullptr, FlatTreeTraversal::previousPostOrder(*s120->firstChild(), s12)); |
| } |
| |
| TEST_F(FlatTreeTraversalTest, nextSiblingNotInDocumentFlatTree) |
| { |
| const char* mainHTML = |
| "<div id='m0'>m0</div>" |
| "<div id='m1'>" |
| "<span id='m10'>m10</span>" |
| "<span id='m11'>m11</span>" |
| "</div>" |
| "<div id='m2'>m2</div>"; |
| const char* shadowHTML = |
| "<content select='#m11'></content>"; |
| setupSampleHTML(mainHTML, shadowHTML, 1); |
| |
| Element* body = document().body(); |
| Element* m10 = body->querySelector("#m10"); |
| |
| EXPECT_EQ(nullptr, FlatTreeTraversal::nextSibling(*m10)); |
| EXPECT_EQ(nullptr, FlatTreeTraversal::previousSibling(*m10)); |
| } |
| |
| TEST_F(FlatTreeTraversalTest, redistribution) |
| { |
| const char* mainHTML = |
| "<div id='m0'>m0</div>" |
| "<div id='m1'>" |
| "<span id='m10'>m10</span>" |
| "<span id='m11'>m11</span>" |
| "</div>" |
| "<div id='m2'>m2</div>"; |
| const char* shadowHTML1 = |
| "<div id='s1'>" |
| "<content></content>" |
| "</div>"; |
| |
| setupSampleHTML(mainHTML, shadowHTML1, 1); |
| |
| const char* shadowHTML2 = |
| "<div id='s2'>" |
| "<content select='#m10'></content>" |
| "<span id='s21'>s21</span>" |
| "</div>"; |
| |
| Element* body = document().body(); |
| Element* m1 = body->querySelector("#m1"); |
| Element* m10 = body->querySelector("#m10"); |
| |
| ShadowRoot* shadowRoot1 = m1->openShadowRoot(); |
| Element* s1 = shadowRoot1->querySelector("#s1"); |
| |
| attachV0ShadowRoot(*s1, shadowHTML2); |
| |
| ShadowRoot* shadowRoot2 = s1->openShadowRoot(); |
| Element* s21 = shadowRoot2->querySelector("#s21"); |
| |
| EXPECT_EQ(s21, FlatTreeTraversal::nextSibling(*m10)); |
| EXPECT_EQ(m10, FlatTreeTraversal::previousSibling(*s21)); |
| |
| // FlatTreeTraversal::traverseSiblings does not work for a node which is not in a document flat tree. |
| // e.g. The following test fails. The result of FlatTreeTraversal::previousSibling(*m11)) will be #m10, instead of nullptr. |
| // Element* m11 = body->querySelector("#m11"); |
| // EXPECT_EQ(nullptr, FlatTreeTraversal::previousSibling(*m11)); |
| } |
| |
| TEST_F(FlatTreeTraversalTest, v1Simple) |
| { |
| const char* mainHTML = |
| "<div id='host'>" |
| "<div id='child1' slot='slot1'></div>" |
| "<div id='child2' slot='slot2'></div>" |
| "</div>"; |
| const char* shadowHTML = |
| "<div id='shadow-child1'></div>" |
| "<slot name='slot1'></slot>" |
| "<slot name='slot2'></slot>" |
| "<div id='shadow-child2'></div>"; |
| |
| setupDocumentTree(mainHTML); |
| Element* body = document().body(); |
| Element* host = body->querySelector("#host"); |
| Element* child1 = body->querySelector("#child1"); |
| Element* child2 = body->querySelector("#child2"); |
| |
| attachOpenShadowRoot(*host, shadowHTML); |
| ShadowRoot* shadowRoot = host->openShadowRoot(); |
| Element* slot1 = shadowRoot->querySelector("[name=slot1]"); |
| Element* slot2 = shadowRoot->querySelector("[name=slot2]"); |
| Element* shadowChild1 = shadowRoot->querySelector("#shadow-child1"); |
| Element* shadowChild2 = shadowRoot->querySelector("#shadow-child2"); |
| |
| EXPECT_TRUE(slot1); |
| EXPECT_TRUE(slot2); |
| EXPECT_EQ(shadowChild1, FlatTreeTraversal::firstChild(*host)); |
| EXPECT_EQ(child1, FlatTreeTraversal::nextSibling(*shadowChild1)); |
| EXPECT_EQ(child2, FlatTreeTraversal::nextSibling(*child1)); |
| EXPECT_EQ(shadowChild2, FlatTreeTraversal::nextSibling(*child2)); |
| } |
| |
| TEST_F(FlatTreeTraversalTest, v1Redistribution) |
| { |
| const char* mainHTML = |
| "<div id='d1'>" |
| "<div id='d2' slot='d1-s1'></div>" |
| "<div id='d3' slot='d1-s2'></div>" |
| "<div id='d4' slot='nonexistent'></div>" |
| "<div id='d5'></div>" |
| "</div>" |
| "<div id='d6'></div>"; |
| const char* shadowHTML1 = |
| "<div id='d1-1'>" |
| "<div id='d1-2'></div>" |
| "<slot id='d1-s0'></slot>" |
| "<slot name='d1-s1' slot='d1-1-s1'></slot>" |
| "<slot name='d1-s2'></slot>" |
| "<div id='d1-3'></div>" |
| "<div id='d1-4' slot='d1-1-s1'></div>" |
| "</div>"; |
| const char* shadowHTML2 = |
| "<div id='d1-1-1'></div>" |
| "<slot name='d1-1-s1'></slot>" |
| "<slot name='d1-1-s2'></slot>" |
| "<div id='d1-1-2'></div>"; |
| |
| setupDocumentTree(mainHTML); |
| |
| Element* body = document().body(); |
| Element* d1 = body->querySelector("#d1"); |
| Element* d2 = body->querySelector("#d2"); |
| Element* d3 = body->querySelector("#d3"); |
| Element* d4 = body->querySelector("#d4"); |
| Element* d5 = body->querySelector("#d5"); |
| Element* d6 = body->querySelector("#d6"); |
| |
| attachOpenShadowRoot(*d1, shadowHTML1); |
| ShadowRoot* shadowRoot1 = d1->openShadowRoot(); |
| Element* d11 = shadowRoot1->querySelector("#d1-1"); |
| Element* d12 = shadowRoot1->querySelector("#d1-2"); |
| Element* d13 = shadowRoot1->querySelector("#d1-3"); |
| Element* d14 = shadowRoot1->querySelector("#d1-4"); |
| Element* d1s0 = shadowRoot1->querySelector("#d1-s0"); |
| Element* d1s1 = shadowRoot1->querySelector("[name=d1-s1]"); |
| Element* d1s2 = shadowRoot1->querySelector("[name=d1-s2]"); |
| |
| attachOpenShadowRoot(*d11, shadowHTML2); |
| ShadowRoot* shadowRoot2 = d11->openShadowRoot(); |
| Element* d111 = shadowRoot2->querySelector("#d1-1-1"); |
| Element* d112 = shadowRoot2->querySelector("#d1-1-2"); |
| Element* d11s1 = shadowRoot2->querySelector("[name=d1-1-s1]"); |
| Element* d11s2 = shadowRoot2->querySelector("[name=d1-1-s2]"); |
| |
| EXPECT_TRUE(d5); |
| EXPECT_TRUE(d12); |
| EXPECT_TRUE(d13); |
| EXPECT_TRUE(d1s0); |
| EXPECT_TRUE(d1s1); |
| EXPECT_TRUE(d1s2); |
| EXPECT_TRUE(d11s1); |
| EXPECT_TRUE(d11s2); |
| EXPECT_EQ(d11, FlatTreeTraversal::next(*d1)); |
| EXPECT_EQ(d111, FlatTreeTraversal::next(*d11)); |
| EXPECT_EQ(d2, FlatTreeTraversal::next(*d111)); |
| EXPECT_EQ(d14, FlatTreeTraversal::next(*d2)); |
| EXPECT_EQ(d112, FlatTreeTraversal::next(*d14)); |
| EXPECT_EQ(d6, FlatTreeTraversal::next(*d112)); |
| |
| EXPECT_EQ(d112, FlatTreeTraversal::previous(*d6)); |
| |
| EXPECT_EQ(d11, FlatTreeTraversal::parent(*d111)); |
| EXPECT_EQ(d11, FlatTreeTraversal::parent(*d112)); |
| EXPECT_EQ(d11, FlatTreeTraversal::parent(*d2)); |
| EXPECT_EQ(d11, FlatTreeTraversal::parent(*d14)); |
| EXPECT_EQ(nullptr, FlatTreeTraversal::parent(*d3)); |
| EXPECT_EQ(nullptr, FlatTreeTraversal::parent(*d4)); |
| } |
| |
| } // namespace blink |