blob: ec0505a065c121258cb16ebacdf6a3e1051e8218 [file] [log] [blame]
// Copyright 2017 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 "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/html_slot_element.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace {
// Copy elements in HTMLCollection into a vector because HTMLCollection is a
// live list and would be invalid if a mutation occurs.
HeapVector<Member<Element>> CollectFromHTMLCollection(
HTMLCollection& collection) {
HeapVector<Member<Element>> elements;
for (Element* element : collection)
elements.push_back(element);
return elements;
}
template <class T>
HeapVector<Member<Node>> CollectFromIterable(T iterable) {
HeapVector<Member<Node>> nodes;
for (auto& node : iterable)
nodes.push_back(&node);
return nodes;
}
// Process Mini-DSL for Declarative Shadow DOM.
// In Mini-DSL, <shadowroot> elements would behaves as if it were "Declarative
// Shadow DOM". This is usuful for constructing a composed tree in testing.
// Declarative Shadow DOM is under the dicussion. See
// https://github.com/whatwg/dom/issues/510. Once Declarative Shadow DOM is
// standardized and implemented, this DSL is no longer required.
void ConvertDeclarativeShadowDOMToShadowRoot(Element& element) {
HTMLCollection* shadow_root_collection =
element.getElementsByTagName("shadowroot");
if (!shadow_root_collection)
return;
for (Element* shadow_root :
CollectFromHTMLCollection(*shadow_root_collection)) {
Element* parent = shadow_root->parentElement();
DCHECK(parent);
parent->removeChild(shadow_root);
ShadowRoot& attached_shadow_root =
parent->AttachShadowRootInternal(ShadowRootType::kOpen);
for (Node* child :
CollectFromIterable(NodeTraversal::ChildrenOf(*shadow_root)))
attached_shadow_root.AppendChild(child);
}
}
void RemoveWhiteSpaceOnlyTextNode(ContainerNode& container) {
for (Node* descendant :
CollectFromIterable(NodeTraversal::InclusiveDescendantsOf(container))) {
if (auto* text = DynamicTo<Text>(descendant)) {
if (text->ContainsOnlyWhitespaceOrEmpty())
text->remove();
} else if (auto* element = DynamicTo<Element>(descendant)) {
if (ShadowRoot* shadow_root = element->OpenShadowRoot())
RemoveWhiteSpaceOnlyTextNode(*shadow_root);
}
}
}
} // namespace
class SlotAssignmentTest : public testing::Test {
public:
SlotAssignmentTest() {}
protected:
Document& GetDocument() const { return *document_; }
void SetBody(const char* html);
private:
void SetUp() override;
Persistent<Document> document_;
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
};
void SlotAssignmentTest::SetUp() {
dummy_page_holder_ = std::make_unique<DummyPageHolder>(IntSize(800, 600));
document_ = &dummy_page_holder_->GetDocument();
DCHECK(document_);
}
void SlotAssignmentTest::SetBody(const char* html) {
Element* body = GetDocument().body();
body->setInnerHTML(String::FromUTF8(html));
ConvertDeclarativeShadowDOMToShadowRoot(*body);
RemoveWhiteSpaceOnlyTextNode(*body);
}
TEST_F(SlotAssignmentTest, DeclarativeShadowDOM) {
SetBody(R"HTML(
<div id=host>
<shadowroot></shadowroot>
</div>
)HTML");
Element* host = GetDocument().QuerySelector("#host");
ASSERT_NE(nullptr, host);
ASSERT_NE(nullptr, host->OpenShadowRoot());
}
TEST_F(SlotAssignmentTest, NestedDeclarativeShadowDOM) {
SetBody(R"HTML(
<div id=host1>
<shadowroot>
<div id=host2>
<shadowroot></shadowroot>
</div>
</shadowroot>
</div>
)HTML");
Element* host1 = GetDocument().QuerySelector("#host1");
ASSERT_NE(nullptr, host1);
ShadowRoot* shadow_root1 = host1->OpenShadowRoot();
ASSERT_NE(nullptr, shadow_root1);
Element* host2 = shadow_root1->QuerySelector("#host2");
ASSERT_NE(nullptr, host2);
ShadowRoot* shadow_root2 = host2->OpenShadowRoot();
ASSERT_NE(nullptr, shadow_root2);
}
TEST_F(SlotAssignmentTest, AssignedNodesAreSet) {
SetBody(R"HTML(
<div id=host>
<shadowroot>
<slot></slot>
</shadowroot>
<div id='host-child'></div>
</div>
)HTML");
Element* host = GetDocument().QuerySelector("#host");
Element* host_child = GetDocument().QuerySelector("#host-child");
ShadowRoot* shadow_root = host->OpenShadowRoot();
auto* slot = DynamicTo<HTMLSlotElement>(shadow_root->QuerySelector("slot"));
ASSERT_NE(nullptr, slot);
EXPECT_EQ(slot, host_child->AssignedSlot());
HeapVector<Member<Node>> expected_nodes;
expected_nodes.push_back(host_child);
EXPECT_EQ(expected_nodes, slot->AssignedNodes());
}
} // namespace blink