Add CPDF_NameTree::CreateForTesting().

Make the test-only ctor private, and migrate non-test code that use the
test-only ctor to use the non-test ctor. Also disallow copying
CPDF_NameTree.

Change-Id: Ie95aec563ce6312a6a259bd2ef294fd4d8f5d7a2
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/68192
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index 80bb5de..a42376e 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -312,6 +312,12 @@
 
 CPDF_NameTree::~CPDF_NameTree() = default;
 
+// static
+std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateForTesting(
+    CPDF_Dictionary* pRoot) {
+  return pdfium::WrapUnique(new CPDF_NameTree(pRoot));  // Private ctor.
+}
+
 size_t CPDF_NameTree::GetCount() const {
   return m_pRoot ? CountNamesInternal(m_pRoot.Get(), 0) : 0;
 }
diff --git a/core/fpdfdoc/cpdf_nametree.h b/core/fpdfdoc/cpdf_nametree.h
index b018ae7..6ad1c1e 100644
--- a/core/fpdfdoc/cpdf_nametree.h
+++ b/core/fpdfdoc/cpdf_nametree.h
@@ -19,10 +19,14 @@
 
 class CPDF_NameTree {
  public:
-  explicit CPDF_NameTree(CPDF_Dictionary* pRoot);
   CPDF_NameTree(CPDF_Document* pDoc, const ByteString& category);
+  CPDF_NameTree(const CPDF_NameTree&) = delete;
+  CPDF_NameTree& operator=(const CPDF_NameTree&) = delete;
   ~CPDF_NameTree();
 
+  static std::unique_ptr<CPDF_NameTree> CreateForTesting(
+      CPDF_Dictionary* pRoot);
+
   bool AddValueAndName(RetainPtr<CPDF_Object> pObj, const WideString& name);
   bool DeleteValueAndName(int nIndex);
 
@@ -34,6 +38,8 @@
   CPDF_Dictionary* GetRootForTest() const { return m_pRoot.Get(); }
 
  private:
+  explicit CPDF_NameTree(CPDF_Dictionary* pRoot);
+
   RetainPtr<CPDF_Dictionary> m_pRoot;
 };
 
diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp
index 8b9c95f..6ef0d1b 100644
--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp
+++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp
@@ -89,14 +89,15 @@
   pNames->AppendNew<CPDF_Number>(100);
 
   // Check that the key is as expected.
-  CPDF_NameTree name_tree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
   WideString storedName;
-  name_tree.LookupValueAndName(0, &storedName);
+  name_tree->LookupValueAndName(0, &storedName);
   EXPECT_STREQ(L"1", storedName.c_str());
 
   // Check that the correct value object can be obtained by looking up "1".
   WideString matchName = L"1";
-  CPDF_Object* pObj = name_tree.LookupValue(matchName);
+  CPDF_Object* pObj = name_tree->LookupValue(matchName);
   ASSERT_TRUE(pObj->IsNumber());
   EXPECT_EQ(100, pObj->AsNumber()->GetInteger());
 }
@@ -108,23 +109,24 @@
   AddNameKeyValue(pNames, "2.txt", 222);
   AddNameKeyValue(pNames, "7.txt", 777);
 
-  CPDF_NameTree name_tree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Insert a name that already exists in the names array.
-  EXPECT_FALSE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
-                                         L"2.txt"));
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                          L"2.txt"));
 
   // Insert in the beginning of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
-                                        L"1.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                         L"1.txt"));
 
   // Insert in the middle of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
-                                        L"5.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
+                                         L"5.txt"));
 
   // Insert at the end of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
-                                        L"9.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
+                                         L"9.txt"));
 
   // Check that the names array has the expected key-value pairs.
   CheckNameKeyValue(pNames, 0, "1.txt", 111);
@@ -139,27 +141,28 @@
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   CPDF_Array* pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
 
-  CPDF_NameTree name_tree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Insert a name should work.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
-                                        L"2.txt"));
-
-  // Insert a name that already exists in the names array.
-  EXPECT_FALSE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
                                          L"2.txt"));
 
+  // Insert a name that already exists in the names array.
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                          L"2.txt"));
+
   // Insert in the beginning of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
-                                        L"1.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(111),
+                                         L"1.txt"));
 
   // Insert in the middle of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
-                                        L"5.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(555),
+                                         L"5.txt"));
 
   // Insert at the end of the names array.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
-                                        L"9.txt"));
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(999),
+                                         L"9.txt"));
 
   // Check that the names array has the expected key-value pairs.
   CheckNameKeyValue(pNames, 0, "1.txt", 111);
@@ -172,39 +175,40 @@
   // Set up a name tree with five nodes of three levels.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   FillNameTreeDict(pRootDict.Get());
-  CPDF_NameTree name_tree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Check that adding an existing name would fail.
-  EXPECT_FALSE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
-                                         L"9.txt"));
+  EXPECT_FALSE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
+                                          L"9.txt"));
 
   // Add a name within the limits of a leaf node.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
-                                        L"4.txt"));
-  ASSERT_TRUE(name_tree.LookupValue(L"4.txt"));
-  EXPECT_EQ(444, name_tree.LookupValue(L"4.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(444),
+                                         L"4.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"4.txt"));
+  EXPECT_EQ(444, name_tree->LookupValue(L"4.txt")->GetInteger());
 
   // Add a name that requires changing the limits of two bottom levels.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(666),
-                                        L"6.txt"));
-  ASSERT_TRUE(name_tree.LookupValue(L"6.txt"));
-  EXPECT_EQ(666, name_tree.LookupValue(L"6.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(666),
+                                         L"6.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"6.txt"));
+  EXPECT_EQ(666, name_tree->LookupValue(L"6.txt")->GetInteger());
 
   // Add a name that requires changing the limits of two top levels.
-  EXPECT_TRUE(name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(99),
-                                        L"99.txt"));
-  ASSERT_TRUE(name_tree.LookupValue(L"99.txt"));
-  EXPECT_EQ(99, name_tree.LookupValue(L"99.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(99),
+                                         L"99.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"99.txt"));
+  EXPECT_EQ(99, name_tree->LookupValue(L"99.txt")->GetInteger());
 
   // Add a name that requires changing the lower limit of all levels.
-  EXPECT_TRUE(
-      name_tree.AddValueAndName(pdfium::MakeRetain<CPDF_Number>(-5), L"0.txt"));
-  ASSERT_TRUE(name_tree.LookupValue(L"0.txt"));
-  EXPECT_EQ(-5, name_tree.LookupValue(L"0.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->AddValueAndName(pdfium::MakeRetain<CPDF_Number>(-5),
+                                         L"0.txt"));
+  ASSERT_TRUE(name_tree->LookupValue(L"0.txt"));
+  EXPECT_EQ(-5, name_tree->LookupValue(L"0.txt")->GetInteger());
 
   // Check that the node on the first level has the expected limits.
   CPDF_Dictionary* pKid1 =
-      name_tree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
+      name_tree->GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
   CheckLimitsArray(pKid1, "0.txt", "99.txt");
 
@@ -246,11 +250,12 @@
   // Set up a name tree with five nodes of three levels.
   auto pRootDict = pdfium::MakeRetain<CPDF_Dictionary>();
   FillNameTreeDict(pRootDict.Get());
-  CPDF_NameTree name_tree(pRootDict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(pRootDict.Get());
 
   // Retrieve the kid dictionaries.
   CPDF_Dictionary* pKid1 =
-      name_tree.GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
+      name_tree->GetRootForTest()->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid1);
   CPDF_Dictionary* pKid2 = pKid1->GetArrayFor("Kids")->GetDictAt(0);
   ASSERT_TRUE(pKid2);
@@ -262,28 +267,28 @@
   ASSERT_TRUE(pKid5);
 
   // Check that deleting an out-of-bound index would fail.
-  EXPECT_FALSE(name_tree.DeleteValueAndName(5));
+  EXPECT_FALSE(name_tree->DeleteValueAndName(5));
 
   // Delete the name "9.txt", and check that its node gets deleted and its
   // parent node's limits get updated.
   WideString csName;
-  ASSERT_TRUE(name_tree.LookupValue(L"9.txt"));
-  EXPECT_EQ(999, name_tree.LookupValue(L"9.txt")->GetInteger());
-  EXPECT_TRUE(name_tree.LookupValueAndName(4, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"9.txt"));
+  EXPECT_EQ(999, name_tree->LookupValue(L"9.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(4, &csName));
   EXPECT_STREQ(L"9.txt", csName.c_str());
   EXPECT_EQ(2u, pKid1->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(name_tree.DeleteValueAndName(4));
+  EXPECT_TRUE(name_tree->DeleteValueAndName(4));
   EXPECT_EQ(1u, pKid1->GetArrayFor("Kids")->size());
   CheckLimitsArray(pKid1, "1.txt", "5.txt");
 
   // Delete the name "2.txt", and check that its node does not get deleted, its
   // node's limits get updated, and no other limits get updated.
-  ASSERT_TRUE(name_tree.LookupValue(L"2.txt"));
-  EXPECT_EQ(222, name_tree.LookupValue(L"2.txt")->GetInteger());
-  EXPECT_TRUE(name_tree.LookupValueAndName(1, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"2.txt"));
+  EXPECT_EQ(222, name_tree->LookupValue(L"2.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(1, &csName));
   EXPECT_STREQ(L"2.txt", csName.c_str());
   EXPECT_EQ(4u, pKid4->GetArrayFor("Names")->size());
-  EXPECT_TRUE(name_tree.DeleteValueAndName(1));
+  EXPECT_TRUE(name_tree->DeleteValueAndName(1));
   EXPECT_EQ(2u, pKid4->GetArrayFor("Names")->size());
   CheckLimitsArray(pKid4, "1.txt", "1.txt");
   CheckLimitsArray(pKid2, "1.txt", "5.txt");
@@ -291,24 +296,24 @@
 
   // Delete the name "1.txt", and check that its node gets deleted, and its
   // parent's and gradparent's limits get updated.
-  ASSERT_TRUE(name_tree.LookupValue(L"1.txt"));
-  EXPECT_EQ(111, name_tree.LookupValue(L"1.txt")->GetInteger());
-  EXPECT_TRUE(name_tree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"1.txt"));
+  EXPECT_EQ(111, name_tree->LookupValue(L"1.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"1.txt", csName.c_str());
   EXPECT_EQ(2u, pKid2->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(name_tree.DeleteValueAndName(0));
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
   EXPECT_EQ(1u, pKid2->GetArrayFor("Kids")->size());
   CheckLimitsArray(pKid2, "3.txt", "5.txt");
   CheckLimitsArray(pKid1, "3.txt", "5.txt");
 
   // Delete the name "3.txt", and check that its node does not get deleted, and
   // its node's, its parent's, and its grandparent's limits get updated.
-  ASSERT_TRUE(name_tree.LookupValue(L"3.txt"));
-  EXPECT_EQ(333, name_tree.LookupValue(L"3.txt")->GetInteger());
-  EXPECT_TRUE(name_tree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"3.txt"));
+  EXPECT_EQ(333, name_tree->LookupValue(L"3.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"3.txt", csName.c_str());
   EXPECT_EQ(4u, pKid5->GetArrayFor("Names")->size());
-  EXPECT_TRUE(name_tree.DeleteValueAndName(0));
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
   EXPECT_EQ(2u, pKid5->GetArrayFor("Names")->size());
   CheckLimitsArray(pKid5, "5.txt", "5.txt");
   CheckLimitsArray(pKid2, "5.txt", "5.txt");
@@ -316,16 +321,16 @@
 
   // Delete the name "5.txt", and check that all nodes in the tree get deleted
   // since they are now all empty.
-  ASSERT_TRUE(name_tree.LookupValue(L"5.txt"));
-  EXPECT_EQ(555, name_tree.LookupValue(L"5.txt")->GetInteger());
-  EXPECT_TRUE(name_tree.LookupValueAndName(0, &csName));
+  ASSERT_TRUE(name_tree->LookupValue(L"5.txt"));
+  EXPECT_EQ(555, name_tree->LookupValue(L"5.txt")->GetInteger());
+  EXPECT_TRUE(name_tree->LookupValueAndName(0, &csName));
   EXPECT_STREQ(L"5.txt", csName.c_str());
-  EXPECT_EQ(1u, name_tree.GetRootForTest()->GetArrayFor("Kids")->size());
-  EXPECT_TRUE(name_tree.DeleteValueAndName(0));
-  EXPECT_EQ(0u, name_tree.GetRootForTest()->GetArrayFor("Kids")->size());
+  EXPECT_EQ(1u, name_tree->GetRootForTest()->GetArrayFor("Kids")->size());
+  EXPECT_TRUE(name_tree->DeleteValueAndName(0));
+  EXPECT_EQ(0u, name_tree->GetRootForTest()->GetArrayFor("Kids")->size());
 
   // Check that the tree is now empty.
-  EXPECT_EQ(0u, name_tree.GetCount());
-  EXPECT_FALSE(name_tree.LookupValueAndName(0, &csName));
-  EXPECT_FALSE(name_tree.DeleteValueAndName(0));
+  EXPECT_EQ(0u, name_tree->GetCount());
+  EXPECT_FALSE(name_tree->LookupValueAndName(0, &csName));
+  EXPECT_FALSE(name_tree->DeleteValueAndName(0));
 }
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
index 9a37024..d810051 100644
--- a/testing/fuzzers/pdf_nametree_fuzzer.cc
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -54,22 +54,23 @@
 
   CPDF_StreamParser parser(remaining);
   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
-  CPDF_NameTree name_tree(dict.Get());
+  std::unique_ptr<CPDF_NameTree> name_tree =
+      CPDF_NameTree::CreateForTesting(dict.Get());
   for (const auto& name : params.names) {
     RetainPtr<CPDF_Object> obj = parser.ReadNextObject(
         /*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0);
     if (!obj)
       break;
 
-    name_tree.AddValueAndName(std::move(obj), name);
+    name_tree->AddValueAndName(std::move(obj), name);
   }
 
   if (params.delete_backwards) {
     for (size_t i = params.count; i > 0; --i)
-      name_tree.DeleteValueAndName(i);
+      name_tree->DeleteValueAndName(i);
   } else {
     for (size_t i = 0; i < params.count; ++i)
-      name_tree.DeleteValueAndName(0);
+      name_tree->DeleteValueAndName(0);
   }
   return 0;
 }
diff --git a/xfa/fxfa/cxfa_ffdoc.cpp b/xfa/fxfa/cxfa_ffdoc.cpp
index ac4b35f..f885644 100644
--- a/xfa/fxfa/cxfa_ffdoc.cpp
+++ b/xfa/fxfa/cxfa_ffdoc.cpp
@@ -179,22 +179,14 @@
     return it->second.pDibSource.As<CFX_DIBitmap>();
   }
 
-  CPDF_Dictionary* pRoot = m_pPDFDoc->GetRoot();
-  if (!pRoot)
+  CPDF_NameTree nametree(m_pPDFDoc.Get(), "XFAImages");
+  size_t count = nametree.GetCount();
+  if (count == 0)
     return nullptr;
 
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames)
-    return nullptr;
-
-  CPDF_Dictionary* pXFAImages = pNames->GetDictFor("XFAImages");
-  if (!pXFAImages)
-    return nullptr;
-
-  CPDF_NameTree nametree(pXFAImages);
   CPDF_Object* pObject = nametree.LookupValue(WideString(wsName));
   if (!pObject) {
-    for (size_t i = 0; i < nametree.GetCount(); i++) {
+    for (size_t i = 0; i < count; ++i) {
       WideString wsTemp;
       CPDF_Object* pTempObject = nametree.LookupValueAndName(i, &wsTemp);
       if (wsTemp == wsName) {