Add more helper methods to tree_node.h

Adds RemoveSelfIfParented(), RemoveAllChildren() and GetNthChild(). The
last two are expected to be useful for converting CFX_XMLNode over
to TreeNodes.

Change-Id: Ib03464e994416156a9d79c4c229af77e413b64c3
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/54350
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/tree_node.h b/core/fxcrt/tree_node.h
index 7f1f46a..656d743 100644
--- a/core/fxcrt/tree_node.h
+++ b/core/fxcrt/tree_node.h
@@ -27,6 +27,15 @@
     return child != this && child->m_pParent == this;
   }
 
+  T* GetNthChild(size_t n) {
+    T* result = GetFirstChild();
+    while (n && result) {
+      result = result->GetNextSibling();
+      --n;
+    }
+    return result;
+  }
+
   void AppendFirstChild(T* child) {
     BecomeParent(child);
     if (m_pFirstChild) {
@@ -110,6 +119,16 @@
     child->m_pNextSibling = nullptr;
   }
 
+  void RemoveAllChildren() {
+    while (T* child = GetFirstChild())
+      RemoveChild(child);
+  }
+
+  void RemoveSelfIfParented() {
+    if (T* parent = GetParent())
+      parent->RemoveChild(static_cast<T*>(this));
+  }
+
  private:
   // Child left in state where sibling members need subsequent adjustment.
   void BecomeParent(T* child) {
diff --git a/core/fxcrt/tree_node_unittest.cpp b/core/fxcrt/tree_node_unittest.cpp
index f2334cf..748c1be 100644
--- a/core/fxcrt/tree_node_unittest.cpp
+++ b/core/fxcrt/tree_node_unittest.cpp
@@ -67,4 +67,56 @@
   EXPECT_DEATH(pBadParent->RemoveChild(pNode.get()), "");
 }
 
+TEST(TreeNode, SafeRemove) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  auto pChild = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendFirstChild(pChild.get());
+  pChild->RemoveSelfIfParented();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+  EXPECT_EQ(nullptr, pChild->GetParent());
+}
+
+TEST(TreeNode, SafeRemoveParentless) {
+  auto pNode = pdfium::MakeUnique<TestTreeNode>();
+  pNode->RemoveSelfIfParented();
+  EXPECT_EQ(nullptr, pNode->GetParent());
+}
+
+TEST(TreeNode, RemoveAllChildren) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  pParent->RemoveAllChildren();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+
+  auto p0 = pdfium::MakeUnique<TestTreeNode>();
+  auto p1 = pdfium::MakeUnique<TestTreeNode>();
+  auto p2 = pdfium::MakeUnique<TestTreeNode>();
+  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendLastChild(p0.get());
+  pParent->AppendLastChild(p1.get());
+  pParent->AppendLastChild(p2.get());
+  pParent->AppendLastChild(p3.get());
+  pParent->RemoveAllChildren();
+  EXPECT_EQ(nullptr, pParent->GetFirstChild());
+}
+
+TEST(TreeNode, NthChild) {
+  auto pParent = pdfium::MakeUnique<TestTreeNode>();
+  EXPECT_EQ(nullptr, pParent->GetNthChild(0));
+
+  auto p0 = pdfium::MakeUnique<TestTreeNode>();
+  auto p1 = pdfium::MakeUnique<TestTreeNode>();
+  auto p2 = pdfium::MakeUnique<TestTreeNode>();
+  auto p3 = pdfium::MakeUnique<TestTreeNode>();
+  pParent->AppendLastChild(p0.get());
+  pParent->AppendLastChild(p1.get());
+  pParent->AppendLastChild(p2.get());
+  pParent->AppendLastChild(p3.get());
+  EXPECT_EQ(p0.get(), pParent->GetNthChild(0));
+  EXPECT_EQ(p1.get(), pParent->GetNthChild(1));
+  EXPECT_EQ(p2.get(), pParent->GetNthChild(2));
+  EXPECT_EQ(p3.get(), pParent->GetNthChild(3));
+  EXPECT_EQ(nullptr, pParent->GetNthChild(4));
+  pParent->RemoveAllChildren();
+}
+
 }  // namespace fxcrt
diff --git a/xfa/fxfa/layout/cxfa_itemlayoutprocessor.cpp b/xfa/fxfa/layout/cxfa_itemlayoutprocessor.cpp
index 12e8a28..0a22d25 100644
--- a/xfa/fxfa/layout/cxfa_itemlayoutprocessor.cpp
+++ b/xfa/fxfa/layout/cxfa_itemlayoutprocessor.cpp
@@ -804,8 +804,7 @@
   CXFA_ContentLayoutItem* pLayoutItem = m_pLayoutItem;
   if (pLayoutItem) {
     m_pLayoutItem = ToContentLayoutItem(pLayoutItem->GetNextSibling());
-    if (pLayoutItem->GetParent())
-      pLayoutItem->GetParent()->RemoveChild(pLayoutItem);
+    pLayoutItem->RemoveSelfIfParented();
   }
   if (m_nCurChildNodeStage != Stage::kDone || !m_pOldLayoutItem)
     return pLayoutItem;
@@ -821,8 +820,7 @@
     if (pToDeleteItem == pLayoutItem)
       break;
     pNotify->OnLayoutItemRemoving(pDocLayout, pToDeleteItem);
-    if (pToDeleteItem->GetParent())
-      pToDeleteItem->GetParent()->RemoveChild(pToDeleteItem);
+    pToDeleteItem->RemoveSelfIfParented();
     delete pToDeleteItem;
   }
   return pLayoutItem;
@@ -1598,8 +1596,7 @@
       while (pLayoutNextTemp) {
         CXFA_ContentLayoutItem* pSaveLayoutNext =
             ToContentLayoutItem(pLayoutNextTemp->GetNextSibling());
-        if (pLayoutNextTemp->GetParent())
-          pLayoutNextTemp->GetParent()->RemoveChild(pLayoutNextTemp);
+        pLayoutNextTemp->RemoveSelfIfParented();
         pLayoutNextTemp = pSaveLayoutNext;
       }
       pLastChild = nullptr;
diff --git a/xfa/fxfa/layout/cxfa_layoutitem.cpp b/xfa/fxfa/layout/cxfa_layoutitem.cpp
index a59da61..f39ba88 100644
--- a/xfa/fxfa/layout/cxfa_layoutitem.cpp
+++ b/xfa/fxfa/layout/cxfa_layoutitem.cpp
@@ -29,8 +29,7 @@
     pNotify->OnPageEvent(ToViewLayoutItem(pLayoutItem),
                          XFA_PAGEVIEWEVENT_PostRemoved);
   }
-  if (pLayoutItem->GetParent())
-    pLayoutItem->GetParent()->RemoveChild(pLayoutItem);
+  pLayoutItem->RemoveSelfIfParented();
   delete pLayoutItem;
 }
 
@@ -44,8 +43,7 @@
   if (pLayoutItem->GetFormNode()->GetElementType() == XFA_Element::PageArea)
     return;
 
-  if (pLayoutItem->GetParent())
-    pLayoutItem->GetParent()->RemoveChild(pLayoutItem);
+  pLayoutItem->RemoveSelfIfParented();
   delete pLayoutItem;
 }
 
diff --git a/xfa/fxfa/layout/cxfa_layoutpagemgr.cpp b/xfa/fxfa/layout/cxfa_layoutpagemgr.cpp
index d9c4e11..700eba5 100644
--- a/xfa/fxfa/layout/cxfa_layoutpagemgr.cpp
+++ b/xfa/fxfa/layout/cxfa_layoutpagemgr.cpp
@@ -167,14 +167,6 @@
   pParentLayoutItem->AppendLastChild(pLayoutItem);
 }
 
-void RemoveLayoutItem(CXFA_LayoutItem* pLayoutItem) {
-  CXFA_LayoutItem* pParentLayoutItem = pLayoutItem->GetParent();
-  if (!pParentLayoutItem)
-    return;
-
-  pParentLayoutItem->RemoveChild(pLayoutItem);
-}
-
 CXFA_Node* ResolveBreakTarget(CXFA_Node* pPageSetRoot,
                               bool bNewExprStyle,
                               WideString* pTargetAll) {
@@ -262,7 +254,7 @@
       SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
 
     pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
-    RemoveLayoutItem(pCurLayoutItem);
+    pCurLayoutItem->RemoveSelfIfParented();
     delete pCurLayoutItem;
     pCurLayoutItem = pNextLayoutItem;
   }
@@ -388,7 +380,7 @@
   ASSERT(m_pTemplatePageSetRoot);
 
   if (m_pPageSetLayoutItemRoot) {
-    RemoveLayoutItem(m_pPageSetLayoutItemRoot);
+    m_pPageSetLayoutItemRoot->RemoveSelfIfParented();
   } else {
     m_pPageSetLayoutItemRoot =
         new CXFA_ViewLayoutItem(m_pTemplatePageSetRoot, nullptr);
@@ -522,15 +514,15 @@
   if (!pNewRecord || !pPrevRecord)
     return;
   if (pNewRecord->pCurPageSet != pPrevRecord->pCurPageSet) {
-    RemoveLayoutItem(pNewRecord->pCurPageSet);
+    pNewRecord->pCurPageSet->RemoveSelfIfParented();
     return;
   }
   if (pNewRecord->pCurPageArea != pPrevRecord->pCurPageArea) {
-    RemoveLayoutItem(pNewRecord->pCurPageArea);
+    pNewRecord->pCurPageArea->RemoveSelfIfParented();
     return;
   }
   if (pNewRecord->pCurContentArea != pPrevRecord->pCurContentArea) {
-    RemoveLayoutItem(pNewRecord->pCurContentArea);
+    pNewRecord->pCurContentArea->RemoveSelfIfParented();
     return;
   }
 }
@@ -1622,7 +1614,7 @@
           SyncRemoveLayoutItem(pCurLayoutItem, pNotify, pDocLayout);
 
         pNotify->OnLayoutItemRemoving(pDocLayout, pCurLayoutItem);
-        RemoveLayoutItem(pCurLayoutItem);
+        pCurLayoutItem->RemoveSelfIfParented();
         delete pCurLayoutItem;
         pCurLayoutItem = pNextLayoutItem;
         continue;
@@ -1641,7 +1633,7 @@
     if (pCurLayoutItem->GetFirstChild())
       SaveLayoutItem(pCurLayoutItem);
 
-    RemoveLayoutItem(pCurLayoutItem);
+    pCurLayoutItem->RemoveSelfIfParented();
     if (!pCurLayoutItem->IsContentLayoutItem() &&
         pCurLayoutItem->GetFormNode()->GetElementType() !=
             XFA_Element::PageArea) {
@@ -1768,7 +1760,7 @@
                       pIter->JSObject()->GetLayoutItem();
                   if (pLayoutItem) {
                     pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-                    RemoveLayoutItem(pLayoutItem);
+                    pLayoutItem->RemoveSelfIfParented();
                     delete pLayoutItem;
                   }
                 }
@@ -1834,7 +1826,7 @@
                   pChildNode->JSObject()->GetLayoutItem();
               if (pLayoutItem) {
                 pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-                RemoveLayoutItem(pLayoutItem);
+                pLayoutItem->RemoveSelfIfParented();
                 delete pLayoutItem;
               }
             }
@@ -1842,7 +1834,7 @@
             CXFA_LayoutItem* pLayoutItem = pNode->JSObject()->GetLayoutItem();
             if (pLayoutItem) {
               pNotify->OnLayoutItemRemoving(pDocLayout, pLayoutItem);
-              RemoveLayoutItem(pLayoutItem);
+              pLayoutItem->RemoveSelfIfParented();
               delete pLayoutItem;
             }
           }
@@ -1971,7 +1963,7 @@
   for (; pRootLayoutItem; pRootLayoutItem = pNextLayout) {
     pNextLayout = ToViewLayoutItem(pRootLayoutItem->GetNextSibling());
     SaveLayoutItem(pRootLayoutItem);
-    RemoveLayoutItem(pRootLayoutItem);
+    pRootLayoutItem->RemoveSelfIfParented();
     delete pRootLayoutItem;
   }
   m_pPageSetLayoutItemRoot = nullptr;