| // 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 "core/dom/LiveNodeListRegistry.h" |
| |
| #include "core/dom/Document.h" |
| #include "core/dom/NameNodeList.h" |
| #include "core/testing/DummyPageHolder.h" |
| #include "platform/heap/Persistent.h" |
| #include "platform/heap/ThreadState.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace blink { |
| namespace { |
| |
| class LiveNodeListRegistryTest : public ::testing::Test { |
| public: |
| void SetUp() override { page_holder_ = DummyPageHolder::Create(); } |
| |
| protected: |
| const LiveNodeListBase* CreateNodeList() { |
| return NameNodeList::Create(page_holder_->GetDocument(), kNameNodeListType, |
| g_empty_atom); |
| } |
| |
| private: |
| std::unique_ptr<DummyPageHolder> page_holder_; |
| }; |
| |
| TEST_F(LiveNodeListRegistryTest, InitialState) { |
| LiveNodeListRegistry registry; |
| EXPECT_TRUE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| } |
| |
| // The invalidation types which match should be updated as elements are added. |
| TEST_F(LiveNodeListRegistryTest, Add) { |
| LiveNodeListRegistry registry; |
| const auto* a = CreateNodeList(); |
| const auto* b = CreateNodeList(); |
| |
| // Addition of a single node list with a single invalidation type. |
| registry.Add(a, kInvalidateOnNameAttrChange); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| // Addition of another node list with another invalidation type. |
| registry.Add(b, kInvalidateOnClassAttrChange); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| // It is okay for the same node list to be added with different invalidation |
| // types. |
| registry.Add(a, kInvalidateOnIdNameAttrChange); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| } |
| |
| // The set of types which match should be updated as elements are removed. |
| TEST_F(LiveNodeListRegistryTest, ExplicitRemove) { |
| LiveNodeListRegistry registry; |
| const auto* a = CreateNodeList(); |
| const auto* b = CreateNodeList(); |
| |
| registry.Add(a, kInvalidateOnNameAttrChange); |
| registry.Add(b, kInvalidateOnClassAttrChange); |
| registry.Add(a, kInvalidateOnIdNameAttrChange); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| registry.Remove(a, kInvalidateOnNameAttrChange); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| registry.Remove(a, kInvalidateOnIdNameAttrChange); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| registry.Remove(b, kInvalidateOnClassAttrChange); |
| EXPECT_TRUE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| } |
| |
| // This is a hack for test purposes. The test below forces a GC to happen and |
| // claims that there are no GC pointers on the stack. For this to be valid, the |
| // tracker itself must live on the heap, not on the stack. |
| struct LiveNodeListRegistryWrapper |
| : public GarbageCollectedFinalized<LiveNodeListRegistryWrapper> { |
| LiveNodeListRegistry registry; |
| void Trace(blink::Visitor* visitor) { visitor->Trace(registry); } |
| }; |
| |
| // The set of types which match should be updated as elements are removed due to |
| // the garbage collected. Similar to the previous case, except all references to |
| // |a| are removed together by the GC. |
| TEST_F(LiveNodeListRegistryTest, ImplicitRemove) { |
| auto wrapper = WrapPersistent(new LiveNodeListRegistryWrapper); |
| auto& registry = wrapper->registry; |
| auto a = WrapPersistent(CreateNodeList()); |
| auto b = WrapPersistent(CreateNodeList()); |
| |
| registry.Add(a, kInvalidateOnNameAttrChange); |
| registry.Add(b, kInvalidateOnClassAttrChange); |
| registry.Add(a, kInvalidateOnIdNameAttrChange); |
| ThreadState::Current()->CollectAllGarbage(); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| a.Clear(); |
| ThreadState::Current()->CollectAllGarbage(); |
| EXPECT_FALSE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_TRUE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| |
| b.Clear(); |
| ThreadState::Current()->CollectAllGarbage(); |
| EXPECT_TRUE(registry.IsEmpty()); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnNameAttrChange)); |
| EXPECT_FALSE(registry.ContainsInvalidationType(kInvalidateOnClassAttrChange)); |
| EXPECT_FALSE( |
| registry.ContainsInvalidationType(kInvalidateOnIdNameAttrChange)); |
| } |
| |
| } // namespace |
| } // namespace blink |