Implemented Goto Action Handling for Internal links via key press.

Support for opening an internal link exists using mouse. But after
setting focus over internal link by pressing Tab key, we do not have
the provision to open the internal link using enter key press.
This implementation uses FFI_DoGoToAction API to handle opening an
internal link.

Bug: chromium:994500
Change-Id: I94fde897bc22c283d9304bffd093ee810ba128e8
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/68931
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Badhri Ravikumar <bravi@microsoft.com>
diff --git a/fpdfsdk/cpdfsdk_actionhandler.cpp b/fpdfsdk/cpdfsdk_actionhandler.cpp
index 5e3e83b..356cc14 100644
--- a/fpdfsdk/cpdfsdk_actionhandler.cpp
+++ b/fpdfsdk/cpdfsdk_actionhandler.cpp
@@ -65,13 +65,41 @@
     CPDFSDK_FormFillEnvironment* form_fill_env,
     int modifiers) {
   ASSERT(form_fill_env);
-  if (action.GetType() != CPDF_Action::URI)
-    return false;
 
   if (!CPDF_AAction::IsUserInput(type))
     return false;
 
-  DoAction_URI(form_fill_env, action, modifiers);
+  switch (action.GetType()) {
+    case CPDF_Action::GoTo:
+      DoAction_GoTo(form_fill_env, action);
+      return true;
+    case CPDF_Action::URI:
+      DoAction_URI(form_fill_env, action, modifiers);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool CPDFSDK_ActionHandler::DoAction_Destination(
+    const CPDF_Dest& dest,
+    CPDFSDK_FormFillEnvironment* form_fill_env) {
+  ASSERT(form_fill_env);
+  CPDF_Document* document = form_fill_env->GetPDFDocument();
+  ASSERT(document);
+
+  const CPDF_Array* dest_array = dest.GetArray();
+  std::vector<float> dest_positions;
+  // |dest_array| index 0 contains destination page details and index 1 contains
+  // parameter that explains about the rest of |dest_array|.
+  if (dest_array) {
+    for (size_t i = 2; i < dest_array->size(); i++)
+      dest_positions.push_back(dest_array->GetNumberAt(i));
+  }
+
+  form_fill_env->DoGoToAction(dest.GetDestPageIndex(document),
+                              dest.GetZoomMode(), dest_positions.data(),
+                              dest_positions.size());
   return true;
 }
 
@@ -271,16 +299,7 @@
   ASSERT(pPDFDocument);
 
   CPDF_Dest MyDest = action.GetDest(pPDFDocument);
-  int nPageIndex = MyDest.GetDestPageIndex(pPDFDocument);
-  int nFitType = MyDest.GetZoomMode();
-  const CPDF_Array* pMyArray = MyDest.GetArray();
-  std::vector<float> posArray;
-  if (pMyArray) {
-    for (size_t i = 2; i < pMyArray->size(); i++)
-      posArray.push_back(pMyArray->GetNumberAt(i));
-  }
-  pFormFillEnv->DoGoToAction(nPageIndex, nFitType, posArray.data(),
-                             posArray.size());
+  DoAction_Destination(MyDest, pFormFillEnv);
 }
 
 void CPDFSDK_ActionHandler::DoAction_URI(
diff --git a/fpdfsdk/cpdfsdk_actionhandler.h b/fpdfsdk/cpdfsdk_actionhandler.h
index 7880eba..c4a8bbe 100644
--- a/fpdfsdk/cpdfsdk_actionhandler.h
+++ b/fpdfsdk/cpdfsdk_actionhandler.h
@@ -48,6 +48,8 @@
                      CPDF_AAction::AActionType type,
                      CPDFSDK_FormFillEnvironment* form_fill_env,
                      int modifiers);
+  bool DoAction_Destination(const CPDF_Dest& dest,
+                            CPDFSDK_FormFillEnvironment* form_fill_env);
 
  private:
   using RunScriptCallback = std::function<void(IJS_EventContext* context)>;
diff --git a/fpdfsdk/cpdfsdk_baannot.cpp b/fpdfsdk/cpdfsdk_baannot.cpp
index 968e8b5..43b3a7b 100644
--- a/fpdfsdk/cpdfsdk_baannot.cpp
+++ b/fpdfsdk/cpdfsdk_baannot.cpp
@@ -229,3 +229,13 @@
 
   return CPDFSDK_Annot::GetLayoutOrder();
 }
+
+CPDF_Dest CPDFSDK_BAAnnot::GetDestination() const {
+  if (m_pAnnot->GetSubtype() != CPDF_Annot::Subtype::LINK)
+    return CPDF_Dest();
+
+  // Link annotations can have "Dest" entry defined as an explicit array.
+  // https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf#page=373
+  return CPDF_Dest::Create(m_pPageView->GetPDFDocument(),
+                           GetAnnotDict()->GetDirectObjectFor("Dest"));
+}
\ No newline at end of file
diff --git a/fpdfsdk/cpdfsdk_baannot.h b/fpdfsdk/cpdfsdk_baannot.h
index 482aa0a..f119b28 100644
--- a/fpdfsdk/cpdfsdk_baannot.h
+++ b/fpdfsdk/cpdfsdk_baannot.h
@@ -66,6 +66,8 @@
 
   void SetOpenState(bool bOpenState);
 
+  CPDF_Dest GetDestination() const;
+
  protected:
   CPDF_Dictionary* GetAPDict() const;
 
diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp
index ef90835..53d6107 100644
--- a/fpdfsdk/cpdfsdk_baannothandler.cpp
+++ b/fpdfsdk/cpdfsdk_baannothandler.cpp
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfdoc/cpdf_action.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
 #include "core/fxge/cfx_drawutils.h"
 #include "fpdfsdk/cpdfsdk_actionhandler.h"
@@ -187,12 +188,14 @@
 
   CPDFSDK_BAAnnot* ba_annot = pAnnot->AsBAAnnot();
   CPDF_Action action = ba_annot->GetAAction(CPDF_AAction::kKeyStroke);
-  if (!action.GetDict() || action.GetType() != CPDF_Action::URI) {
-    return false;
+
+  if (action.GetDict()) {
+    return form_fill_environment_->GetActionHandler()->DoAction_Link(
+        action, CPDF_AAction::kKeyStroke, form_fill_environment_.Get(), nFlag);
   }
 
-  return form_fill_environment_->GetActionHandler()->DoAction_Link(
-      action, CPDF_AAction::kKeyStroke, form_fill_environment_.Get(), nFlag);
+  return form_fill_environment_->GetActionHandler()->DoAction_Destination(
+      ba_annot->GetDestination(), form_fill_environment_.Get());
 }
 
 bool CPDFSDK_BAAnnotHandler::OnKeyUp(CPDFSDK_Annot* pAnnot,
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index 835ded1..03d9c22 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -611,6 +611,7 @@
   EXPECT_CALL(mock, OnFocusChange(_, _, _)).Times(0);
   EXPECT_CALL(mock, DoURIAction(_)).Times(0);
   EXPECT_CALL(mock, DoURIActionWithKeyboardModifier(_, _, _)).Times(0);
+  EXPECT_CALL(mock, DoGoToAction(_, _, _, _, _)).Times(0);
   SetDelegate(&mock);
 
   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
@@ -3076,6 +3077,52 @@
       FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
 }
 
+TEST_F(FPDFFormFillActionUriTest, InternalLinkActionInvokeTest) {
+  NiceMock<EmbedderTestMockDelegate> mock;
+  EXPECT_CALL(mock, DoGoToAction(_, _, 1, _, _)).Times(12);
+  SetDelegate(&mock);
+
+  SetFocusOnNthAnnot(4);
+  int modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  SetFocusOnNthAnnot(5);
+  modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  SetFocusOnNthAnnot(6);
+  modifier = 0;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier = FWL_EVENTFLAG_ShiftKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+  modifier |= FWL_EVENTFLAG_ControlKey;
+  ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Return, modifier));
+
+  ASSERT_FALSE(FORM_OnKeyDown(nullptr, page(), FWL_VKEY_Return, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), nullptr, FWL_VKEY_Return, modifier));
+  // Following checks should be changed to ASSERT_TRUE if FORM_OnKeyDown starts
+  // handling for Shift/Space/Control.
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Shift, modifier));
+  ASSERT_FALSE(FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Space, modifier));
+  ASSERT_FALSE(
+      FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
+}
+
 class FPDFFormFillActionUriTestVersion2 : public FPDFFormFillActionUriTest {
   void SetUp() override {
     SetFormFillInfoVersion(2);
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index 69842a4..a85f5c6 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -251,6 +251,7 @@
   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
   formfillinfo->FFI_GetPage = GetPageTrampoline;
   formfillinfo->FFI_DoURIAction = DoURIActionTrampoline;
+  formfillinfo->FFI_DoGoToAction = DoGoToActionTrampoline;
   formfillinfo->FFI_OnFocusChange = OnFocusChangeTrampoline;
   formfillinfo->FFI_DoURIActionWithKeyboardModifier =
       DoURIActionWithKeyboardModifierTrampoline;
@@ -607,6 +608,17 @@
 }
 
 // static
+void EmbedderTest::DoGoToActionTrampoline(FPDF_FORMFILLINFO* info,
+                                          int page_index,
+                                          int zoom_mode,
+                                          float* pos_array,
+                                          int array_size) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  return test->delegate_->DoGoToAction(info, page_index, zoom_mode, pos_array,
+                                       array_size);
+}
+
+// static
 void EmbedderTest::OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info,
                                            FPDF_ANNOTATION annot,
                                            int page_index) {
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index e9e69fb..cdb4268 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -65,6 +65,13 @@
     // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction().
     virtual void DoURIAction(FPDF_BYTESTRING uri) {}
 
+    // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction().
+    virtual void DoGoToAction(FPDF_FORMFILLINFO* info,
+                              int page_index,
+                              int zoom_mode,
+                              float* pos_arry,
+                              int array_size) {}
+
     // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange().
     virtual void OnFocusChange(FPDF_FORMFILLINFO* info,
                                FPDF_ANNOTATION annot,
@@ -304,6 +311,11 @@
                                      int page_index);
   static void DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
                                     FPDF_BYTESTRING uri);
+  static void DoGoToActionTrampoline(FPDF_FORMFILLINFO* info,
+                                     int page_index,
+                                     int zoom_mode,
+                                     float* pos_array,
+                                     int array_size);
   static void OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info,
                                       FPDF_ANNOTATION annot,
                                       int page_index);
diff --git a/testing/embedder_test_mock_delegate.h b/testing/embedder_test_mock_delegate.h
index f2cbb84..0efcd52 100644
--- a/testing/embedder_test_mock_delegate.h
+++ b/testing/embedder_test_mock_delegate.h
@@ -17,6 +17,12 @@
   MOCK_METHOD2(SetTimer, int(int msecs, TimerCallback fn));
   MOCK_METHOD1(KillTimer, void(int msecs));
   MOCK_METHOD1(DoURIAction, void(FPDF_BYTESTRING uri));
+  MOCK_METHOD5(DoGoToAction,
+               void(FPDF_FORMFILLINFO* info,
+                    int page_index,
+                    int zoom_mode,
+                    float* pos_array,
+                    int array_size));
   MOCK_METHOD3(OnFocusChange,
                void(FPDF_FORMFILLINFO* info,
                     FPDF_ANNOTATION annot,
diff --git a/testing/resources/annots_action_handling.in b/testing/resources/annots_action_handling.in
index be46954..e5c728b 100644
--- a/testing/resources/annots_action_handling.in
+++ b/testing/resources/annots_action_handling.in
@@ -2,24 +2,31 @@
 {{object 1 0}} <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm [5 0 R 6 0 R]
+  /Names 12 0 R
+  /AcroForm [6 0 R 7 0 R]
 >>
 endobj
 {{object 2 0}} <<
   /Type /Pages
-  /Count 1
-  /Kids [3 0 R]
+  /Count 2
+  /Kids [3 0 R 4 0 R]
 >>
 endobj
 {{object 3 0}} <<
   /Type /Page
   /Parent 2 0 R
-  /Annots [5 0 R 6 0 R 7 0 R]
-  /Contents 4 0 R
+  /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+  /Contents 5 0 R
   /Tabs /R
 >>
 endobj
 {{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Tabs /R
+>>
+endobj
+{{object 5 0}} <<
   {{streamlen}}
 >>
 stream
@@ -27,10 +34,19 @@
 70 340 Td
 14 Tf
 (External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
 ET
 endstream
 endobj
-{{object 5 0}} <<
+{{object 6 0}} <<
   /Type /Annot
   /Subtype /Widget
   /FT /Tx
@@ -39,7 +55,7 @@
   /Rect [69 670 220 690]
 >>
 endobj
-{{object 6 0}} <<
+{{object 7 0}} <<
   /Type /Annot
   /Subtype /Widget
   /FT /Btn
@@ -53,7 +69,7 @@
   /Ff 65536
 >>
 endobj
-{{object 7 0}} <<
+{{object 8 0}} <<
   /Type /Annot
   /Subtype /Link
   /Rect [69 338 180 358]
@@ -65,6 +81,51 @@
   /F 4
 >>
 endobj
+{{object 9 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 305 180 325]
+  /BS <<
+    /W 0
+  >>
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 270 180 290]
+  /BS <<
+    /W 0
+  >>
+  /Dest /top
+  /F 4
+>>
+{{object 11 0}} <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 235 180 255]
+  /BS <<
+    /W 0
+  >>
+  /Dest (target10)
+  /F 4
+>>
+{{object 12 0}} <<
+  /Dests 13 0 R
+>>
+endobj
+{{object 13 0}} <<
+  /Names [
+    (target10) 14 0 R
+    /top 14 0 R
+  ]
+>>
+endobj
+{{object 14 0}} <<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/annots_action_handling.pdf b/testing/resources/annots_action_handling.pdf
index 92dc830..b8b6c2a 100644
--- a/testing/resources/annots_action_handling.pdf
+++ b/testing/resources/annots_action_handling.pdf
@@ -3,35 +3,51 @@
 1 0 obj <<
   /Type /Catalog
   /Pages 2 0 R
-  /AcroForm [5 0 R 6 0 R]
+  /Names 12 0 R
+  /AcroForm [6 0 R 7 0 R]
 >>
 endobj
 2 0 obj <<
   /Type /Pages
-  /Count 1
-  /Kids [3 0 R]
+  /Count 2
+  /Kids [3 0 R 4 0 R]
 >>
 endobj
 3 0 obj <<
   /Type /Page
   /Parent 2 0 R
-  /Annots [5 0 R 6 0 R 7 0 R]
-  /Contents 4 0 R
+  /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+  /Contents 5 0 R
   /Tabs /R
 >>
 endobj
 4 0 obj <<
-  /Length 42
+  /Type /Page
+  /Parent 2 0 R
+  /Tabs /R
+>>
+endobj
+5 0 obj <<
+  /Length 145
 >>
 stream
 BT
 70 340 Td
 14 Tf
 (External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
 ET
 endstream
 endobj
-5 0 obj <<
+6 0 obj <<
   /Type /Annot
   /Subtype /Widget
   /FT /Tx
@@ -40,7 +56,7 @@
   /Rect [69 670 220 690]
 >>
 endobj
-6 0 obj <<
+7 0 obj <<
   /Type /Annot
   /Subtype /Widget
   /FT /Btn
@@ -54,7 +70,7 @@
   /Ff 65536
 >>
 endobj
-7 0 obj <<
+8 0 obj <<
   /Type /Annot
   /Subtype /Link
   /Rect [69 338 180 358]
@@ -66,20 +82,72 @@
   /F 4
 >>
 endobj
+9 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 305 180 325]
+  /BS <<
+    /W 0
+  >>
+  /Dest [4 0 R /XYZ 200 725 0]
+  /F 4
+>>
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 270 180 290]
+  /BS <<
+    /W 0
+  >>
+  /Dest /top
+  /F 4
+>>
+11 0 obj <<
+  /Type /Annot
+  /Subtype /Link
+  /Rect [69 235 180 255]
+  /BS <<
+    /W 0
+  >>
+  /Dest (target10)
+  /F 4
+>>
+12 0 obj <<
+  /Dests 13 0 R
+>>
+endobj
+13 0 obj <<
+  /Names [
+    (target10) 14 0 R
+    /top 14 0 R
+  ]
+>>
+endobj
+14 0 obj <<
+  /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
 xref
-0 8
+0 15
 0000000000 65535 f 
 0000000015 00000 n 
-0000000094 00000 n 
-0000000157 00000 n 
-0000000267 00000 n 
-0000000360 00000 n 
-0000000483 00000 n 
-0000000666 00000 n 
+0000000110 00000 n 
+0000000179 00000 n 
+0000000309 00000 n 
+0000000371 00000 n 
+0000000568 00000 n 
+0000000691 00000 n 
+0000000874 00000 n 
+0000001038 00000 n 
+0000001170 00000 n 
+0000001285 00000 n 
+0000001406 00000 n 
+0000001444 00000 n 
+0000001519 00000 n 
 trailer <<
   /Root 1 0 R
-  /Size 8
+  /Size 15
 >>
 startxref
-830
+1569
 %%EOF
\ No newline at end of file