// Copyright 2017 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "xfa/fxfa/parser/cxfa_node.h" #include "fxjs/gc/heap.h" #include "fxjs/xfa/cjx_node.h" #include "testing/fxgc_unittest.h" #include "testing/gtest/include/gtest/gtest.h" #include "v8/include/cppgc/allocation.h" #include "v8/include/cppgc/persistent.h" #include "xfa/fxfa/parser/cxfa_document.h" namespace { class TestNode final : public CXFA_Node { public: CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; ~TestNode() override = default; private: explicit TestNode(CXFA_Document* doc) : CXFA_Node(doc, XFA_PacketType::Form, {XFA_XDPPACKET::kTemplate, XFA_XDPPACKET::kForm}, XFA_ObjectType::Node, XFA_Element::Node, {}, {}, cppgc::MakeGarbageCollected( doc->GetHeap()->GetAllocationHandle(), this)) {} }; } // namespace class CXFANodeTest : public FXGCUnitTest { public: void SetUp() override { FXGCUnitTest::SetUp(); doc_ = cppgc::MakeGarbageCollected( heap()->GetAllocationHandle(), nullptr, heap(), nullptr); node_ = cppgc::MakeGarbageCollected(heap()->GetAllocationHandle(), doc_); } void TearDown() override { node_.Clear(); doc_.Clear(); FXGCUnitTest::TearDown(); } CXFA_Document* GetDoc() const { return doc_; } CXFA_Node* GetNode() const { return node_; } private: cppgc::Persistent doc_; cppgc::Persistent node_; }; TEST_F(CXFANodeTest, InsertFirstChild) { EXPECT_FALSE(GetNode()->GetFirstChild()); EXPECT_FALSE(GetNode()->GetLastChild()); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_EQ(child, GetNode()->GetFirstChild()); EXPECT_EQ(child, GetNode()->GetLastChild()); EXPECT_FALSE(child->GetPrevSibling()); EXPECT_FALSE(child->GetNextSibling()); } TEST_F(CXFANodeTest, InsertChildByNegativeIndex) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_FALSE(child->GetNextSibling()); EXPECT_EQ(child0, child->GetPrevSibling()); EXPECT_EQ(child, child0->GetNextSibling()); EXPECT_FALSE(child0->GetPrevSibling()); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, InsertChildByIndex) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child2 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child2); CXFA_Node* child3 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child3); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(2, child); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child1, child0->GetNextSibling()); EXPECT_EQ(child, child1->GetNextSibling()); EXPECT_EQ(child2, child->GetNextSibling()); EXPECT_EQ(child3, child2->GetNextSibling()); EXPECT_FALSE(child3->GetNextSibling()); EXPECT_EQ(child3, GetNode()->GetLastChild()); EXPECT_EQ(child2, child3->GetPrevSibling()); EXPECT_EQ(child, child2->GetPrevSibling()); EXPECT_EQ(child1, child->GetPrevSibling()); EXPECT_EQ(child0, child1->GetPrevSibling()); EXPECT_FALSE(child0->GetPrevSibling()); } TEST_F(CXFANodeTest, InsertChildIndexPastEnd) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(20, child); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_FALSE(child->GetNextSibling()); EXPECT_EQ(child1, child->GetPrevSibling()); EXPECT_EQ(child, child1->GetNextSibling()); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, InsertFirstChildBeforeNullptr) { EXPECT_FALSE(GetNode()->GetFirstChild()); EXPECT_FALSE(GetNode()->GetLastChild()); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(child, nullptr); EXPECT_EQ(child, GetNode()->GetFirstChild()); EXPECT_EQ(child, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, InsertBeforeWithNullBefore) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(child, nullptr); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_FALSE(child->GetNextSibling()); EXPECT_EQ(child1, child->GetPrevSibling()); EXPECT_EQ(child, child1->GetNextSibling()); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, InsertBeforeFirstChild) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(child, child0); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_EQ(child0, child->GetNextSibling()); EXPECT_FALSE(child->GetPrevSibling()); EXPECT_EQ(child, child0->GetPrevSibling()); EXPECT_EQ(child, GetNode()->GetFirstChild()); EXPECT_EQ(child1, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, InsertBefore) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child2 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child2); CXFA_Node* child3 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child3); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(child, child2); EXPECT_EQ(GetNode(), child->GetParent()); EXPECT_EQ(child2, child->GetNextSibling()); EXPECT_EQ(child1, child->GetPrevSibling()); EXPECT_EQ(child, child1->GetNextSibling()); EXPECT_EQ(child, child2->GetPrevSibling()); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child3, GetNode()->GetLastChild()); } TEST_F(CXFANodeTest, RemoveOnlyChild) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child0, GetNode()->GetLastChild()); GetNode()->RemoveChildAndNotify(child0, false); EXPECT_FALSE(GetNode()->GetFirstChild()); EXPECT_FALSE(GetNode()->GetLastChild()); EXPECT_FALSE(child0->GetParent()); EXPECT_FALSE(child0->GetNextSibling()); EXPECT_FALSE(child0->GetPrevSibling()); } TEST_F(CXFANodeTest, RemoveFirstChild) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child2 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child2); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child2, GetNode()->GetLastChild()); GetNode()->RemoveChildAndNotify(child0, false); EXPECT_EQ(child1, GetNode()->GetFirstChild()); EXPECT_EQ(child2, GetNode()->GetLastChild()); EXPECT_FALSE(child1->GetPrevSibling()); EXPECT_FALSE(child0->GetParent()); EXPECT_FALSE(child0->GetNextSibling()); EXPECT_FALSE(child0->GetPrevSibling()); } TEST_F(CXFANodeTest, RemoveLastChild) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child2 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child2); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child2, GetNode()->GetLastChild()); GetNode()->RemoveChildAndNotify(child2, false); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child1, GetNode()->GetLastChild()); EXPECT_FALSE(child1->GetNextSibling()); EXPECT_FALSE(child2->GetParent()); EXPECT_FALSE(child2->GetNextSibling()); EXPECT_FALSE(child2->GetPrevSibling()); } TEST_F(CXFANodeTest, RemoveChild) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* child2 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child2); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child2, GetNode()->GetLastChild()); GetNode()->RemoveChildAndNotify(child1, false); EXPECT_EQ(child0, GetNode()->GetFirstChild()); EXPECT_EQ(child2, GetNode()->GetLastChild()); EXPECT_EQ(child2, child0->GetNextSibling()); EXPECT_EQ(child0, child2->GetPrevSibling()); EXPECT_FALSE(child1->GetParent()); EXPECT_FALSE(child1->GetNextSibling()); EXPECT_FALSE(child1->GetPrevSibling()); } TEST_F(CXFANodeTest, InsertChildWithParent) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); child0->InsertChildAndNotify(-1, child1); EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(0, child1), ""); } TEST_F(CXFANodeTest, InsertNullChild) { EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(0, nullptr), ""); } TEST_F(CXFANodeTest, InsertBeforeWithNullChild) { EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(nullptr, nullptr), ""); } TEST_F(CXFANodeTest, InsertBeforeWithBeforeInAnotherParent) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); child0->InsertChildAndNotify(-1, child1); CXFA_Node* child = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(child, child1), ""); } TEST_F(CXFANodeTest, InsertBeforeWithNodeInAnotherParent) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); child0->InsertChildAndNotify(-1, child1); EXPECT_DEATH_IF_SUPPORTED(GetNode()->InsertChildAndNotify(child1, nullptr), ""); } TEST_F(CXFANodeTest, RemoveChildNullptr) { EXPECT_DEATH_IF_SUPPORTED(GetNode()->RemoveChildAndNotify(nullptr, false), ""); } TEST_F(CXFANodeTest, RemoveChildAnotherParent) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); child0->InsertChildAndNotify(-1, child1); GetNode()->RemoveChildAndNotify(child1, false); EXPECT_EQ(child0, child1->GetParent()); } TEST_F(CXFANodeTest, AncestorOf) { CXFA_Node* child0 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child0); CXFA_Node* child1 = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); GetNode()->InsertChildAndNotify(-1, child1); CXFA_Node* grandchild = GetDoc()->CreateNode(XFA_PacketType::Form, XFA_Element::Ui); child0->InsertChildAndNotify(-1, grandchild); EXPECT_TRUE(GetNode()->IsAncestorOf(child0)); EXPECT_TRUE(GetNode()->IsAncestorOf(child1)); EXPECT_FALSE(child1->IsAncestorOf(child0)); EXPECT_TRUE(child0->IsAncestorOf(grandchild)); EXPECT_TRUE(GetNode()->IsAncestorOf(grandchild)); EXPECT_FALSE(child1->IsAncestorOf(grandchild)); } TEST_F(CXFANodeTest, DeltaObjectIsNode) { CXFA_Node* delta = CXFA_Node::Create(GetDoc(), XFA_Element::Delta, XFA_PacketType::Form); ASSERT_TRUE(delta); ASSERT_TRUE(delta->IsNode()); // This call should not crash, like in crbug.com/1465239. delta->JSObject()->SetAttributeByEnum(XFA_Attribute::Name, L"delta", /*bNotify=*/false); }