Add an experimental API to surface a page's additional action.

Add an API to surface a page's additional action (/AA) to the embedder.
FORM_GetPageAAction() returns either a page open action or a page close action, depending on the input type.

Change-Id: I435d04ddd6bd0755d651a3cbb6e1abd93b81d356
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/72590
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp
index b6a5ed4..94a1d04 100644
--- a/fpdfsdk/fpdf_doc.cpp
+++ b/fpdfsdk/fpdf_doc.cpp
@@ -10,6 +10,7 @@
 #include <set>
 #include <utility>
 
+#include "constants/form_fields.h"
 #include "core/fpdfapi/page/cpdf_annotcontext.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -18,12 +19,14 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
+#include "core/fpdfdoc/cpdf_aaction.h"
 #include "core/fpdfdoc/cpdf_bookmark.h"
 #include "core/fpdfdoc/cpdf_bookmarktree.h"
 #include "core/fpdfdoc/cpdf_dest.h"
 #include "core/fpdfdoc/cpdf_linklist.h"
 #include "core/fpdfdoc/cpdf_pagelabel.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "public/fpdf_formfill.h"
 #include "third_party/base/stl_util.h"
 
 namespace {
@@ -412,6 +415,30 @@
                               quad_points);
 }
 
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
+                                                          int aa_type) {
+  CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page);
+  if (!pdf_page)
+    return nullptr;
+
+  CPDF_Dictionary* page_dict = pdf_page->GetDict();
+  CPDF_AAction aa(page_dict->GetDictFor(pdfium::form_fields::kAA));
+
+  CPDF_AAction::AActionType type;
+  if (aa_type == FPDFPAGE_AACTION_OPEN)
+    type = CPDF_AAction::kOpenPage;
+  else if (aa_type == FPDFPAGE_AACTION_CLOSE)
+    type = CPDF_AAction::kClosePage;
+  else
+    return nullptr;
+
+  if (!aa.ActionExist(type))
+    return nullptr;
+
+  CPDF_Action action = aa.GetAction(type);
+  return FPDFActionFromCPDFDictionary(action.GetDict());
+}
+
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDF_GetFileIdentifier(FPDF_DOCUMENT document,
                        FPDF_FILEIDTYPE id_type,
diff --git a/fpdfsdk/fpdf_doc_embeddertest.cpp b/fpdfsdk/fpdf_doc_embeddertest.cpp
index 24f57a9..87033f1 100644
--- a/fpdfsdk/fpdf_doc_embeddertest.cpp
+++ b/fpdfsdk/fpdf_doc_embeddertest.cpp
@@ -672,6 +672,38 @@
   FPDF_CloseDocument(empty_doc);
 }
 
+TEST_F(FPDFDocEmbedderTest, GetPageAAction) {
+  ASSERT_TRUE(OpenDocument("get_page_aaction.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  EXPECT_TRUE(page);
+
+  EXPECT_EQ(nullptr, FPDF_GetPageAAction(nullptr, FPDFPAGE_AACTION_OPEN));
+  EXPECT_EQ(nullptr, FPDF_GetPageAAction(page, FPDFPAGE_AACTION_CLOSE));
+  EXPECT_EQ(nullptr, FPDF_GetPageAAction(page, -1));
+  EXPECT_EQ(nullptr, FPDF_GetPageAAction(page, 999));
+
+  FPDF_ACTION action = FPDF_GetPageAAction(page, FPDFPAGE_AACTION_OPEN);
+  EXPECT_EQ(static_cast<unsigned long>(PDFACTION_EMBEDDEDGOTO),
+            FPDFAction_GetType(action));
+
+  const char kExpectedResult[] = "\\\\127.0.0.1\\c$\\Program Files\\test.exe";
+  const unsigned long kExpectedLength = sizeof(kExpectedResult);
+  char buf[1024];
+
+  unsigned long bufsize = FPDFAction_GetFilePath(action, nullptr, 0);
+  EXPECT_EQ(kExpectedLength, bufsize);
+  EXPECT_EQ(kExpectedLength, FPDFAction_GetFilePath(action, buf, bufsize));
+  EXPECT_STREQ(kExpectedResult, buf);
+
+  UnloadPage(page);
+
+  page = LoadPage(1);
+  EXPECT_TRUE(page);
+  EXPECT_EQ(nullptr, FPDF_GetPageAAction(page, -1));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFDocEmbedderTest, NoPageLabels) {
   ASSERT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_EQ(1, FPDF_GetPageCount(document()));
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 75d2367..e36ece4 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -144,6 +144,7 @@
     CHK(FPDFLink_GetQuadPoints);
     CHK(FPDF_GetFileIdentifier);
     CHK(FPDF_GetMetaText);
+    CHK(FPDF_GetPageAAction);
     CHK(FPDF_GetPageLabel);
 
     // fpdf_edit.h
diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h
index 6355ec6..3a17c01 100644
--- a/public/fpdf_doc.h
+++ b/public/fpdf_doc.h
@@ -332,6 +332,18 @@
                        int quad_index,
                        FS_QUADPOINTSF* quad_points);
 
+// Experimental API
+// Gets an additional-action from |page|.
+//
+//   page      - handle to the page, as returned by FPDF_LoadPage().
+//   aa_type   - the type of the page object's addtional-action, defined
+//               in public/fpdf_formfill.h
+//
+//   Returns the handle to the action data, or NULL if there is no
+//   additional-action of type |aa_type|.
+FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
+                                                          int aa_type);
+
 // Experimental API.
 // Get the file identifer defined in the trailer of |document|.
 //
diff --git a/testing/resources/get_page_aaction.in b/testing/resources/get_page_aaction.in
new file mode 100644
index 0000000..83f0c01
--- /dev/null
+++ b/testing/resources/get_page_aaction.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+  /Count 2
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+    /C <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/get_page_aaction.pdf b/testing/resources/get_page_aaction.pdf
new file mode 100644
index 0000000..7d46651
--- /dev/null
+++ b/testing/resources/get_page_aaction.pdf
@@ -0,0 +1,61 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+  /Count 2
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /AA <<
+    /O <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+    /C <<
+      /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+      /D [1 /Fit]
+      /S /GoToE
+    >>
+  >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000149 00000 n 
+0000000345 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+675
+%%EOF