11#include "core/fxcrt/bytestring.h"
12#include "core/fxcrt/span_util.h"
13#include "public/fpdf_doc.h"
14#include "public/fpdfview.h"
15#include "testing/embedder_test.h"
16#include "testing/fx_string_testhelpers.h"
17#include "testing/gtest/include/gtest/gtest.h"
18#include "testing/range_set.h"
19#include "testing/utils/file_util.h"
20#include "testing/utils/path_service.h"
21#include "third_party/base/numerics/safe_conversions.h"
25class MockDownloadHints
final :
public FX_DOWNLOADHINTS {
27 static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
35 ~MockDownloadHints() =
default;
38class TestAsyncLoader
final :
public FX_DOWNLOADHINTS, FX_FILEAVAIL {
40 explicit TestAsyncLoader(
const std::string& file_name) {
42 if (file_path.empty()) {
45 file_contents_ = GetFileContents(file_path.c_str());
46 if (file_contents_.empty()) {
50 file_access_.m_FileLen =
51 pdfium::base::checked_cast<
unsigned long>(file_contents_.size());
52 file_access_.m_GetBlock = SGetBlock;
53 file_access_.m_Param =
this;
59 FX_FILEAVAIL::IsDataAvail = SIsDataAvail;
62 bool IsOpened()
const {
return !file_contents_.empty(); }
64 FPDF_FILEACCESS* file_access() {
return &file_access_; }
65 FX_DOWNLOADHINTS* hints() {
return this; }
66 FX_FILEAVAIL* file_avail() {
return this; }
68 const std::vector<std::pair<size_t, size_t>>& requested_segments()
const {
69 return requested_segments_;
72 size_t max_requested_bound()
const {
return max_requested_bound_; }
74 void ClearRequestedSegments() {
75 requested_segments_.clear();
76 max_requested_bound_ = 0;
79 bool is_new_data_available()
const {
return is_new_data_available_; }
80 void set_is_new_data_available(
bool is_new_data_available) {
81 is_new_data_available_ = is_new_data_available;
84 size_t max_already_available_bound()
const {
85 return available_ranges_.IsEmpty()
87 : available_ranges_.ranges().rbegin()->second;
90 void FlushRequestedData() {
91 for (
const auto& it : requested_segments_) {
92 SetDataAvailable(it.first, it.second);
94 ClearRequestedSegments();
97 pdfium::span<
const uint8_t> file_contents()
const {
return file_contents_; }
98 pdfium::span<uint8_t> mutable_file_contents() {
return file_contents_; }
101 void SetDataAvailable(size_t start, size_t size) {
102 available_ranges_.Union(RangeSet::Range(start, start + size));
105 bool CheckDataAlreadyAvailable(size_t start, size_t size)
const {
106 return available_ranges_.Contains(RangeSet::Range(start, start + size));
109 int GetBlockImpl(
unsigned long pos,
unsigned char* pBuf,
unsigned long size) {
110 if (!IsDataAvailImpl(pos, size))
112 const unsigned long end = std::min(
113 pdfium::base::checked_cast<
unsigned long>(file_contents_.size()),
117 const unsigned long bytes_to_copy = end - pos;
118 fxcrt::spancpy(pdfium::make_span(pBuf, size),
119 file_contents().subspan(pos, bytes_to_copy));
120 SetDataAvailable(pos, bytes_to_copy);
121 return static_cast<
int>(bytes_to_copy);
124 void AddSegmentImpl(size_t offset, size_t size) {
125 requested_segments_.emplace_back(offset, size);
126 max_requested_bound_ = std::max(max_requested_bound_, offset + size);
129 bool IsDataAvailImpl(size_t offset, size_t size) {
130 if (offset + size > file_contents_.size()) {
133 if (is_new_data_available_) {
134 SetDataAvailable(offset, size);
137 return CheckDataAlreadyAvailable(offset, size);
140 static int SGetBlock(
void* param,
143 unsigned long size) {
144 return static_cast<TestAsyncLoader*>(param)->GetBlockImpl(pos, pBuf, size);
147 static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
148 return static_cast<TestAsyncLoader*>(pThis)->AddSegmentImpl(offset, size);
151 static FPDF_BOOL SIsDataAvail(FX_FILEAVAIL* pThis,
154 return static_cast<TestAsyncLoader*>(pThis)->IsDataAvailImpl(offset, size);
157 FPDF_FILEACCESS file_access_;
159 std::vector<uint8_t> file_contents_;
160 std::vector<std::pair<size_t, size_t>> requested_segments_;
161 size_t max_requested_bound_ = 0;
162 bool is_new_data_available_ =
true;
173 EXPECT_FALSE(OpenDocument(
"trailer_unterminated.pdf"));
174 MockDownloadHints hints;
175 EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
180 EXPECT_FALSE(OpenDocument(
"trailer_as_hexstring.pdf"));
181 MockDownloadHints hints;
182 EXPECT_FALSE(FPDFAvail_IsDocAvail(avail(), &hints));
186 TestAsyncLoader loader(
"feature_linearized_loading.pdf");
187 CreateAvail(loader.file_avail(), loader.file_access());
188 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
189 SetDocumentFromAvail();
190 ASSERT_TRUE(document());
191 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
194 loader.set_is_new_data_available(
false);
195 ScopedFPDFPage page(FPDF_LoadPage(document(), 1));
200 TestAsyncLoader loader(
"feature_linearized_loading.pdf");
201 CreateAvail(loader.file_avail(), loader.file_access());
202 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
203 SetDocumentFromAvail();
204 ASSERT_TRUE(document());
208 loader.set_is_new_data_available(
false);
209 loader.ClearRequestedSegments();
213 loader.FlushRequestedData();
214 status = FPDFAvail_IsFormAvail(avail(), loader.hints());
221 TestAsyncLoader loader(
"feature_linearized_loading.pdf");
222 CreateAvail(loader.file_avail(), loader.file_access());
223 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
224 SetDocumentFromAvail();
225 ASSERT_TRUE(document());
226 const int first_page_num = FPDFAvail_GetFirstPageNum(document());
230 EXPECT_GT(loader.file_access()->m_FileLen,
231 loader.max_already_available_bound());
235 loader.set_is_new_data_available(
false);
236 FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints());
240 EXPECT_GT(loader.file_access()->m_FileLen, loader.max_requested_bound());
243 loader.set_is_new_data_available(
true);
245 FPDFAvail_IsPageAvail(avail(), first_page_num, loader.hints()));
249 EXPECT_GT(loader.file_access()->m_FileLen,
250 loader.max_already_available_bound());
253 loader.set_is_new_data_available(
false);
254 ScopedFPDFPage page(FPDF_LoadPage(document(), first_page_num));
259 TestAsyncLoader loader(
"feature_linearized_loading.pdf");
260 CreateAvail(loader.file_avail(), loader.file_access());
261 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
262 SetDocumentFromAvail();
263 ASSERT_TRUE(document());
265 static constexpr uint32_t kSecondPageNum = 1;
269 loader.set_is_new_data_available(
false);
270 loader.ClearRequestedSegments();
274 loader.FlushRequestedData();
275 status = FPDFAvail_IsPageAvail(avail(), kSecondPageNum, loader.hints());
280 loader.set_is_new_data_available(
false);
281 ScopedFPDFPage page(FPDF_LoadPage(document(), kSecondPageNum));
286 TestAsyncLoader loader(
"linearized.pdf");
287 loader.set_is_new_data_available(
false);
288 CreateAvail(loader.file_avail(), loader.file_access());
289 while (
PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
290 loader.FlushRequestedData();
293 SetDocumentFromAvail();
294 ASSERT_TRUE(document());
297 EXPECT_FALSE(FPDF_GetMetaText(document(),
"CreationDate",
nullptr, 0));
300 loader.set_is_new_data_available(
true);
302 EXPECT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 1, loader.hints()));
304 EXPECT_TRUE(FPDF_GetMetaText(document(),
"CreationDate",
nullptr, 0));
308 TestAsyncLoader loader(
"linearized.pdf");
311 ByteString data(ByteStringView(loader.file_contents()));
312 absl::optional<size_t> index = data.Find(
"/Info 27 0 R");
313 ASSERT_TRUE(index.has_value());
314 auto span = loader.mutable_file_contents().subspan(index.value()).subspan(7);
315 ASSERT_FALSE(span.empty());
316 EXPECT_EQ(
'7', span[0]);
319 loader.set_is_new_data_available(
false);
320 CreateAvail(loader.file_avail(), loader.file_access());
321 while (
PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
322 loader.FlushRequestedData();
325 SetDocumentFromAvail();
326 ASSERT_TRUE(document());
331 unsigned short buffer[100] = {0};
332 EXPECT_TRUE(FPDF_GetMetaText(document(),
"Type", buffer,
sizeof(buffer)));
337 TestAsyncLoader loader(
"linearized.pdf");
339 ByteString data(ByteStringView(loader.file_contents()));
340 absl::optional<size_t> index = data.Find(
"/Info 27 0 R");
341 ASSERT_TRUE(index.has_value());
342 auto span = loader.mutable_file_contents().subspan(index.value()).subspan(6);
343 ASSERT_GE(span.size(), 2u);
344 EXPECT_EQ(
'2', span[0]);
345 EXPECT_EQ(
'7', span[1]);
349 loader.set_is_new_data_available(
false);
350 CreateAvail(loader.file_avail(), loader.file_access());
351 while (
PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
352 loader.FlushRequestedData();
355 SetDocumentFromAvail();
356 ASSERT_TRUE(document());
359 loader.set_is_new_data_available(
true);
361 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
364 EXPECT_FALSE(FPDF_GetMetaText(document(),
"Type",
nullptr, 0));
368 TestAsyncLoader loader(
"linearized.pdf");
370 ByteString data(ByteStringView(loader.file_contents()));
371 absl::optional<size_t> index = data.Find(
"/Info 27 0 R");
372 ASSERT_TRUE(index.has_value());
373 auto span = loader.mutable_file_contents().subspan(index.value()).subspan(2);
374 ASSERT_FALSE(span.empty());
375 EXPECT_EQ(
'n', span[0]);
378 loader.set_is_new_data_available(
false);
379 CreateAvail(loader.file_avail(), loader.file_access());
380 while (
PDF_DATA_AVAIL != FPDFAvail_IsDocAvail(avail(), loader.hints())) {
381 loader.FlushRequestedData();
384 SetDocumentFromAvail();
385 ASSERT_TRUE(document());
388 loader.set_is_new_data_available(
true);
390 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsPageAvail(avail(), 0, loader.hints()));
393 EXPECT_FALSE(FPDF_GetMetaText(document(),
"Type",
nullptr, 0));
406 TestAsyncLoader loader(
"linearized.pdf");
407 CreateAvail(loader.file_avail(), loader.file_access());
408 ASSERT_EQ(
PDF_DATA_AVAIL, FPDFAvail_IsDocAvail(avail(), loader.hints()));
410 FPDFAvail_IsPageAvail(avail(), -1, loader.hints()));
415 TestAsyncLoader loader(
"bug_1324189.pdf");
416 CreateAvail(loader.file_avail(), loader.file_access());
422 TestAsyncLoader loader(
"bug_1324503.pdf");
423 CreateAvail(loader.file_avail(), loader.file_access());
static std::string GetTestFilePath(const std::string &file_name)
#define PDF_FORM_NOTAVAIL
#define PDF_DATA_NOTAVAIL
#define PDF_LINEARIZATION_UNKNOWN
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsFormAvail(FPDF_AVAIL avail, FX_DOWNLOADHINTS *hints)
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsPageAvail(FPDF_AVAIL avail, int page_index, FX_DOWNLOADHINTS *hints)
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password)
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsDocAvail(FPDF_AVAIL avail, FX_DOWNLOADHINTS *hints)
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_GetFirstPageNum(FPDF_DOCUMENT doc)
FPDF_EXPORT int FPDF_CALLCONV FPDFAvail_IsLinearized(FPDF_AVAIL avail)
TEST_F(FPDFDataAvailEmbedderTest, TrailerUnterminated)
std::wstring GetPlatformWString(FPDF_WIDESTRING wstr)
void(* AddSegment)(struct _FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)