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