xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_dataavail_embeddertest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1*3ac0a46fSAndroid Build Coastguard Worker // Copyright 2015 The PDFium Authors
2*3ac0a46fSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*3ac0a46fSAndroid Build Coastguard Worker // found in the LICENSE file.
4*3ac0a46fSAndroid Build Coastguard Worker 
5*3ac0a46fSAndroid Build Coastguard Worker #include <algorithm>
6*3ac0a46fSAndroid Build Coastguard Worker #include <memory>
7*3ac0a46fSAndroid Build Coastguard Worker #include <string>
8*3ac0a46fSAndroid Build Coastguard Worker #include <utility>
9*3ac0a46fSAndroid Build Coastguard Worker #include <vector>
10*3ac0a46fSAndroid Build Coastguard Worker 
11*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxcrt/bytestring.h"
12*3ac0a46fSAndroid Build Coastguard Worker #include "public/fpdf_doc.h"
13*3ac0a46fSAndroid Build Coastguard Worker #include "public/fpdfview.h"
14*3ac0a46fSAndroid Build Coastguard Worker #include "testing/embedder_test.h"
15*3ac0a46fSAndroid Build Coastguard Worker #include "testing/fx_string_testhelpers.h"
16*3ac0a46fSAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
17*3ac0a46fSAndroid Build Coastguard Worker #include "testing/range_set.h"
18*3ac0a46fSAndroid Build Coastguard Worker #include "testing/utils/file_util.h"
19*3ac0a46fSAndroid Build Coastguard Worker #include "testing/utils/path_service.h"
20*3ac0a46fSAndroid Build Coastguard Worker 
21*3ac0a46fSAndroid Build Coastguard Worker namespace {
22*3ac0a46fSAndroid Build Coastguard Worker 
23*3ac0a46fSAndroid Build Coastguard Worker class MockDownloadHints final : public FX_DOWNLOADHINTS {
24*3ac0a46fSAndroid Build Coastguard Worker  public:
SAddSegment(FX_DOWNLOADHINTS * pThis,size_t offset,size_t size)25*3ac0a46fSAndroid Build Coastguard Worker   static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
26*3ac0a46fSAndroid Build Coastguard Worker   }
27*3ac0a46fSAndroid Build Coastguard Worker 
MockDownloadHints()28*3ac0a46fSAndroid Build Coastguard Worker   MockDownloadHints() {
29*3ac0a46fSAndroid Build Coastguard Worker     FX_DOWNLOADHINTS::version = 1;
30*3ac0a46fSAndroid Build Coastguard Worker     FX_DOWNLOADHINTS::AddSegment = SAddSegment;
31*3ac0a46fSAndroid Build Coastguard Worker   }
32*3ac0a46fSAndroid Build Coastguard Worker 
33*3ac0a46fSAndroid Build Coastguard Worker   ~MockDownloadHints() = default;
34*3ac0a46fSAndroid Build Coastguard Worker };
35*3ac0a46fSAndroid Build Coastguard Worker 
36*3ac0a46fSAndroid Build Coastguard Worker class TestAsyncLoader final : public FX_DOWNLOADHINTS, FX_FILEAVAIL {
37*3ac0a46fSAndroid Build Coastguard Worker  public:
TestAsyncLoader(const std::string & file_name)38*3ac0a46fSAndroid Build Coastguard Worker   explicit TestAsyncLoader(const std::string& file_name) {
39*3ac0a46fSAndroid Build Coastguard Worker     std::string file_path;
40*3ac0a46fSAndroid Build Coastguard Worker     if (!PathService::GetTestFilePath(file_name, &file_path))
41*3ac0a46fSAndroid Build Coastguard Worker       return;
42*3ac0a46fSAndroid Build Coastguard Worker     file_contents_ = GetFileContents(file_path.c_str(), &file_length_);
43*3ac0a46fSAndroid Build Coastguard Worker     if (!file_contents_)
44*3ac0a46fSAndroid Build Coastguard Worker       return;
45*3ac0a46fSAndroid Build Coastguard Worker 
46*3ac0a46fSAndroid Build Coastguard Worker     file_access_.m_FileLen = static_cast<unsigned long>(file_length_);
47*3ac0a46fSAndroid Build Coastguard Worker     file_access_.m_GetBlock = SGetBlock;
48*3ac0a46fSAndroid Build Coastguard Worker     file_access_.m_Param = this;
49*3ac0a46fSAndroid Build Coastguard Worker 
50*3ac0a46fSAndroid Build Coastguard Worker     FX_DOWNLOADHINTS::version = 1;
51*3ac0a46fSAndroid Build Coastguard Worker     FX_DOWNLOADHINTS::AddSegment = SAddSegment;
52*3ac0a46fSAndroid Build Coastguard Worker 
53*3ac0a46fSAndroid Build Coastguard Worker     FX_FILEAVAIL::version = 1;
54*3ac0a46fSAndroid Build Coastguard Worker     FX_FILEAVAIL::IsDataAvail = SIsDataAvail;
55*3ac0a46fSAndroid Build Coastguard Worker   }
56*3ac0a46fSAndroid Build Coastguard Worker 
IsOpened() const57*3ac0a46fSAndroid Build Coastguard Worker   bool IsOpened() const { return !!file_contents_; }
58*3ac0a46fSAndroid Build Coastguard Worker 
file_access()59*3ac0a46fSAndroid Build Coastguard Worker   FPDF_FILEACCESS* file_access() { return &file_access_; }
hints()60*3ac0a46fSAndroid Build Coastguard Worker   FX_DOWNLOADHINTS* hints() { return this; }
file_avail()61*3ac0a46fSAndroid Build Coastguard Worker   FX_FILEAVAIL* file_avail() { return this; }
62*3ac0a46fSAndroid Build Coastguard Worker 
requested_segments() const63*3ac0a46fSAndroid Build Coastguard Worker   const std::vector<std::pair<size_t, size_t>>& requested_segments() const {
64*3ac0a46fSAndroid Build Coastguard Worker     return requested_segments_;
65*3ac0a46fSAndroid Build Coastguard Worker   }
66*3ac0a46fSAndroid Build Coastguard Worker 
max_requested_bound() const67*3ac0a46fSAndroid Build Coastguard Worker   size_t max_requested_bound() const { return max_requested_bound_; }
68*3ac0a46fSAndroid Build Coastguard Worker 
ClearRequestedSegments()69*3ac0a46fSAndroid Build Coastguard Worker   void ClearRequestedSegments() {
70*3ac0a46fSAndroid Build Coastguard Worker     requested_segments_.clear();
71*3ac0a46fSAndroid Build Coastguard Worker     max_requested_bound_ = 0;
72*3ac0a46fSAndroid Build Coastguard Worker   }
73*3ac0a46fSAndroid Build Coastguard Worker 
is_new_data_available() const74*3ac0a46fSAndroid Build Coastguard Worker   bool is_new_data_available() const { return is_new_data_available_; }
set_is_new_data_available(bool is_new_data_available)75*3ac0a46fSAndroid Build Coastguard Worker   void set_is_new_data_available(bool is_new_data_available) {
76*3ac0a46fSAndroid Build Coastguard Worker     is_new_data_available_ = is_new_data_available;
77*3ac0a46fSAndroid Build Coastguard Worker   }
78*3ac0a46fSAndroid Build Coastguard Worker 
max_already_available_bound() const79*3ac0a46fSAndroid Build Coastguard Worker   size_t max_already_available_bound() const {
80*3ac0a46fSAndroid Build Coastguard Worker     return available_ranges_.IsEmpty()
81*3ac0a46fSAndroid Build Coastguard Worker                ? 0
82*3ac0a46fSAndroid Build Coastguard Worker                : available_ranges_.ranges().rbegin()->second;
83*3ac0a46fSAndroid Build Coastguard Worker   }
84*3ac0a46fSAndroid Build Coastguard Worker 
FlushRequestedData()85*3ac0a46fSAndroid Build Coastguard Worker   void FlushRequestedData() {
86*3ac0a46fSAndroid Build Coastguard Worker     for (const auto& it : requested_segments_) {
87*3ac0a46fSAndroid Build Coastguard Worker       SetDataAvailable(it.first, it.second);
88*3ac0a46fSAndroid Build Coastguard Worker     }
89*3ac0a46fSAndroid Build Coastguard Worker     ClearRequestedSegments();
90*3ac0a46fSAndroid Build Coastguard Worker   }
91*3ac0a46fSAndroid Build Coastguard Worker 
file_contents()92*3ac0a46fSAndroid Build Coastguard Worker   char* file_contents() { return file_contents_.get(); }
file_length() const93*3ac0a46fSAndroid Build Coastguard Worker   size_t file_length() const { return file_length_; }
94*3ac0a46fSAndroid Build Coastguard Worker 
95*3ac0a46fSAndroid Build Coastguard Worker  private:
SetDataAvailable(size_t start,size_t size)96*3ac0a46fSAndroid Build Coastguard Worker   void SetDataAvailable(size_t start, size_t size) {
97*3ac0a46fSAndroid Build Coastguard Worker     available_ranges_.Union(RangeSet::Range(start, start + size));
98*3ac0a46fSAndroid Build Coastguard Worker   }
99*3ac0a46fSAndroid Build Coastguard Worker 
CheckDataAlreadyAvailable(size_t start,size_t size) const100*3ac0a46fSAndroid Build Coastguard Worker   bool CheckDataAlreadyAvailable(size_t start, size_t size) const {
101*3ac0a46fSAndroid Build Coastguard Worker     return available_ranges_.Contains(RangeSet::Range(start, start + size));
102*3ac0a46fSAndroid Build Coastguard Worker   }
103*3ac0a46fSAndroid Build Coastguard Worker 
GetBlockImpl(unsigned long pos,unsigned char * pBuf,unsigned long size)104*3ac0a46fSAndroid Build Coastguard Worker   int GetBlockImpl(unsigned long pos, unsigned char* pBuf, unsigned long size) {
105*3ac0a46fSAndroid Build Coastguard Worker     if (!IsDataAvailImpl(pos, size))
106*3ac0a46fSAndroid Build Coastguard Worker       return 0;
107*3ac0a46fSAndroid Build Coastguard Worker     const unsigned long end =
108*3ac0a46fSAndroid Build Coastguard Worker         std::min(static_cast<unsigned long>(file_length_), pos + size);
109*3ac0a46fSAndroid Build Coastguard Worker     if (end <= pos)
110*3ac0a46fSAndroid Build Coastguard Worker       return 0;
111*3ac0a46fSAndroid Build Coastguard Worker     memcpy(pBuf, file_contents_.get() + pos, end - pos);
112*3ac0a46fSAndroid Build Coastguard Worker     SetDataAvailable(pos, end - pos);
113*3ac0a46fSAndroid Build Coastguard Worker     return static_cast<int>(end - pos);
114*3ac0a46fSAndroid Build Coastguard Worker   }
115*3ac0a46fSAndroid Build Coastguard Worker 
AddSegmentImpl(size_t offset,size_t size)116*3ac0a46fSAndroid Build Coastguard Worker   void AddSegmentImpl(size_t offset, size_t size) {
117*3ac0a46fSAndroid Build Coastguard Worker     requested_segments_.push_back(std::make_pair(offset, size));
118*3ac0a46fSAndroid Build Coastguard Worker     max_requested_bound_ = std::max(max_requested_bound_, offset + size);
119*3ac0a46fSAndroid Build Coastguard Worker   }
120*3ac0a46fSAndroid Build Coastguard Worker 
IsDataAvailImpl(size_t offset,size_t size)121*3ac0a46fSAndroid Build Coastguard Worker   bool IsDataAvailImpl(size_t offset, size_t size) {
122*3ac0a46fSAndroid Build Coastguard Worker     if (offset + size > file_length_)
123*3ac0a46fSAndroid Build Coastguard Worker       return false;
124*3ac0a46fSAndroid Build Coastguard Worker     if (is_new_data_available_) {
125*3ac0a46fSAndroid Build Coastguard Worker       SetDataAvailable(offset, size);
126*3ac0a46fSAndroid Build Coastguard Worker       return true;
127*3ac0a46fSAndroid Build Coastguard Worker     }
128*3ac0a46fSAndroid Build Coastguard Worker     return CheckDataAlreadyAvailable(offset, size);
129*3ac0a46fSAndroid Build Coastguard Worker   }
130*3ac0a46fSAndroid Build Coastguard Worker 
SGetBlock(void * param,unsigned long pos,unsigned char * pBuf,unsigned long size)131*3ac0a46fSAndroid Build Coastguard Worker   static int SGetBlock(void* param,
132*3ac0a46fSAndroid Build Coastguard Worker                        unsigned long pos,
133*3ac0a46fSAndroid Build Coastguard Worker                        unsigned char* pBuf,
134*3ac0a46fSAndroid Build Coastguard Worker                        unsigned long size) {
135*3ac0a46fSAndroid Build Coastguard Worker     return static_cast<TestAsyncLoader*>(param)->GetBlockImpl(pos, pBuf, size);
136*3ac0a46fSAndroid Build Coastguard Worker   }
137*3ac0a46fSAndroid Build Coastguard Worker 
SAddSegment(FX_DOWNLOADHINTS * pThis,size_t offset,size_t size)138*3ac0a46fSAndroid Build Coastguard Worker   static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
139*3ac0a46fSAndroid Build Coastguard Worker     return static_cast<TestAsyncLoader*>(pThis)->AddSegmentImpl(offset, size);
140*3ac0a46fSAndroid Build Coastguard Worker   }
141*3ac0a46fSAndroid Build Coastguard Worker 
SIsDataAvail(FX_FILEAVAIL * pThis,size_t offset,size_t size)142*3ac0a46fSAndroid Build Coastguard Worker   static FPDF_BOOL SIsDataAvail(FX_FILEAVAIL* pThis,
143*3ac0a46fSAndroid Build Coastguard Worker                                 size_t offset,
144*3ac0a46fSAndroid Build Coastguard Worker                                 size_t size) {
145*3ac0a46fSAndroid Build Coastguard Worker     return static_cast<TestAsyncLoader*>(pThis)->IsDataAvailImpl(offset, size);
146*3ac0a46fSAndroid Build Coastguard Worker   }
147*3ac0a46fSAndroid Build Coastguard Worker 
148*3ac0a46fSAndroid Build Coastguard Worker   FPDF_FILEACCESS file_access_;
149*3ac0a46fSAndroid Build Coastguard Worker 
150*3ac0a46fSAndroid Build Coastguard Worker   std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
151*3ac0a46fSAndroid Build Coastguard Worker   size_t file_length_ = 0;
152*3ac0a46fSAndroid Build Coastguard Worker   std::vector<std::pair<size_t, size_t>> requested_segments_;
153*3ac0a46fSAndroid Build Coastguard Worker   size_t max_requested_bound_ = 0;
154*3ac0a46fSAndroid Build Coastguard Worker   bool is_new_data_available_ = true;
155*3ac0a46fSAndroid Build Coastguard Worker 
156*3ac0a46fSAndroid Build Coastguard Worker   RangeSet available_ranges_;
157*3ac0a46fSAndroid Build Coastguard Worker };
158*3ac0a46fSAndroid Build Coastguard Worker 
159*3ac0a46fSAndroid Build Coastguard Worker }  // namespace
160*3ac0a46fSAndroid Build Coastguard Worker 
161*3ac0a46fSAndroid Build Coastguard Worker class FPDFDataAvailEmbedderTest : public EmbedderTest {};
162*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,TrailerUnterminated)163*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, TrailerUnterminated) {
164*3ac0a46fSAndroid Build Coastguard Worker   // Document must load without crashing but is too malformed to be available.
165*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(OpenDocument("trailer_unterminated.pdf"));
166*3ac0a46fSAndroid Build Coastguard Worker   MockDownloadHints hints;
167*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
168*3ac0a46fSAndroid Build Coastguard Worker }
169*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,TrailerAsHexstring)170*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, TrailerAsHexstring) {
171*3ac0a46fSAndroid Build Coastguard Worker   // Document must load without crashing but is too malformed to be available.
172*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(OpenDocument("trailer_as_hexstring.pdf"));
173*3ac0a46fSAndroid Build Coastguard Worker   MockDownloadHints hints;
174*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
175*3ac0a46fSAndroid Build Coastguard Worker }
176*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,LoadUsingHintTables)177*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, LoadUsingHintTables) {
178*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("feature_linearized_loading.pdf");
179*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
180*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
181*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
182*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
183*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
184*3ac0a46fSAndroid Build Coastguard Worker 
185*3ac0a46fSAndroid Build Coastguard Worker   // No new data available, to prevent load "Pages" node.
186*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
187*3ac0a46fSAndroid Build Coastguard Worker   ScopedFPDFPage page(FPDF_LoadPage(document(), 1));
188*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_TRUE(page);
189*3ac0a46fSAndroid Build Coastguard Worker }
190*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,CheckFormAvailIfLinearized)191*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, CheckFormAvailIfLinearized) {
192*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("feature_linearized_loading.pdf");
193*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
194*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
195*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
196*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
197*3ac0a46fSAndroid Build Coastguard Worker 
198*3ac0a46fSAndroid Build Coastguard Worker   // Prevent access to non-requested data to coerce the parser to send new
199*3ac0a46fSAndroid Build Coastguard Worker   // request for non available (non-requested before) data.
200*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
201*3ac0a46fSAndroid Build Coastguard Worker   loader.ClearRequestedSegments();
202*3ac0a46fSAndroid Build Coastguard Worker 
203*3ac0a46fSAndroid Build Coastguard Worker   int status = PDF_FORM_NOTAVAIL;
204*3ac0a46fSAndroid Build Coastguard Worker   while (status == PDF_FORM_NOTAVAIL) {
205*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
206*3ac0a46fSAndroid Build Coastguard Worker     status = FPDFAvail_IsFormAvail(avail(), loader.hints());
207*3ac0a46fSAndroid Build Coastguard Worker   }
208*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_NE(PDF_FORM_ERROR, status);
209*3ac0a46fSAndroid Build Coastguard Worker }
210*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,DoNotLoadMainCrossRefForFirstPageIfLinearized)211*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest,
212*3ac0a46fSAndroid Build Coastguard Worker        DoNotLoadMainCrossRefForFirstPageIfLinearized) {
213*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("feature_linearized_loading.pdf");
214*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
215*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
216*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
217*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
218*3ac0a46fSAndroid Build Coastguard Worker   const int first_page_num = FPDFAvail_GetFirstPageNum(document());
219*3ac0a46fSAndroid Build Coastguard Worker 
220*3ac0a46fSAndroid Build Coastguard Worker   // The main cross ref table should not be processed.
221*3ac0a46fSAndroid Build Coastguard Worker   // (It is always at file end)
222*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_GT(loader.file_access()->m_FileLen,
223*3ac0a46fSAndroid Build Coastguard Worker             loader.max_already_available_bound());
224*3ac0a46fSAndroid Build Coastguard Worker 
225*3ac0a46fSAndroid Build Coastguard Worker   // Prevent access to non-requested data to coerce the parser to send new
226*3ac0a46fSAndroid Build Coastguard Worker   // request for non available (non-requested before) data.
227*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
228*3ac0a46fSAndroid Build Coastguard Worker   FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints());
229*3ac0a46fSAndroid Build Coastguard Worker 
230*3ac0a46fSAndroid Build Coastguard Worker   // The main cross ref table should not be requested.
231*3ac0a46fSAndroid Build Coastguard Worker   // (It is always at file end)
232*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_GT(loader.file_access()->m_FileLen, loader.max_requested_bound());
233*3ac0a46fSAndroid Build Coastguard Worker 
234*3ac0a46fSAndroid Build Coastguard Worker   // Allow parse page.
235*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(true);
236*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL,
237*3ac0a46fSAndroid Build Coastguard Worker             FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints()));
238*3ac0a46fSAndroid Build Coastguard Worker 
239*3ac0a46fSAndroid Build Coastguard Worker   // The main cross ref table should not be processed.
240*3ac0a46fSAndroid Build Coastguard Worker   // (It is always at file end)
241*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_GT(loader.file_access()->m_FileLen,
242*3ac0a46fSAndroid Build Coastguard Worker             loader.max_already_available_bound());
243*3ac0a46fSAndroid Build Coastguard Worker 
244*3ac0a46fSAndroid Build Coastguard Worker   // Prevent loading data, while page loading.
245*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
246*3ac0a46fSAndroid Build Coastguard Worker   ScopedFPDFPage page(FPDF_LoadPage(document(), first_page_num));
247*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_TRUE(page);
248*3ac0a46fSAndroid Build Coastguard Worker }
249*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,LoadSecondPageIfLinearizedWithHints)250*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, LoadSecondPageIfLinearizedWithHints) {
251*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("feature_linearized_loading.pdf");
252*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
253*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
254*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
255*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
256*3ac0a46fSAndroid Build Coastguard Worker 
257*3ac0a46fSAndroid Build Coastguard Worker   static constexpr uint32_t kSecondPageNum = 1;
258*3ac0a46fSAndroid Build Coastguard Worker 
259*3ac0a46fSAndroid Build Coastguard Worker   // Prevent access to non-requested data to coerce the parser to send new
260*3ac0a46fSAndroid Build Coastguard Worker   // request for non available (non-requested before) data.
261*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
262*3ac0a46fSAndroid Build Coastguard Worker   loader.ClearRequestedSegments();
263*3ac0a46fSAndroid Build Coastguard Worker 
264*3ac0a46fSAndroid Build Coastguard Worker   int status = PDF_DATA_NOTAVAIL;
265*3ac0a46fSAndroid Build Coastguard Worker   while (status == PDF_DATA_NOTAVAIL) {
266*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
267*3ac0a46fSAndroid Build Coastguard Worker     status = FPDFAvail_IsPageAvail(avail(), kSecondPageNum, loader.hints());
268*3ac0a46fSAndroid Build Coastguard Worker   }
269*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_DATA_AVAIL, status);
270*3ac0a46fSAndroid Build Coastguard Worker 
271*3ac0a46fSAndroid Build Coastguard Worker   // Prevent loading data, while page loading.
272*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
273*3ac0a46fSAndroid Build Coastguard Worker   ScopedFPDFPage page(FPDF_LoadPage(document(), kSecondPageNum));
274*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_TRUE(page);
275*3ac0a46fSAndroid Build Coastguard Worker }
276*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,LoadInfoAfterReceivingWholeDocument)277*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingWholeDocument) {
278*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("linearized.pdf");
279*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
280*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
281*3ac0a46fSAndroid Build Coastguard Worker   while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
282*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
283*3ac0a46fSAndroid Build Coastguard Worker   }
284*3ac0a46fSAndroid Build Coastguard Worker 
285*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
286*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
287*3ac0a46fSAndroid Build Coastguard Worker 
288*3ac0a46fSAndroid Build Coastguard Worker   // The "info" dictionary should still be unavailable.
289*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0));
290*3ac0a46fSAndroid Build Coastguard Worker 
291*3ac0a46fSAndroid Build Coastguard Worker   // Simulate receiving whole file.
292*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(true);
293*3ac0a46fSAndroid Build Coastguard Worker   // Load second page, to parse additional crossref sections.
294*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
295*3ac0a46fSAndroid Build Coastguard Worker 
296*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_TRUE(FPDF_GetMetaText(document(), "CreationDate", nullptr, 0));
297*3ac0a46fSAndroid Build Coastguard Worker }
298*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,LoadInfoAfterReceivingFirstPage)299*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, LoadInfoAfterReceivingFirstPage) {
300*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("linearized.pdf");
301*3ac0a46fSAndroid Build Coastguard Worker   // Map "Info" to an object within the first section without breaking
302*3ac0a46fSAndroid Build Coastguard Worker   // linearization.
303*3ac0a46fSAndroid Build Coastguard Worker   ByteString data(loader.file_contents(), loader.file_length());
304*3ac0a46fSAndroid Build Coastguard Worker   absl::optional<size_t> index = data.Find("/Info 27 0 R");
305*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(index);
306*3ac0a46fSAndroid Build Coastguard Worker   memcpy(loader.file_contents() + *index, "/Info 29 0 R", 12);
307*3ac0a46fSAndroid Build Coastguard Worker 
308*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
309*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
310*3ac0a46fSAndroid Build Coastguard Worker   while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
311*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
312*3ac0a46fSAndroid Build Coastguard Worker   }
313*3ac0a46fSAndroid Build Coastguard Worker 
314*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
315*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
316*3ac0a46fSAndroid Build Coastguard Worker 
317*3ac0a46fSAndroid Build Coastguard Worker   // The "Info" dictionary should be available for the linearized document, if
318*3ac0a46fSAndroid Build Coastguard Worker   // it is located in the first page section.
319*3ac0a46fSAndroid Build Coastguard Worker   // Info was remapped to a dictionary with Type "Catalog"
320*3ac0a46fSAndroid Build Coastguard Worker   unsigned short buffer[100] = {0};
321*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_TRUE(FPDF_GetMetaText(document(), "Type", buffer, sizeof(buffer)));
322*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(L"Catalog", GetPlatformWString(buffer));
323*3ac0a46fSAndroid Build Coastguard Worker }
324*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,TryLoadInvalidInfo)325*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, TryLoadInvalidInfo) {
326*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("linearized.pdf");
327*3ac0a46fSAndroid Build Coastguard Worker   // Map "Info" to an invalid object without breaking linearization.
328*3ac0a46fSAndroid Build Coastguard Worker   ByteString data(loader.file_contents(), loader.file_length());
329*3ac0a46fSAndroid Build Coastguard Worker   absl::optional<size_t> index = data.Find("/Info 27 0 R");
330*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(index);
331*3ac0a46fSAndroid Build Coastguard Worker   memcpy(loader.file_contents() + *index, "/Info 99 0 R", 12);
332*3ac0a46fSAndroid Build Coastguard Worker 
333*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
334*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
335*3ac0a46fSAndroid Build Coastguard Worker   while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
336*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
337*3ac0a46fSAndroid Build Coastguard Worker   }
338*3ac0a46fSAndroid Build Coastguard Worker 
339*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
340*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
341*3ac0a46fSAndroid Build Coastguard Worker 
342*3ac0a46fSAndroid Build Coastguard Worker   // Set all data available.
343*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(true);
344*3ac0a46fSAndroid Build Coastguard Worker   // Check second page, to load additional crossrefs.
345*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
346*3ac0a46fSAndroid Build Coastguard Worker 
347*3ac0a46fSAndroid Build Coastguard Worker   // Test that api is robust enough to handle the bad case.
348*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0));
349*3ac0a46fSAndroid Build Coastguard Worker }
350*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,TryLoadNonExistsInfo)351*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, TryLoadNonExistsInfo) {
352*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("linearized.pdf");
353*3ac0a46fSAndroid Build Coastguard Worker   // Break the "Info" parameter without breaking linearization.
354*3ac0a46fSAndroid Build Coastguard Worker   ByteString data(loader.file_contents(), loader.file_length());
355*3ac0a46fSAndroid Build Coastguard Worker   absl::optional<size_t> index = data.Find("/Info 27 0 R");
356*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(index);
357*3ac0a46fSAndroid Build Coastguard Worker   memcpy(loader.file_contents() + *index, "/I_fo 27 0 R", 12);
358*3ac0a46fSAndroid Build Coastguard Worker 
359*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(false);
360*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
361*3ac0a46fSAndroid Build Coastguard Worker   while (PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
362*3ac0a46fSAndroid Build Coastguard Worker     loader.FlushRequestedData();
363*3ac0a46fSAndroid Build Coastguard Worker   }
364*3ac0a46fSAndroid Build Coastguard Worker 
365*3ac0a46fSAndroid Build Coastguard Worker   SetDocumentFromAvail();
366*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_TRUE(document());
367*3ac0a46fSAndroid Build Coastguard Worker 
368*3ac0a46fSAndroid Build Coastguard Worker   // Set all data available.
369*3ac0a46fSAndroid Build Coastguard Worker   loader.set_is_new_data_available(true);
370*3ac0a46fSAndroid Build Coastguard Worker   // Check second page, to load additional crossrefs.
371*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
372*3ac0a46fSAndroid Build Coastguard Worker 
373*3ac0a46fSAndroid Build Coastguard Worker   // Test that api is robust enough to handle the bad case.
374*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDF_GetMetaText(document(), "Type", nullptr, 0));
375*3ac0a46fSAndroid Build Coastguard Worker }
376*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,BadInputsToAPIs)377*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, BadInputsToAPIs) {
378*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_DATA_ERROR, FPDFAvail_IsDocAvail(nullptr, nullptr));
379*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_FALSE(FPDFAvail_GetDocument(nullptr, nullptr));
380*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(0, FPDFAvail_GetFirstPageNum(nullptr));
381*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_DATA_ERROR, FPDFAvail_IsPageAvail(nullptr, 0, nullptr));
382*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_FORM_ERROR, FPDFAvail_IsFormAvail(nullptr, nullptr));
383*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_LINEARIZATION_UNKNOWN, FPDFAvail_IsLinearized(nullptr));
384*3ac0a46fSAndroid Build Coastguard Worker }
385*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,NegativePageIndex)386*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, NegativePageIndex) {
387*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("linearized.pdf");
388*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
389*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
390*3ac0a46fSAndroid Build Coastguard Worker   EXPECT_EQ(PDF_DATA_NOTAVAIL,
391*3ac0a46fSAndroid Build Coastguard Worker             FPDFAvail_IsPageAvail(avail(), -1, loader.hints()));
392*3ac0a46fSAndroid Build Coastguard Worker }
393*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,Bug_1324189)394*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, Bug_1324189) {
395*3ac0a46fSAndroid Build Coastguard Worker   // Test passes if it doesn't crash.
396*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("bug_1324189.pdf");
397*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
398*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
399*3ac0a46fSAndroid Build Coastguard Worker }
400*3ac0a46fSAndroid Build Coastguard Worker 
TEST_F(FPDFDataAvailEmbedderTest,Bug_1324503)401*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFDataAvailEmbedderTest, Bug_1324503) {
402*3ac0a46fSAndroid Build Coastguard Worker   // Test passes if it doesn't crash.
403*3ac0a46fSAndroid Build Coastguard Worker   TestAsyncLoader loader("bug_1324503.pdf");
404*3ac0a46fSAndroid Build Coastguard Worker   CreateAvail(loader.file_avail(), loader.file_access());
405*3ac0a46fSAndroid Build Coastguard Worker   ASSERT_EQ(PDF_DATA_NOTAVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
406*3ac0a46fSAndroid Build Coastguard Worker }
407