// Copyright 2016 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "core/fpdfdoc/cpdf_formfield.h" #include #include "constants/form_fields.h" #include "constants/form_flags.h" #include "core/fpdfapi/page/cpdf_pagemodule.h" #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_indirect_object_holder.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fpdfapi/parser/cpdf_test_document.h" #include "core/fpdfdoc/cpdf_interactiveform.h" #include "core/fxcrt/containers/contains.h" #include "core/fxcrt/fx_memory.h" #include "testing/gtest/include/gtest/gtest.h" namespace { // Create and destroys the page module that is necessary when instantiating a // CPDF_Document. class ScopedCPDF_PageModule { public: FX_STACK_ALLOCATED(); ScopedCPDF_PageModule() { CPDF_PageModule::Create(); } ~ScopedCPDF_PageModule() { CPDF_PageModule::Destroy(); } }; void TestMultiselectFieldDict(RetainPtr opt_array, RetainPtr values, RetainPtr selected_indices, bool expected_use_indices, const std::vector& expected_indices, const std::vector& excluded_indices) { auto form_dict = pdfium::MakeRetain(); form_dict->SetNewFor("Type", "Annot"); form_dict->SetNewFor("Subtype", "Widget"); form_dict->SetNewFor(pdfium::form_fields::kFT, pdfium::form_fields::kCh); constexpr int kMuliSelectFlag = pdfium::form_flags::kChoiceMultiSelect; form_dict->SetNewFor(pdfium::form_fields::kFf, kMuliSelectFlag); form_dict->SetFor("Opt", opt_array); form_dict->SetFor(pdfium::form_fields::kV, values); form_dict->SetFor("I", selected_indices); CPDF_TestDocument doc; CPDF_InteractiveForm form(&doc); CPDF_FormField form_field(&form, std::move(form_dict)); EXPECT_EQ(expected_use_indices, form_field.UseSelectedIndicesObject()); for (int i = 0; i < form_field.CountOptions(); i++) { const bool expected_selected = pdfium::Contains(expected_indices, i); EXPECT_EQ(expected_selected, form_field.IsItemSelected(i)); } for (int i : excluded_indices) { EXPECT_FALSE(form_field.IsItemSelected(i)); } } } // namespace TEST(CPDFFormFieldTest, GetFullNameForDict) { WideString name = CPDF_FormField::GetFullNameForDict(nullptr); EXPECT_TRUE(name.IsEmpty()); CPDF_IndirectObjectHolder obj_holder; auto root = obj_holder.NewIndirect(); root->SetNewFor("T", "foo"); name = CPDF_FormField::GetFullNameForDict(root.Get()); EXPECT_EQ("foo", name.ToUTF8()); auto dict1 = obj_holder.NewIndirect(); root->SetNewFor("Parent", &obj_holder, dict1->GetObjNum()); dict1->SetNewFor("T", "bar"); name = CPDF_FormField::GetFullNameForDict(root.Get()); EXPECT_EQ("bar.foo", name.ToUTF8()); auto dict2 = dict1->SetNewFor("Parent"); name = CPDF_FormField::GetFullNameForDict(root.Get()); EXPECT_EQ("bar.foo", name.ToUTF8()); auto dict3 = obj_holder.NewIndirect(); dict2->SetNewFor("Parent", &obj_holder, dict3->GetObjNum()); dict3->SetNewFor("T", "qux"); name = CPDF_FormField::GetFullNameForDict(root.Get()); EXPECT_EQ("qux.bar.foo", name.ToUTF8()); dict3->SetNewFor("Parent", &obj_holder, root->GetObjNum()); name = CPDF_FormField::GetFullNameForDict(root.Get()); EXPECT_EQ("qux.bar.foo", name.ToUTF8()); name = CPDF_FormField::GetFullNameForDict(dict1.Get()); EXPECT_EQ("foo.qux.bar", name.ToUTF8()); name = CPDF_FormField::GetFullNameForDict(dict2.Get()); EXPECT_EQ("bar.foo.qux", name.ToUTF8()); name = CPDF_FormField::GetFullNameForDict(dict3.Get()); EXPECT_EQ("bar.foo.qux", name.ToUTF8()); } TEST(CPDFFormFieldTest, IsItemSelected) { ScopedCPDF_PageModule page_module; auto opt_array = pdfium::MakeRetain(); opt_array->AppendNew(L"Alpha"); opt_array->AppendNew(L"Beta"); opt_array->AppendNew(L"Gamma"); opt_array->AppendNew(L"Delta"); opt_array->AppendNew(L"Epsilon"); { // No Values (/V) or Selected Indices (/I) objects. std::vector expected_indices; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is just a string. auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Gamma"); std::vector expected_indices{2}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is just an invalid string. auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Omega"); std::vector expected_indices; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is an array with one object. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); std::vector expected_indices{1}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is an array with one invalid object. auto values = pdfium::MakeRetain(); values->AppendNew(L"Omega"); std::vector expected_indices; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is an array with multiple objects. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) object is an array with multiple objects with one invalid. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); values->AppendNew(L"Omega"); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Selected indices (/I) object is just a number. auto selected_indices = pdfium::MakeRetain(3); std::vector expected_indices{3}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, /*expected_use_indices=*/true, expected_indices, excluded_indices); } { // Selected indices (/I) object is just an invalid number. auto selected_indices = pdfium::MakeRetain(26); std::vector expected_indices; std::vector excluded_indices{-1, 5, 26}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, /*expected_use_indices=*/true, expected_indices, excluded_indices); } { // Selected indices (/I) object is an array with one object. auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(0); std::vector expected_indices{0}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, /*expected_use_indices=*/true, expected_indices, excluded_indices); } { // Selected indices (/I) object is an array with multiple objects. auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(0); selected_indices->AppendNew(2); selected_indices->AppendNew(3); std::vector expected_indices{0, 2, 3}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, /*expected_use_indices=*/true, expected_indices, excluded_indices); } { // Selected indices (/I) object is an array with multiple objects and some // are invalid. auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(0); selected_indices->AppendNew(2); selected_indices->AppendNew(3); selected_indices->AppendNew(-5); selected_indices->AppendNew(12); selected_indices->AppendNew(42); std::vector expected_indices{0, 2, 3}; std::vector excluded_indices{-5, -1, 5, 12, 42}; TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices, /*expected_use_indices=*/true, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with different // lengths. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(0); selected_indices->AppendNew(2); selected_indices->AppendNew(3); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with same lengths. auto values = pdfium::MakeRetain(); values->AppendNew(L"Alpha"); values->AppendNew(L"Epsilon"); auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(2); selected_indices->AppendNew(3); std::vector expected_indices{0, 4}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with values being // invalid. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); values->AppendNew(L"Omega"); auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(1); selected_indices->AppendNew(4); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with selected // indices being invalid. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(1); selected_indices->AppendNew(4); selected_indices->AppendNew(26); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5, 26}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with both being // invalid. auto values = pdfium::MakeRetain(); values->AppendNew(L"Beta"); values->AppendNew(L"Epsilon"); values->AppendNew(L"Omega"); auto selected_indices = pdfium::MakeRetain(); selected_indices->AppendNew(0); selected_indices->AppendNew(2); selected_indices->AppendNew(3); selected_indices->AppendNew(26); std::vector expected_indices{1, 4}; std::vector excluded_indices{-1, 5, 26}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } { // Values (/V) or Selected Indices (/I) objects conflict with each not being // an array. auto values = pdfium::MakeRetain(/*pPool=*/nullptr, L"Gamma"); auto selected_indices = pdfium::MakeRetain(4); std::vector expected_indices{2}; std::vector excluded_indices{-1, 5}; TestMultiselectFieldDict(opt_array, values, selected_indices, /*expected_use_indices=*/false, expected_indices, excluded_indices); } }