Move name tree internal structure creation code into CPDF_NameTree.

The code to do so is non-trivial, so better to centralize it in
CPDF_NameTree, instead of relying on CPDF_NameTree users will get it
right.

Change-Id: I3704ef8ea30a21079a7de0c285bfcd8253cd496e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/68194
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 549fccb..a0fc935 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -12,6 +12,7 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
@@ -317,6 +318,31 @@
 CPDF_NameTree::~CPDF_NameTree() = default;
 
 // static
+std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateWithRootNameArray(
+    CPDF_Document* pDoc,
+    const ByteString& category) {
+  CPDF_Dictionary* pRoot = pDoc->GetRoot();
+  if (!pRoot)
+    return nullptr;
+
+  // Retrieve the document's Names dictionary; create it if missing.
+  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
+  if (!pNames) {
+    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
+    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
+  }
+
+  // Create the |category| dictionary if missing.
+  if (!pNames->GetDictFor(category)) {
+    CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
+    pFiles->SetNewFor<CPDF_Array>("Names");
+    pNames->SetNewFor<CPDF_Reference>(category, pDoc, pFiles->GetObjNum());
+  }
+
+  return pdfium::MakeUnique<CPDF_NameTree>(pDoc, category);
+}
+
+// static
 std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateForTesting(
     CPDF_Dictionary* pRoot) {
   return pdfium::WrapUnique(new CPDF_NameTree(pRoot));  // Private ctor.
diff --git a/core/fpdfdoc/cpdf_nametree.h b/core/fpdfdoc/cpdf_nametree.h
index 01d85aa..043a2dc 100644
--- a/core/fpdfdoc/cpdf_nametree.h
+++ b/core/fpdfdoc/cpdf_nametree.h
@@ -24,6 +24,13 @@
   CPDF_NameTree& operator=(const CPDF_NameTree&) = delete;
   ~CPDF_NameTree();
 
+  // If necessary, create missing Names dictionary in |pDoc|, and/or missing
+  // Names array in the dictionary that corresponds to |category|, if necessary.
+  // Returns nullptr on failure.
+  static std::unique_ptr<CPDF_NameTree> CreateWithRootNameArray(
+      CPDF_Document* pDoc,
+      const ByteString& category);
+
   static std::unique_ptr<CPDF_NameTree> CreateForTesting(
       CPDF_Dictionary* pRoot);
 
diff --git a/fpdfsdk/fpdf_attachment.cpp b/fpdfsdk/fpdf_attachment.cpp
index 18c12df..a8d4eeb 100644
--- a/fpdfsdk/fpdf_attachment.cpp
+++ b/fpdfsdk/fpdf_attachment.cpp
@@ -64,28 +64,14 @@
   if (!pDoc)
     return nullptr;
 
-  CPDF_Dictionary* pRoot = pDoc->GetRoot();
-  if (!pRoot)
-    return nullptr;
-
   WideString wsName = WideStringFromFPDFWideString(name);
   if (wsName.IsEmpty())
     return nullptr;
 
-  // Retrieve the document's Names dictionary; create it if missing.
-  CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
-  if (!pNames) {
-    pNames = pDoc->NewIndirect<CPDF_Dictionary>();
-    pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
-  }
-
-  // Create the EmbeddedFiles dictionary if missing.
-  if (!pNames->GetDictFor("EmbeddedFiles")) {
-    CPDF_Dictionary* pFiles = pDoc->NewIndirect<CPDF_Dictionary>();
-    pFiles->SetNewFor<CPDF_Array>("Names");
-    pNames->SetNewFor<CPDF_Reference>("EmbeddedFiles", pDoc,
-                                      pFiles->GetObjNum());
-  }
+  auto name_tree =
+      CPDF_NameTree::CreateWithRootNameArray(pDoc, "EmbeddedFiles");
+  if (!name_tree)
+    return nullptr;
 
   // Set up the basic entries in the filespec dictionary.
   CPDF_Dictionary* pFile = pDoc->NewIndirect<CPDF_Dictionary>();
@@ -94,10 +80,8 @@
   pFile->SetNewFor<CPDF_String>(pdfium::stream::kF, wsName);
 
   // Add the new attachment name and filespec into the document's EmbeddedFiles.
-  CPDF_NameTree name_tree(pDoc, "EmbeddedFiles");
-  if (!name_tree.AddValueAndName(pFile->MakeReference(pDoc), wsName)) {
+  if (!name_tree->AddValueAndName(pFile->MakeReference(pDoc), wsName))
     return nullptr;
-  }
 
   return FPDFAttachmentFromCPDFObject(pFile);
 }