Merge and extend FocusableAnnotSubtypes tests.

Instead of testing the 3 related APIs separately, test them at the same
time as they can naturally be used together.

Also add some FORM_SetFocusedAnnot() calls to see the effect of calling
FPDFAnnot_SetFocusableSubtypes(). This also reveals an issue with
FORM_SetFocusedAnnot() when it comes to focusing popups and widgets.

Bug: pdfium:1507
Change-Id: Ic8ee8bb897b0f92f02c6c0b55f286f735ee84cd3
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/68530
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 8067026..029718f 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -22,6 +22,55 @@
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/hash.h"
+#include "third_party/base/span.h"
+
+namespace {
+
+void VerifyFocusableAnnotSubtypes(
+    FPDF_FORMHANDLE form_handle,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes) {
+  ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
+            FPDFAnnot_GetFocusableSubtypesCount(form_handle));
+
+  std::vector<FPDF_ANNOTATION_SUBTYPE> actual_subtypes(
+      expected_subtypes.size());
+  ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(
+      form_handle, actual_subtypes.data(), actual_subtypes.size()));
+  for (size_t i = 0; i < expected_subtypes.size(); ++i)
+    ASSERT_EQ(expected_subtypes[i], actual_subtypes[i]);
+}
+
+void SetAndVerifyFocusableAnnotSubtypes(
+    FPDF_FORMHANDLE form_handle,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> subtypes) {
+  ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle, subtypes.data(),
+                                             subtypes.size()));
+  VerifyFocusableAnnotSubtypes(form_handle, subtypes);
+}
+
+void VerifyAnnotationSubtypesAndFocusability(
+    FPDF_FORMHANDLE form_handle,
+    FPDF_PAGE page,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes,
+    pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_focusable_subtypes) {
+  ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
+            FPDFPage_GetAnnotCount(page));
+  for (size_t i = 0; i < expected_subtypes.size(); ++i) {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
+    ASSERT_TRUE(annot);
+    EXPECT_EQ(expected_subtypes[i], FPDFAnnot_GetSubtype(annot.get()));
+
+    bool expected_focusable = pdfium::ContainsValue(expected_focusable_subtypes,
+                                                    expected_subtypes[i]);
+    EXPECT_EQ(expected_focusable,
+              FORM_SetFocusedAnnot(form_handle, annot.get()));
+
+    // Kill the focus so the next test starts in an unfocused state.
+    FORM_ForceToKillFocus(form_handle);
+  }
+}
+
+}  // namespace
 
 class FPDFAnnotEmbedderTest : public EmbedderTest {};
 
@@ -2383,87 +2432,72 @@
   UnloadPage(page);
 }
 
-TEST_F(FPDFAnnotEmbedderTest, SetFocusableAnnotSubtypes) {
-  ASSERT_TRUE(OpenDocument("text_form.pdf"));
+TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotSubtypes) {
+  ASSERT_TRUE(OpenDocument("annots.pdf"));
   FPDF_PAGE page = LoadPage(0);
   ASSERT_TRUE(page);
 
-  // Annotations of type FPDF_ANNOT_WIDGET are by default focusable.
-  EXPECT_EQ(1, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+  // Verify widgets are by default focusable.
+  const FPDF_ANNOTATION_SUBTYPE kDefaultSubtypes[] = {FPDF_ANNOT_WIDGET};
+  VerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
 
-  EXPECT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, 0));
-  EXPECT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+  // Expected annot subtypes for page 0 of annots.pdf.
+  const FPDF_ANNOTATION_SUBTYPE kExpectedAnnotSubtypes[] = {
+      FPDF_ANNOT_LINK,  FPDF_ANNOT_LINK,      FPDF_ANNOT_LINK,
+      FPDF_ANNOT_LINK,  FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_HIGHLIGHT,
+      FPDF_ANNOT_POPUP, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET,
+  };
 
-  constexpr FPDF_ANNOTATION_SUBTYPE kFocusableSubtypes[] = {FPDF_ANNOT_WIDGET};
-  constexpr size_t kSubtypeCount = FX_ArraySize(kFocusableSubtypes);
+  // TODO(crbug.com/pdfium/1507): Popups should not be focusable.
+  const FPDF_ANNOTATION_SUBTYPE kExpectedDefaultFocusableSubtypes[] = {
+      FPDF_ANNOT_WIDGET, FPDF_ANNOT_POPUP};
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes,
+                                          kExpectedDefaultFocusableSubtypes);
 
-  EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(nullptr, kFocusableSubtypes,
-                                              kSubtypeCount));
-  EXPECT_FALSE(
-      FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, kSubtypeCount));
+  // Make no annotation type focusable using the preferred method.
+  ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, 0));
+  ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
 
-  EXPECT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), kFocusableSubtypes,
-                                             kSubtypeCount));
-  EXPECT_EQ(static_cast<int>(kSubtypeCount),
-            FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+  // Restore the focusable type count back to 1, then set it back to 0 using a
+  // different method.
+  SetAndVerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
+  ASSERT_TRUE(
+      FPDFAnnot_SetFocusableSubtypes(form_handle(), kDefaultSubtypes, 0));
+  ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
 
-  EXPECT_TRUE(
-      FPDFAnnot_SetFocusableSubtypes(form_handle(), kFocusableSubtypes, 0));
-  EXPECT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
+  // TODO(crbug.com/pdfium/1507): Widgets and popups should not be focusable.
+  const FPDF_ANNOTATION_SUBTYPE kExpectedNoFocusableSubtypes[] = {
+      FPDF_ANNOT_WIDGET, FPDF_ANNOT_POPUP};
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes,
+                                          kExpectedNoFocusableSubtypes);
 
-  UnloadPage(page);
-}
+  // Now make links focusable.
+  const FPDF_ANNOTATION_SUBTYPE kLinkSubtypes[] = {FPDF_ANNOT_LINK};
+  SetAndVerifyFocusableAnnotSubtypes(form_handle(), kLinkSubtypes);
 
-TEST_F(FPDFAnnotEmbedderTest, GetFocusableAnnotSubtypesCount) {
-  ASSERT_TRUE(OpenDocument("text_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
+  // TODO(crbug.com/pdfium/1507): Widgets and popups should not be focusable.
+  const FPDF_ANNOTATION_SUBTYPE kExpectedLinkocusableSubtypes[] = {
+      FPDF_ANNOT_WIDGET, FPDF_ANNOT_LINK, FPDF_ANNOT_POPUP};
+  VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
+                                          kExpectedAnnotSubtypes,
+                                          kExpectedLinkocusableSubtypes);
 
+  // Test invalid parameters.
+  EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(nullptr, kDefaultSubtypes,
+                                              FX_ArraySize(kDefaultSubtypes)));
+  EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr,
+                                              FX_ArraySize(kDefaultSubtypes)));
   EXPECT_EQ(-1, FPDFAnnot_GetFocusableSubtypesCount(nullptr));
 
-  // FPDF_ANNOT_WIDGET is default annot subtype which is focusable,
-  // hence 1 is default count.
-  EXPECT_EQ(1, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
-
-  constexpr FPDF_ANNOTATION_SUBTYPE kFocusableSubtypes[] = {FPDF_ANNOT_WIDGET,
-                                                            FPDF_ANNOT_LINK};
-  constexpr size_t kSubtypeCount = FX_ArraySize(kFocusableSubtypes);
-
-  FPDFAnnot_SetFocusableSubtypes(form_handle(), kFocusableSubtypes,
-                                 kSubtypeCount);
-
-  EXPECT_EQ(static_cast<int>(kSubtypeCount),
-            FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
-
-  UnloadPage(page);
-}
-
-TEST_F(FPDFAnnotEmbedderTest, GetFocusableAnnotSubtypes) {
-  ASSERT_TRUE(OpenDocument("text_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
-
-  constexpr FPDF_ANNOTATION_SUBTYPE kFocusableSubtypes[] = {FPDF_ANNOT_WIDGET};
-  constexpr size_t kSubtypeCount = FX_ArraySize(kFocusableSubtypes);
-
-  FPDFAnnot_SetFocusableSubtypes(form_handle(), kFocusableSubtypes,
-                                 kSubtypeCount);
-
-  int count = FPDFAnnot_GetFocusableSubtypesCount(form_handle());
-  ASSERT_EQ(1, count);
-  EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(form_handle(), nullptr, count));
-
-  std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(count, FPDF_ANNOT_UNKNOWN);
-  EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(),
-                                              count - 1));
-  EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(nullptr, subtypes.data(), count));
-
-  EXPECT_TRUE(
-      FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), count));
-
-  for (int i = 0; i < count; ++i) {
-    EXPECT_EQ(kFocusableSubtypes[i], subtypes[i]);
-  }
+  std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(1);
+  EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(nullptr, subtypes.data(),
+                                              subtypes.size()));
+  EXPECT_FALSE(
+      FPDFAnnot_GetFocusableSubtypes(form_handle(), nullptr, subtypes.size()));
+  EXPECT_FALSE(
+      FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), 0));
 
   UnloadPage(page);
 }