blob: 0c9ebfa99c6bd7ed8491dc8f29a3d337ecc507cc [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 "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