1 // Copyright 2017 PDFium Authors. All rights reserved. 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/fpdfapi/parser/cpdf_page_object_avail.h" 6 7 #include <map> 8 #include <memory> 9 #include <utility> 10 11 #include "core/fpdfapi/parser/cpdf_array.h" 12 #include "core/fpdfapi/parser/cpdf_dictionary.h" 13 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h" 14 #include "core/fpdfapi/parser/cpdf_read_validator.h" 15 #include "core/fpdfapi/parser/cpdf_reference.h" 16 #include "core/fpdfapi/parser/cpdf_string.h" 17 #include "core/fxcrt/fx_stream.h" 18 #include "testing/fx_string_testhelpers.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 #include "third_party/base/ptr_util.h" 21 22 namespace { 23 24 25 class TestReadValidator : public CPDF_ReadValidator { 26 public: 27 template <typename T, typename... Args> 28 friend RetainPtr<T> pdfium::MakeRetain(Args&&... args); 29 30 void SimulateReadError() { ReadBlock(nullptr, 0, 1); } 31 32 protected: 33 TestReadValidator() 34 : CPDF_ReadValidator( 35 pdfium::MakeRetain<CFX_InvalidSeekableReadStream>(100), 36 nullptr) {} 37 ~TestReadValidator() override {} 38 }; 39 40 class TestHolder : public CPDF_IndirectObjectHolder { 41 public: 42 enum class ObjectState { 43 Unavailable, 44 Available, 45 }; 46 TestHolder() : validator_(pdfium::MakeRetain<TestReadValidator>()) {} 47 ~TestHolder() override {} 48 49 // CPDF_IndirectObjectHolder overrides: 50 CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override { 51 auto it = objects_data_.find(objnum); 52 if (it == objects_data_.end()) 53 return nullptr; 54 55 ObjectData& obj_data = it->second; 56 if (obj_data.state == ObjectState::Unavailable) { 57 validator_->SimulateReadError(); 58 return nullptr; 59 } 60 return obj_data.object.get(); 61 } 62 63 RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; } 64 65 void AddObject(uint32_t objnum, 66 std::unique_ptr<CPDF_Object> object, 67 ObjectState state) { 68 ObjectData object_data; 69 object_data.object = std::move(object); 70 object_data.state = state; 71 ASSERT(objects_data_.find(objnum) == objects_data_.end()); 72 objects_data_[objnum] = std::move(object_data); 73 } 74 75 void SetObjectState(uint32_t objnum, ObjectState state) { 76 auto it = objects_data_.find(objnum); 77 ASSERT(it != objects_data_.end()); 78 ObjectData& obj_data = it->second; 79 obj_data.state = state; 80 } 81 82 CPDF_Object* GetTestObject(uint32_t objnum) { 83 auto it = objects_data_.find(objnum); 84 if (it == objects_data_.end()) 85 return nullptr; 86 return it->second.object.get(); 87 } 88 89 private: 90 struct ObjectData { 91 std::unique_ptr<CPDF_Object> object; 92 ObjectState state = ObjectState::Unavailable; 93 }; 94 std::map<uint32_t, ObjectData> objects_data_; 95 RetainPtr<TestReadValidator> validator_; 96 }; 97 98 } // namespace 99 100 TEST(CPDF_PageObjectAvailTest, ExcludePages) { 101 TestHolder holder; 102 holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(), 103 TestHolder::ObjectState::Available); 104 holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Kids", &holder, 105 2); 106 holder.AddObject(2, pdfium::MakeUnique<CPDF_Array>(), 107 TestHolder::ObjectState::Available); 108 holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 3); 109 110 holder.AddObject(3, pdfium::MakeUnique<CPDF_Dictionary>(), 111 TestHolder::ObjectState::Available); 112 holder.GetTestObject(3)->GetDict()->SetFor( 113 "Type", pdfium::MakeUnique<CPDF_String>(nullptr, "Page", false)); 114 holder.GetTestObject(3)->GetDict()->SetNewFor<CPDF_Reference>("OtherPageData", 115 &holder, 4); 116 // Add unavailable object related to other page. 117 holder.AddObject( 118 4, pdfium::MakeUnique<CPDF_String>(nullptr, "Other page data", false), 119 TestHolder::ObjectState::Unavailable); 120 121 CPDF_PageObjectAvail avail(holder.GetValidator().Get(), &holder, 1); 122 // Now object should be available, although the object '4' is not available, 123 // because it is in skipped other page. 124 EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail()); 125 } 126