Make CFX_XMLNode inherit from TreeNode<>.

Simplify things by making GetNthChild() interpret its index argument
in the same manner as existing code.

Two small behaviour changes reflected in the tests:
- TreeNodes can't be tied into knots by duplicate insertions
- TreeNodes don't tolerate removing a child from the wrong parent.

Change-Id: Ia80844b593e3ad03ebb0cd466c0b4cdf741cf192
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/54371
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h
index 656d743..5b1d7df 100644
--- a/core/fxcrt/tree_node.h
+++ b/core/fxcrt/tree_node.h
@@ -27,11 +27,12 @@
     return child != this && child->m_pParent == this;
   }
 
-  T* GetNthChild(size_t n) {
+  T* GetNthChild(int32_t n) {
+    if (n < 0)
+      return nullptr;
     T* result = GetFirstChild();
-    while (n && result) {
+    while (n-- && result) {
       result = result->GetNextSibling();
-      --n;
     }
     return result;
   }
diff --git a/core/fxcrt/tree_node_unittest.cpp b/core/fxcrt/tree_node_unittest.cpp
index 748c1be..fe0ebca 100644
--- a/core/fxcrt/tree_node_unittest.cpp
+++ b/core/fxcrt/tree_node_unittest.cpp
@@ -101,6 +101,7 @@
 
 TEST(TreeNode, NthChild) {
   auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
   EXPECT_EQ(nullptr, pParent->GetNthChild(0));
 
   auto p0 = pdfium::MakeUnique<TestTreeNode>();
@@ -111,6 +112,7 @@
   pParent->AppendLastChild(p1.get());
   pParent->AppendLastChild(p2.get());
   pParent->AppendLastChild(p3.get());
+  EXPECT_EQ(nullptr, pParent->GetNthChild(-1));
   EXPECT_EQ(p0.get(), pParent->GetNthChild(0));
   EXPECT_EQ(p1.get(), pParent->GetNthChild(1));
   EXPECT_EQ(p2.get(), pParent->GetNthChild(2));
diff --git a/core/fxcrt/xml/cfx_xmlelement.cpp b/core/fxcrt/xml/cfx_xmlelement.cpp
index 6ea144e..a294b5f 100644
--- a/core/fxcrt/xml/cfx_xmlelement.cpp
+++ b/core/fxcrt/xml/cfx_xmlelement.cpp
@@ -33,7 +33,7 @@
   for (CFX_XMLNode* pChild = GetFirstChild(); pChild;
        pChild = pChild->GetNextSibling()) {
     if (pChild->GetType() == Type::kText)
-      node->AppendChild(pChild->Clone(doc));
+      node->AppendLastChild(pChild->Clone(doc));
   }
   return node;
 }
diff --git a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
index 08afc61..0053358 100644
--- a/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlelement_unittest.cpp
@@ -78,10 +78,10 @@
   node.SetAttribute(L"xmlns:test", L"https://example.org/test");
 
   CFX_XMLText text_child1(L"Text Child");
-  node.AppendChild(&text_child1);
+  node.AppendLastChild(&text_child1);
 
   CFX_XMLElement node_child1(L"Node child");
-  node.AppendChild(&node_child1);
+  node.AppendLastChild(&node_child1);
 
   CFX_XMLNode* clone = node.Clone(&doc);
   EXPECT_TRUE(clone != nullptr);
@@ -130,16 +130,16 @@
   CFX_XMLElement node(L"node");
 
   CFX_XMLText text_child1(L"Text Child 1");
-  node.AppendChild(&text_child1);
+  node.AppendLastChild(&text_child1);
 
   CFX_XMLElement node_child1(L"node-child");
-  node.AppendChild(&node_child1);
+  node.AppendLastChild(&node_child1);
 
   CFX_XMLText text_child2(L"Text Child 2");
-  node_child1.AppendChild(&text_child2);
+  node_child1.AppendLastChild(&text_child2);
 
   CFX_XMLCharData char_data1(L"Char Data");
-  node.AppendChild(&char_data1);
+  node.AppendLastChild(&char_data1);
 
   node.Save(stream);
   EXPECT_EQ(
@@ -164,7 +164,7 @@
 TEST(CFX_XMLElementTest, GetFirstChildNamed) {
   CFX_XMLElement node(L"node");
   CFX_XMLElement node_child1(L"node-child");
-  node.AppendChild(&node_child1);
+  node.AppendLastChild(&node_child1);
 
   auto* found = node.GetFirstChildNamed(L"node-child");
   EXPECT_TRUE(found != nullptr);
@@ -174,7 +174,7 @@
 TEST(CFX_XMLElementTest, GetFirstChildNamedMissing) {
   CFX_XMLElement node(L"node");
   CFX_XMLElement node_child1(L"node-child");
-  node.AppendChild(&node_child1);
+  node.AppendLastChild(&node_child1);
 
   auto* found = node.GetFirstChildNamed(L"node-sibling");
   EXPECT_TRUE(found == nullptr);
@@ -185,9 +185,9 @@
   CFX_XMLElement node_child1(L"node-child");
   CFX_XMLElement node_child2(L"node-child");
   CFX_XMLElement node_child3(L"node-child");
-  node.AppendChild(&node_child1);
-  node.AppendChild(&node_child2);
-  node.AppendChild(&node_child3);
+  node.AppendLastChild(&node_child1);
+  node.AppendLastChild(&node_child2);
+  node.AppendLastChild(&node_child3);
 
   auto* found = node.GetNthChildNamed(L"node-child", 2);
   EXPECT_TRUE(found != nullptr);
@@ -199,9 +199,9 @@
   CFX_XMLElement node_child1(L"node-child");
   CFX_XMLElement node_child2(L"node-child");
   CFX_XMLElement node_child3(L"node-child");
-  node.AppendChild(&node_child1);
-  node.AppendChild(&node_child2);
-  node.AppendChild(&node_child3);
+  node.AppendLastChild(&node_child1);
+  node.AppendLastChild(&node_child2);
+  node.AppendLastChild(&node_child3);
 
   auto* found = node.GetNthChildNamed(L"node-child", 5);
   EXPECT_TRUE(found == nullptr);
@@ -211,16 +211,16 @@
   CFX_XMLElement node(L"node");
 
   CFX_XMLText text_child1(L"Text Child 1");
-  node.AppendChild(&text_child1);
+  node.AppendLastChild(&text_child1);
 
   CFX_XMLElement node_child1(L"Node child");
-  node.AppendChild(&node_child1);
+  node.AppendLastChild(&node_child1);
 
   CFX_XMLText text_child2(L"Text Child 2");
-  node_child1.AppendChild(&text_child2);
+  node_child1.AppendLastChild(&text_child2);
 
   CFX_XMLCharData char_data1(L"Char Data");
-  node.AppendChild(&char_data1);
+  node.AppendLastChild(&char_data1);
 
   EXPECT_EQ(L"Text Child 1Char Data", node.GetTextData());
 }
diff --git a/core/fxcrt/xml/cfx_xmlnode.cpp b/core/fxcrt/xml/cfx_xmlnode.cpp
index 4fbc8bc..94b035b 100644
--- a/core/fxcrt/xml/cfx_xmlnode.cpp
+++ b/core/fxcrt/xml/cfx_xmlnode.cpp
@@ -6,107 +6,18 @@
 
 #include "core/fxcrt/xml/cfx_xmlnode.h"
 
-#include <utility>
-#include <vector>
-
-#include "core/fxcrt/xml/cfx_xmlchardata.h"
-#include "core/fxcrt/xml/cfx_xmlelement.h"
-#include "core/fxcrt/xml/cfx_xmlinstruction.h"
-#include "core/fxcrt/xml/cfx_xmltext.h"
-
 CFX_XMLNode::CFX_XMLNode() = default;
 
 CFX_XMLNode::~CFX_XMLNode() = default;
 
-void CFX_XMLNode::DeleteChildren() {
-  while (first_child_) {
-    first_child_->parent_ = nullptr;
-    first_child_->prev_sibling_ = nullptr;
-
-    CFX_XMLNode* child = first_child_;
-    first_child_ = child->next_sibling_;
-
-    child->next_sibling_ = nullptr;
-  }
-  last_child_ = nullptr;
-}
-
-void CFX_XMLNode::AppendChild(CFX_XMLNode* pNode) {
-  InsertChildNode(pNode, -1);
-}
-
 void CFX_XMLNode::InsertChildNode(CFX_XMLNode* pNode, int32_t index) {
-  ASSERT(!pNode->parent_);
-
-  pNode->parent_ = this;
-  // No existing children, add node as first child.
-  if (!first_child_) {
-    ASSERT(!last_child_);
-
-    first_child_ = pNode;
-    last_child_ = first_child_;
-    first_child_->prev_sibling_ = nullptr;
-    first_child_->next_sibling_ = nullptr;
-    return;
-  }
-
-  if (index == 0) {
-    first_child_->prev_sibling_ = pNode;
-    pNode->next_sibling_ = first_child_;
-    pNode->prev_sibling_ = nullptr;
-    first_child_ = pNode;
-    return;
-  }
-
-  CFX_XMLNode* pFind;
-  // Note, negative indexes, and indexes after the end of the list will result
-  // in appending to the list.
-  if (index < 0) {
-    // Optimize the negative index case.
-    pFind = last_child_;
-  } else {
-    pFind = first_child_;
-    int32_t iCount = 0;
-    while (++iCount != index && pFind->next_sibling_)
-      pFind = pFind->next_sibling_;
-  }
-
-  pNode->prev_sibling_ = pFind;
-  if (pFind->next_sibling_)
-    pFind->next_sibling_->prev_sibling_ = pNode;
-  pNode->next_sibling_ = pFind->next_sibling_;
-
-  pFind->next_sibling_ = pNode;
-  if (pFind == last_child_)
-    last_child_ = pFind->next_sibling_;
-}
-
-void CFX_XMLNode::RemoveChildNode(CFX_XMLNode* pNode) {
-  ASSERT(first_child_);
-  ASSERT(pNode);
-
-  if (pNode->GetParent() != this)
-    return;
-
-  if (first_child_ == pNode)
-    first_child_ = pNode->next_sibling_;
-  if (last_child_ == pNode)
-    last_child_ = pNode->prev_sibling_;
-
-  if (pNode->prev_sibling_)
-    pNode->prev_sibling_->next_sibling_ = pNode->next_sibling_;
-  if (pNode->next_sibling_)
-    pNode->next_sibling_->prev_sibling_ = pNode->prev_sibling_;
-
-  pNode->parent_ = nullptr;
-  pNode->prev_sibling_ = nullptr;
-  pNode->next_sibling_ = nullptr;
+  InsertBefore(pNode, GetNthChild(index));
 }
 
 CFX_XMLNode* CFX_XMLNode::GetRoot() {
   CFX_XMLNode* pParent = this;
-  while (pParent->parent_)
-    pParent = pParent->parent_;
+  while (pParent->GetParent())
+    pParent = pParent->GetParent();
 
   return pParent;
 }
diff --git a/core/fxcrt/xml/cfx_xmlnode.h b/core/fxcrt/xml/cfx_xmlnode.h
index 728d9f7..d4d48b1 100644
--- a/core/fxcrt/xml/cfx_xmlnode.h
+++ b/core/fxcrt/xml/cfx_xmlnode.h
@@ -8,11 +8,13 @@
 #define CORE_FXCRT_XML_CFX_XMLNODE_H_
 
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/tree_node.h"
 
 class CFX_XMLDocument;
 
-class CFX_XMLNode {
+class CFX_XMLNode : public TreeNode<CFX_XMLNode> {
  public:
   enum class Type {
     kInstruction = 0,
@@ -22,36 +24,17 @@
   };
 
   CFX_XMLNode();
-  virtual ~CFX_XMLNode();
+  ~CFX_XMLNode() override;
 
   virtual Type GetType() const = 0;
   virtual CFX_XMLNode* Clone(CFX_XMLDocument* doc) = 0;
   virtual void Save(const RetainPtr<IFX_SeekableWriteStream>& pXMLStream) = 0;
 
   CFX_XMLNode* GetRoot();
-  CFX_XMLNode* GetParent() const { return parent_; }
-  CFX_XMLNode* GetFirstChild() const { return first_child_; }
-  CFX_XMLNode* GetNextSibling() const { return next_sibling_; }
-
-  void AppendChild(CFX_XMLNode* pNode);
   void InsertChildNode(CFX_XMLNode* pNode, int32_t index);
-  void RemoveChildNode(CFX_XMLNode* pNode);
-  void DeleteChildren();
-
-  CFX_XMLNode* GetLastChildForTesting() const { return last_child_; }
-  CFX_XMLNode* GetPrevSiblingForTesting() const { return prev_sibling_; }
 
  protected:
   WideString EncodeEntities(const WideString& value);
-
- private:
-  // The nodes are owned by the XML document. We do not know what order the
-  // nodes will be destroyed in so they can not be UnownedPtrs.
-  CFX_XMLNode* parent_ = nullptr;
-  CFX_XMLNode* first_child_ = nullptr;
-  CFX_XMLNode* last_child_ = nullptr;
-  CFX_XMLNode* next_sibling_ = nullptr;
-  CFX_XMLNode* prev_sibling_ = nullptr;
 };
 
 #endif  // CORE_FXCRT_XML_CFX_XMLNODE_H_
diff --git a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
index 6055e92..c879cfd 100644
--- a/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
+++ b/core/fxcrt/xml/cfx_xmlnode_unittest.cpp
@@ -19,8 +19,8 @@
 
 WideString ReverseChildrenString(CFX_XMLElement* pParent) {
   WideString result;
-  for (CFX_XMLNode* pChild = pParent->GetLastChildForTesting(); pChild;
-       pChild = pChild->GetPrevSiblingForTesting()) {
+  for (CFX_XMLNode* pChild = pParent->GetLastChild(); pChild;
+       pChild = pChild->GetPrevSibling()) {
     result = static_cast<CFX_XMLElement*>(pChild)->GetName() + result;
   }
   return result;
@@ -33,8 +33,8 @@
   CFX_XMLElement node2(L"node2");
   CFX_XMLElement node3(L"node3");
 
-  node1.AppendChild(&node2);
-  node2.AppendChild(&node3);
+  node1.AppendLastChild(&node2);
+  node2.AppendLastChild(&node3);
 
   EXPECT_EQ(nullptr, node1.GetParent());
   EXPECT_EQ(&node1, node2.GetParent());
@@ -46,8 +46,8 @@
   CFX_XMLElement node2(L"node2");
   CFX_XMLElement node3(L"node3");
 
-  node1.AppendChild(&node2);
-  node2.AppendChild(&node3);
+  node1.AppendLastChild(&node2);
+  node2.AppendLastChild(&node3);
 
   EXPECT_EQ(&node1, node1.GetRoot());
   EXPECT_EQ(&node1, node2.GetRoot());
@@ -60,9 +60,9 @@
   CFX_XMLElement node3(L"node3");
   CFX_XMLElement node4(L"node4");
 
-  node1.AppendChild(&node2);
-  node1.AppendChild(&node4);
-  node2.AppendChild(&node3);
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node4);
+  node2.AppendLastChild(&node3);
 
   EXPECT_EQ(&node2, node1.GetFirstChild());
 
@@ -82,18 +82,18 @@
   CFX_XMLElement node3(L"node3");
   CFX_XMLElement node4(L"node4");
 
-  node1.AppendChild(&node2);
-  node1.AppendChild(&node4);
-  node2.AppendChild(&node3);
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node4);
+  node2.AppendLastChild(&node3);
 
-  node1.DeleteChildren();
+  node1.RemoveAllChildren();
   EXPECT_TRUE(node1.GetFirstChild() == nullptr);
   EXPECT_TRUE(node2.GetParent() == nullptr);
   EXPECT_TRUE(node4.GetParent() == nullptr);
 
   // node2 and node4 should no longer be siblings.
   EXPECT_TRUE(node2.GetNextSibling() == nullptr);
-  EXPECT_TRUE(node4.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node4.GetPrevSibling() == nullptr);
 
   // Deleting children doesn't change deleted substructure
   EXPECT_EQ(&node3, node2.GetFirstChild());
@@ -105,8 +105,8 @@
   CFX_XMLElement nodeA(L"A");
   CFX_XMLElement nodeB(L"B");
 
-  parent.AppendChild(&nodeA);
-  parent.AppendChild(&nodeB);
+  parent.AppendLastChild(&nodeA);
+  parent.AppendLastChild(&nodeB);
 
   EXPECT_EQ(L"AB", ChildrenString(&parent));
   EXPECT_EQ(L"AB", ReverseChildrenString(&parent));
@@ -144,25 +144,15 @@
   EXPECT_EQ(L"EADBCF", ReverseChildrenString(&parent));
 }
 
-#ifndef NDEBUG
-TEST(CFX_XMLNodeTest, DuplicateInsertOfNode) {
-  CFX_XMLElement node1(L"node");
-  CFX_XMLElement node2(L"node2");
-
-  node1.AppendChild(&node2);
-  EXPECT_DEATH(node1.AppendChild(&node2), "");
-}
-#endif  // NDEBUG
-
 TEST(CFX_XMLNodeTest, RemovingMiddleChild) {
   CFX_XMLElement node1(L"node1");
   CFX_XMLElement node2(L"node2");
   CFX_XMLElement node3(L"node3");
   CFX_XMLElement node4(L"node4");
 
-  node1.AppendChild(&node2);
-  node1.AppendChild(&node3);
-  node1.AppendChild(&node4);
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
 
   EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
   EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
@@ -171,16 +161,16 @@
   EXPECT_EQ(&node4, node3.GetNextSibling());
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
 
-  node1.RemoveChildNode(&node3);
+  node1.RemoveChild(&node3);
 
   EXPECT_EQ(L"node2node4", ChildrenString(&node1));
   EXPECT_EQ(L"node2node4", ReverseChildrenString(&node1));
   EXPECT_TRUE(node3.GetParent() == nullptr);
   EXPECT_TRUE(node3.GetNextSibling() == nullptr);
-  EXPECT_TRUE(node3.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node3.GetPrevSibling() == nullptr);
   EXPECT_EQ(&node2, node1.GetFirstChild());
   EXPECT_EQ(&node4, node2.GetNextSibling());
-  EXPECT_EQ(&node2, node4.GetPrevSiblingForTesting());
+  EXPECT_EQ(&node2, node4.GetPrevSibling());
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
 }
 
@@ -190,9 +180,9 @@
   CFX_XMLElement node3(L"node3");
   CFX_XMLElement node4(L"node4");
 
-  node1.AppendChild(&node2);
-  node1.AppendChild(&node3);
-  node1.AppendChild(&node4);
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
 
   EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
   EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
@@ -201,15 +191,15 @@
   EXPECT_EQ(&node4, node3.GetNextSibling());
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
 
-  node1.RemoveChildNode(&node2);
+  node1.RemoveChild(&node2);
 
   EXPECT_EQ(L"node3node4", ChildrenString(&node1));
   EXPECT_EQ(L"node3node4", ReverseChildrenString(&node1));
   EXPECT_TRUE(node2.GetParent() == nullptr);
   EXPECT_TRUE(node2.GetNextSibling() == nullptr);
-  EXPECT_TRUE(node2.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node2.GetPrevSibling() == nullptr);
   EXPECT_EQ(&node3, node1.GetFirstChild());
-  EXPECT_TRUE(node3.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node3.GetPrevSibling() == nullptr);
   EXPECT_EQ(&node4, node3.GetNextSibling());
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
 }
@@ -220,9 +210,9 @@
   CFX_XMLElement node3(L"node3");
   CFX_XMLElement node4(L"node4");
 
-  node1.AppendChild(&node2);
-  node1.AppendChild(&node3);
-  node1.AppendChild(&node4);
+  node1.AppendLastChild(&node2);
+  node1.AppendLastChild(&node3);
+  node1.AppendLastChild(&node4);
 
   EXPECT_EQ(L"node2node3node4", ChildrenString(&node1));
   EXPECT_EQ(L"node2node3node4", ReverseChildrenString(&node1));
@@ -231,13 +221,13 @@
   EXPECT_EQ(&node4, node3.GetNextSibling());
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
 
-  node1.RemoveChildNode(&node4);
+  node1.RemoveChild(&node4);
 
   EXPECT_EQ(L"node2node3", ChildrenString(&node1));
   EXPECT_EQ(L"node2node3", ReverseChildrenString(&node1));
   EXPECT_TRUE(node4.GetParent() == nullptr);
   EXPECT_TRUE(node4.GetNextSibling() == nullptr);
-  EXPECT_TRUE(node4.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node4.GetPrevSibling() == nullptr);
   EXPECT_EQ(&node2, node1.GetFirstChild());
   EXPECT_EQ(&node3, node2.GetNextSibling());
   EXPECT_TRUE(node3.GetNextSibling() == nullptr);
@@ -247,17 +237,17 @@
   CFX_XMLElement node1(L"node1");
   CFX_XMLElement node2(L"node2");
 
-  node1.AppendChild(&node2);
+  node1.AppendLastChild(&node2);
 
   EXPECT_EQ(&node2, node1.GetFirstChild());
   EXPECT_TRUE(node2.GetNextSibling() == nullptr);
 
-  node1.RemoveChildNode(&node2);
+  node1.RemoveChild(&node2);
   EXPECT_TRUE(node2.GetParent() == nullptr);
 
   EXPECT_TRUE(node1.GetFirstChild() == nullptr);
   EXPECT_TRUE(node2.GetNextSibling() == nullptr);
-  EXPECT_TRUE(node2.GetPrevSiblingForTesting() == nullptr);
+  EXPECT_TRUE(node2.GetPrevSibling() == nullptr);
 }
 
 TEST(CFX_XMLNodeTest, RemoveMissingChild) {
@@ -265,10 +255,6 @@
   CFX_XMLElement node2(L"node2");
   CFX_XMLElement node3(L"node3");
 
-  node1.AppendChild(&node2);
-  node1.RemoveChildNode(&node3);
-
-  EXPECT_TRUE(node3.GetParent() == nullptr);
-  EXPECT_EQ(&node2, node1.GetFirstChild());
-  EXPECT_TRUE(node2.GetNextSibling() == nullptr);
+  node1.AppendLastChild(&node2);
+  EXPECT_DEATH(node1.RemoveChild(&node3), "");
 }
diff --git a/core/fxcrt/xml/cfx_xmlparser.cpp b/core/fxcrt/xml/cfx_xmlparser.cpp
index eea684a..f6056e7 100644
--- a/core/fxcrt/xml/cfx_xmlparser.cpp
+++ b/core/fxcrt/xml/cfx_xmlparser.cpp
@@ -126,7 +126,7 @@
         case FDE_XmlSyntaxState::Text:
           if (ch == L'<') {
             if (!current_text_.empty()) {
-              current_node_->AppendChild(
+              current_node_->AppendLastChild(
                   doc->CreateNode<CFX_XMLText>(GetTextData()));
             } else {
               current_buffer_idx++;
@@ -168,7 +168,7 @@
             if (target_name.EqualsASCII("originalXFAVersion") ||
                 target_name.EqualsASCII("acrobat")) {
               auto* node = doc->CreateNode<CFX_XMLInstruction>(target_name);
-              current_node_->AppendChild(node);
+              current_node_->AppendLastChild(node);
               current_node_ = node;
             }
           } else {
@@ -184,7 +184,7 @@
             current_parser_state = FDE_XmlSyntaxState::AttriName;
 
             auto* child = doc->CreateNode<CFX_XMLElement>(GetTextData());
-            current_node_->AppendChild(child);
+            current_node_->AppendLastChild(child);
             current_node_ = child;
           } else {
             current_text_.push_back(ch);
@@ -357,7 +357,7 @@
           if (FXSYS_wcsnicmp(current_span.data(), L"]]>", 3) == 0) {
             current_buffer_idx += 3;
             current_parser_state = FDE_XmlSyntaxState::Text;
-            current_node_->AppendChild(
+            current_node_->AppendLastChild(
                 doc->CreateNode<CFX_XMLCharData>(GetTextData()));
           } else {
             current_text_.push_back(ch);
@@ -461,7 +461,7 @@
     }
   }
 
-  current_node_->AppendChild(doc->CreateNode<CFX_XMLText>(GetTextData()));
+  current_node_->AppendLastChild(doc->CreateNode<CFX_XMLText>(GetTextData()));
   return true;
 }
 
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index 3dcee6c..0cfd135 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -308,16 +308,13 @@
     CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild();
     while (pXMLChild) {
       CFX_XMLNode* pXMLSibling = pXMLChild->GetNextSibling();
-      pXMLNode->RemoveChildNode(pXMLChild);
-      pFakeXMLRoot->AppendChild(pXMLChild);
+      pXMLNode->RemoveChild(pXMLChild);
+      pFakeXMLRoot->AppendLastChild(pXMLChild);
       pXMLChild = pXMLSibling;
     }
   } else {
-    CFX_XMLNode* pXMLParent = pXMLNode->GetParent();
-    if (pXMLParent)
-      pXMLParent->RemoveChildNode(pXMLNode);
-
-    pFakeXMLRoot->AppendChild(pXMLNode);
+    pXMLNode->RemoveSelfIfParented();
+    pFakeXMLRoot->AppendLastChild(pXMLNode);
   }
 
   pParser->ConstructXFANode(pFakeRoot, pFakeXMLRoot);
diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp
index dca33c2..2c8b67d 100644
--- a/fxjs/xfa/cjx_packet.cpp
+++ b/fxjs/xfa/cjx_packet.cpp
@@ -83,7 +83,7 @@
   CFX_XMLElement* element = ToXMLElement(GetXFANode()->GetXMLMappingNode());
   if (bSetting) {
     if (element) {
-      element->AppendChild(
+      element->AppendLastChild(
           GetXFANode()
               ->GetDocument()
               ->GetNotify()
diff --git a/xfa/fxfa/parser/cxfa_document.cpp b/xfa/fxfa/parser/cxfa_document.cpp
index 0264365..541d317 100644
--- a/xfa/fxfa/parser/cxfa_document.cpp
+++ b/xfa/fxfa/parser/cxfa_document.cpp
@@ -1641,9 +1641,8 @@
     pDatasetsRoot->JSObject()->SetCData(XFA_Attribute::Name, L"datasets", false,
                                         false);
 
-    m_pRootNode->GetXMLMappingNode()->AppendChild(pDatasetsXMLNode);
+    m_pRootNode->GetXMLMappingNode()->AppendLastChild(pDatasetsXMLNode);
     m_pRootNode->InsertChildAndNotify(pDatasetsRoot, nullptr);
-
     pDatasetsRoot->SetXMLMappingNode(pDatasetsXMLNode);
   }
 
diff --git a/xfa/fxfa/parser/cxfa_document_parser.cpp b/xfa/fxfa/parser/cxfa_document_parser.cpp
index 61e14fa..9f921e4 100644
--- a/xfa/fxfa/parser/cxfa_document_parser.cpp
+++ b/xfa/fxfa/parser/cxfa_document_parser.cpp
@@ -569,16 +569,14 @@
     pDataXMLNode = pXMLDocumentNode;
   } else {
     auto* pDataElement = xml_doc_->CreateNode<CFX_XMLElement>(L"xfa:data");
-    CFX_XMLNode* pParentXMLNode = pXMLDocumentNode->GetParent();
-    if (pParentXMLNode)
-      pParentXMLNode->RemoveChildNode(pXMLDocumentNode);
+    pXMLDocumentNode->RemoveSelfIfParented();
 
     CFX_XMLElement* pElement = ToXMLElement(pXMLDocumentNode);
     pElement->RemoveAttribute(L"xmlns:xfa");
 
     // The node was either removed from the parent above, or already has no
     // parent so we can take ownership.
-    pDataElement->AppendChild(pXMLDocumentNode);
+    pDataElement->AppendLastChild(pXMLDocumentNode);
     pDataXMLNode = pDataElement;
   }
   if (!pDataXMLNode)
diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp
index 496c5db..4aa61a1 100644
--- a/xfa/fxfa/parser/cxfa_node.cpp
+++ b/xfa/fxfa/parser/cxfa_node.cpp
@@ -989,11 +989,10 @@
                          ->GetHDOC()
                          ->GetXMLDocument()
                          ->CreateNode<CFX_XMLText>(wsValue);
-        pCloneXMLElement->AppendChild(text);
+        pCloneXMLElement->AppendLastChild(text);
       }
 
       pCloneXML = pCloneXMLElement;
-
       pClone->JSObject()->SetEnum(XFA_Attribute::Contains,
                                   XFA_AttributeValue::Unknown, false);
     } else {
@@ -1567,7 +1566,7 @@
     return;
 
   if (!pNode->IsAttributeInXML()) {
-    xml_node_->RemoveChildNode(pNode->xml_node_.Get());
+    xml_node_->RemoveChild(pNode->xml_node_.Get());
     return;
   }
 
@@ -1595,7 +1594,7 @@
                      ->GetHDOC()
                      ->GetXMLDocument()
                      ->CreateNode<CFX_XMLText>(wsValue);
-    pNewXMLElement->AppendChild(text);
+    pNewXMLElement->AppendLastChild(text);
   }
   pNode->xml_node_ = pNewXMLElement;
   pNode->JSObject()->SetEnum(XFA_Attribute::Contains,
@@ -5080,14 +5079,14 @@
         }
       }
       if (bDeleteChildren)
-        elem->DeleteChildren();
+        elem->RemoveAllChildren();
 
       auto* text = GetDocument()
                        ->GetNotify()
                        ->GetHDOC()
                        ->GetXMLDocument()
                        ->CreateNode<CFX_XMLText>(value);
-      elem->AppendChild(text);
+      elem->AppendLastChild(text);
       break;
     }
     case CFX_XMLNode::Type::kText: