| // 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<CJX_Node>( |
| doc->GetHeap()->GetAllocationHandle(), |
| this)) {} |
| }; |
| |
| } // namespace |
| |
| class CXFANodeTest : public FXGCUnitTest { |
| public: |
| void SetUp() override { |
| FXGCUnitTest::SetUp(); |
| doc_ = cppgc::MakeGarbageCollected<CXFA_Document>( |
| heap()->GetAllocationHandle(), nullptr, heap(), nullptr); |
| node_ = cppgc::MakeGarbageCollected<TestNode>(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<CXFA_Document> doc_; |
| cppgc::Persistent<TestNode> 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); |
| } |