Index: third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
diff --git a/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp b/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..11b541c21f0016dc65897956b73ef75e67f03fb9 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/dom/custom/CustomElementsRegistryTest.cpp |
@@ -0,0 +1,319 @@ |
+// 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/custom/CustomElementsRegistry.h" |
+ |
+#include "bindings/core/v8/ExceptionState.h" |
+#include "bindings/core/v8/ScriptValue.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/Element.h" |
+#include "core/dom/ElementRegistrationOptions.h" |
+#include "core/dom/custom/CustomElementDefinition.h" |
+#include "core/dom/custom/CustomElementDefinitionBuilder.h" |
+#include "core/dom/custom/CustomElementDescriptor.h" |
+#include "core/dom/custom/CustomElementTestHelpers.h" |
+#include "core/dom/shadow/ShadowRoot.h" |
+#include "core/dom/shadow/ShadowRootInit.h" |
+#include "core/html/HTMLDocument.h" |
+#include "core/testing/DummyPageHolder.h" |
+#include "platform/ScriptForbiddenScope.h" |
+#include "platform/heap/Handle.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "wtf/text/AtomicString.h" |
+#include <memory> |
+ |
+namespace blink { |
+ |
+class CustomElementsRegistryTestBase : public ::testing::Test { |
+protected: |
+ virtual Document& document() = 0; |
+ virtual CustomElementsRegistry& registry() = 0; |
+ |
+ void collectCandidates( |
+ const CustomElementDescriptor& desc, |
+ HeapVector<Member<Element>>* elements) |
+ { |
+ registry().collectCandidates(desc, elements); |
+ } |
+}; |
+ |
+class CustomElementsRegistryTest : public CustomElementsRegistryTestBase { |
+protected: |
+ void SetUp() override |
+ { |
+ CustomElementsRegistryTestBase::SetUp(); |
+ |
+ m_document = HTMLDocument::create(); |
+ m_document->appendChild(CreateElement("html").inDocument(m_document)); |
+ |
+ m_registry = CustomElementsRegistry::create(m_document); |
+ } |
+ |
+ void TearDown() override |
+ { |
+ m_document = nullptr; |
+ m_registry = nullptr; |
+ CustomElementsRegistryTestBase::TearDown(); |
+ } |
+ |
+ Document& document() override { return *m_document; } |
+ CustomElementsRegistry& registry() override { return *m_registry; } |
+ |
+private: |
+ Persistent<Document> m_document; |
+ Persistent<CustomElementsRegistry> m_registry; |
+}; |
+ |
+class CustomElementsRegistryFrameTest : public CustomElementsRegistryTestBase { |
+protected: |
+ void SetUp() override |
+ { |
+ CustomElementsRegistryTestBase::SetUp(); |
+ m_page.reset(DummyPageHolder::create(IntSize(1, 1)).leakPtr()); |
+ } |
+ |
+ void TearDown() override |
+ { |
+ m_page = nullptr; |
+ CustomElementsRegistryTestBase::TearDown(); |
+ } |
+ |
+ Document& document() override { return m_page->document(); } |
+ |
+ CustomElementsRegistry& registry() override |
+ { |
+ return *m_page->frame().localDOMWindow()->customElements(); |
+ } |
+ |
+ ScriptState* scriptState() |
+ { |
+ return ScriptState::forMainWorld(&m_page->frame()); |
+ } |
+ |
+ ShadowRoot* attachShadowTo(Element* element) |
+ { |
+ NonThrowableExceptionState noExceptions; |
+ ShadowRootInit shadowRootInit; |
+ return |
+ element->attachShadow(scriptState(), shadowRootInit, noExceptions); |
+ } |
+ |
+private: |
+ std::unique_ptr<DummyPageHolder> m_page; |
+}; |
+ |
+TEST_F( |
+ CustomElementsRegistryTest, |
+ collectCandidates_shouldNotIncludeElementsRemovedFromDocument) |
+{ |
+ Element* element = CreateElement("a-a").inDocument(&document()); |
+ registry().addCandidate(element); |
+ |
+ HeapVector<Member<Element>> elements; |
+ collectCandidates( |
+ CustomElementDescriptor("a-a", "a-a"), |
+ &elements); |
+ |
+ EXPECT_TRUE(elements.isEmpty()) |
+ << "no candidates should have been found, but we have " |
+ << elements.size(); |
+ EXPECT_FALSE(elements.contains(element)) |
+ << "the out-of-document candidate should not have been found"; |
+} |
+ |
+TEST_F( |
+ CustomElementsRegistryTest, |
+ collectCandidates_shouldNotIncludeElementsInDifferentDocument) |
+{ |
+ Element* element = CreateElement("a-a").inDocument(&document()); |
+ registry().addCandidate(element); |
+ |
+ Document* otherDocument = HTMLDocument::create(); |
+ otherDocument->appendChild(element); |
+ EXPECT_EQ(otherDocument, element->ownerDocument()) |
+ << "sanity: another document should have adopted an element on append"; |
+ |
+ HeapVector<Member<Element>> elements; |
+ collectCandidates( |
+ CustomElementDescriptor("a-a", "a-a"), |
+ &elements); |
+ |
+ EXPECT_TRUE(elements.isEmpty()) |
+ << "no candidates should have been found, but we have " |
+ << elements.size(); |
+ EXPECT_FALSE(elements.contains(element)) |
+ << "the adopted-away candidate should not have been found"; |
+} |
+ |
+TEST_F( |
+ CustomElementsRegistryTest, |
+ collectCandidates_shouldOnlyIncludeCandidatesMatchingDescriptor) |
+{ |
+ CustomElementDescriptor descriptor("hello-world", "hello-world"); |
+ |
+ // Does not match: namespace is not HTML |
+ Element* elementA = CreateElement("hello-world") |
+ .inDocument(&document()) |
+ .inNamespace("data:text/date,1981-03-10"); |
+ // Matches |
+ Element* elementB = CreateElement("hello-world").inDocument(&document()); |
+ // Does not match: local name is not hello-world |
+ Element* elementC = CreateElement("button") |
+ .inDocument(&document()) |
+ .withIsAttribute("hello-world"); |
+ document().documentElement()->appendChild(elementA); |
+ elementA->appendChild(elementB); |
+ elementA->appendChild(elementC); |
+ |
+ registry().addCandidate(elementA); |
+ registry().addCandidate(elementB); |
+ registry().addCandidate(elementC); |
+ |
+ HeapVector<Member<Element>> elements; |
+ collectCandidates(descriptor, &elements); |
+ |
+ EXPECT_EQ(1u, elements.size()) |
+ << "only one candidates should have been found"; |
+ EXPECT_EQ(elementB, elements[0]) |
+ << "the matching element should have been found"; |
+} |
+ |
+TEST_F(CustomElementsRegistryTest, collectCandidates_oneCandidate) |
+{ |
+ Element* element = CreateElement("a-a").inDocument(&document()); |
+ registry().addCandidate(element); |
+ document().documentElement()->appendChild(element); |
+ |
+ HeapVector<Member<Element>> elements; |
+ collectCandidates( |
+ CustomElementDescriptor("a-a", "a-a"), |
+ &elements); |
+ |
+ EXPECT_EQ(1u, elements.size()) |
+ << "exactly one candidate should have been found"; |
+ EXPECT_TRUE(elements.contains(element)) |
+ << "the candidate should be the element that was added"; |
+} |
+ |
+TEST_F(CustomElementsRegistryTest, collectCandidates_shouldBeInDocumentOrder) |
+{ |
+ CreateElement factory = CreateElement("a-a"); |
+ factory.inDocument(&document()); |
+ Element* elementA = factory.withId("a"); |
+ Element* elementB = factory.withId("b"); |
+ Element* elementC = factory.withId("c"); |
+ |
+ registry().addCandidate(elementB); |
+ registry().addCandidate(elementA); |
+ registry().addCandidate(elementC); |
+ |
+ document().documentElement()->appendChild(elementA); |
+ elementA->appendChild(elementB); |
+ document().documentElement()->appendChild(elementC); |
+ |
+ HeapVector<Member<Element>> elements; |
+ collectCandidates( |
+ CustomElementDescriptor("a-a", "a-a"), |
+ &elements); |
+ |
+ EXPECT_EQ(elementA, elements[0].get()); |
+ EXPECT_EQ(elementB, elements[1].get()); |
+ EXPECT_EQ(elementC, elements[2].get()); |
+} |
+ |
+class TestCustomElementDefinition : public CustomElementDefinition { |
+ WTF_MAKE_NONCOPYABLE(TestCustomElementDefinition); |
+public: |
+ TestCustomElementDefinition(const CustomElementDescriptor& descriptor) |
+ : CustomElementDefinition(descriptor) |
+ { |
+ } |
+ |
+ ~TestCustomElementDefinition() override = default; |
+ |
+ ScriptValue getConstructorForScript() override |
+ { |
+ return ScriptValue(); |
+ } |
+ |
+ bool runConstructor(Element* element) override |
+ { |
+ if (constructionStack().isEmpty() |
+ || constructionStack().last() != element) |
+ return false; |
+ constructionStack().last().clear(); |
+ return true; |
+ } |
+}; |
+ |
+// Classes which use trace macros cannot be local because of the |
+// traceImpl template. |
+class LogUpgradeDefinition : public TestCustomElementDefinition { |
+ WTF_MAKE_NONCOPYABLE(LogUpgradeDefinition); |
+public: |
+ LogUpgradeDefinition(const CustomElementDescriptor& descriptor) |
+ : TestCustomElementDefinition(descriptor) |
+ { |
+ } |
+ |
+ DEFINE_INLINE_VIRTUAL_TRACE() |
+ { |
+ TestCustomElementDefinition::trace(visitor); |
+ visitor->trace(m_element); |
+ } |
+ |
+ // TODO(dominicc): Make this class collect a vector of what's |
+ // upgraded; it will be useful in more tests. |
+ Member<Element> m_element; |
+ uint32_t m_invocationCount; |
+ |
+ bool runConstructor(Element* element) override |
+ { |
+ m_invocationCount++; |
+ m_element = element; |
+ return TestCustomElementDefinition::runConstructor(element); |
+ } |
+}; |
+ |
+class LogUpgradeBuilder final : public CustomElementDefinitionBuilder { |
+ STACK_ALLOCATED(); |
+ WTF_MAKE_NONCOPYABLE(LogUpgradeBuilder); |
+public: |
+ LogUpgradeBuilder() { } |
+ |
+ bool checkConstructorIntrinsics() override { return true; } |
+ bool checkConstructorNotRegistered() override { return true; } |
+ bool checkPrototype() override { return true; } |
+ CustomElementDefinition* build( |
+ const CustomElementDescriptor& descriptor) { |
+ return new LogUpgradeDefinition(descriptor); |
+ } |
+}; |
+ |
+TEST_F(CustomElementsRegistryFrameTest, define_upgradesInDocumentElements) |
+{ |
+ ScriptForbiddenScope doNotRelyOnScript; |
+ |
+ Element* element = CreateElement("a-a").inDocument(&document()); |
+ document().documentElement()->appendChild(element); |
+ |
+ LogUpgradeBuilder builder; |
+ NonThrowableExceptionState shouldNotThrow; |
+ registry().define( |
+ "a-a", |
+ builder, |
+ ElementRegistrationOptions(), |
+ shouldNotThrow); |
+ LogUpgradeDefinition* definition = |
+ static_cast<LogUpgradeDefinition*>(registry().definitionForName("a-a")); |
+ EXPECT_EQ(1u, definition->m_invocationCount) |
+ << "defining the element should have 'upgraded' the existing element"; |
+ EXPECT_EQ(element, definition->m_element) |
+ << "the existing a-a element should have been upgraded"; |
+} |
+ |
+// TODO(dominicc): Add tests which adjust the "is" attribute when type |
+// extensions are implemented. |
+ |
+} // namespace blink |