1 // Copyright 2021 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/fpdfdoc/cpdf_action.h"
6
7 #include "core/fpdfapi/parser/cpdf_dictionary.h"
8 #include "core/fpdfapi/parser/cpdf_name.h"
9 #include "core/fpdfapi/parser/cpdf_string.h"
10 #include "core/fxcrt/retain_ptr.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace {
14
CreateActionDictWithType(const ByteString & action_type)15 RetainPtr<CPDF_Dictionary> CreateActionDictWithType(
16 const ByteString& action_type) {
17 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
18 dict->SetNewFor<CPDF_Name>("Type", "Action");
19 dict->SetNewFor<CPDF_Name>("S", action_type);
20 return dict;
21 }
22
CreateActionDictWithoutType(const ByteString & action_type)23 RetainPtr<CPDF_Dictionary> CreateActionDictWithoutType(
24 const ByteString& action_type) {
25 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
26 dict->SetNewFor<CPDF_Name>("S", action_type);
27 return dict;
28 }
29
CreateActionDictWithInvalidType(const ByteString & action_type)30 RetainPtr<CPDF_Dictionary> CreateActionDictWithInvalidType(
31 const ByteString& action_type) {
32 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
33 dict->SetNewFor<CPDF_Name>("Type", "Lights");
34 dict->SetNewFor<CPDF_Name>("S", action_type);
35 return dict;
36 }
37
CreateInvalidActionDictWithType(const ByteString & action_type)38 RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithType(
39 const ByteString& action_type) {
40 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
41 dict->SetNewFor<CPDF_Name>("Type", "Action");
42 dict->SetNewFor<CPDF_String>("S", action_type);
43 return dict;
44 }
45
CreateInvalidActionDictWithoutType(const ByteString & action_type)46 RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithoutType(
47 const ByteString& action_type) {
48 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
49 dict->SetNewFor<CPDF_String>("S", action_type);
50 return dict;
51 }
52
53 } // namespace
54
TEST(CPDFActionTest,GetType)55 TEST(CPDFActionTest, GetType) {
56 static constexpr struct {
57 const char* action_type;
58 CPDF_Action::Type expected_type;
59 } kValidTestCases[] = {
60 {"GoTo", CPDF_Action::Type::kGoTo},
61 {"GoToR", CPDF_Action::Type::kGoToR},
62 {"GoToE", CPDF_Action::Type::kGoToE},
63 {"Launch", CPDF_Action::Type::kLaunch},
64 {"Thread", CPDF_Action::Type::kThread},
65 {"URI", CPDF_Action::Type::kURI},
66 {"Sound", CPDF_Action::Type::kSound},
67 {"Movie", CPDF_Action::Type::kMovie},
68 {"Hide", CPDF_Action::Type::kHide},
69 {"Named", CPDF_Action::Type::kNamed},
70 {"SubmitForm", CPDF_Action::Type::kSubmitForm},
71 {"ResetForm", CPDF_Action::Type::kResetForm},
72 {"ImportData", CPDF_Action::Type::kImportData},
73 {"JavaScript", CPDF_Action::Type::kJavaScript},
74 {"SetOCGState", CPDF_Action::Type::kSetOCGState},
75 {"Rendition", CPDF_Action::Type::kRendition},
76 {"Trans", CPDF_Action::Type::kTrans},
77 {"GoTo3DView", CPDF_Action::Type::kGoTo3DView},
78 };
79
80 // Test correctly constructed actions.
81 for (const auto& test_case : kValidTestCases) {
82 {
83 // Type is present.
84 CPDF_Action action(CreateActionDictWithType(test_case.action_type));
85 EXPECT_EQ(test_case.expected_type, action.GetType());
86 }
87 {
88 // Type is optional, so omitting it is ok.
89 CPDF_Action action(CreateActionDictWithoutType(test_case.action_type));
90 EXPECT_EQ(test_case.expected_type, action.GetType());
91 }
92 }
93
94 // Test incorrectly constructed actions.
95 for (const auto& test_case : kValidTestCases) {
96 {
97 // Type is optional, but must be valid if present.
98 CPDF_Action action(
99 CreateActionDictWithInvalidType(test_case.action_type));
100 EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
101 }
102 {
103 // The action type (/S) must be a name.
104 CPDF_Action action(
105 CreateInvalidActionDictWithType(test_case.action_type));
106 EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
107 }
108 {
109 // The action type (/S) must be a name.
110 CPDF_Action action(
111 CreateInvalidActionDictWithoutType(test_case.action_type));
112 EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
113 }
114 }
115
116 static constexpr const char* kInvalidTestCases[] = {
117 "Camera",
118 "Javascript",
119 "Unknown",
120 };
121
122 // Test invalid actions.
123 for (const char* test_case : kInvalidTestCases) {
124 CPDF_Action action(CreateActionDictWithType(test_case));
125 EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType());
126 }
127 }
128