blob: 0a2f9447c0ef4f157595e41730ec41bd54fb71d7 [file] [log] [blame]
[email protected]1b1e9eff2014-05-20 01:56:401// Copyright (c) 2010 The Chromium 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
Lei Zhange98901a2018-04-25 23:23:595#include "pdf/document_loader_impl.h"
[email protected]1b1e9eff2014-05-20 01:56:406
avia7c09d52015-12-21 19:49:437#include <stddef.h>
8#include <stdint.h>
9
Artem Stryginaf1b43422017-09-12 18:48:2410#include <algorithm>
Lei Zhang533165f2018-02-27 19:09:1011#include <utility>
Artem Stryginaf1b43422017-09-12 18:48:2412
[email protected]1b1e9eff2014-05-20 01:56:4013#include "base/logging.h"
Artem Stryginaf1b43422017-09-12 18:48:2414#include "base/numerics/safe_math.h"
[email protected]1b1e9eff2014-05-20 01:56:4015#include "base/strings/string_util.h"
Artem Stryginaf1b43422017-09-12 18:48:2416#include "pdf/url_loader_wrapper.h"
[email protected]1b1e9eff2014-05-20 01:56:4017#include "ppapi/c/pp_errors.h"
Artem Stryginaf1b43422017-09-12 18:48:2418#include "ui/gfx/range/range.h"
[email protected]1b1e9eff2014-05-20 01:56:4019
20namespace chrome_pdf {
21
thestig945cd0cb2015-05-28 01:58:0522namespace {
23
Artem Stryginaf1b43422017-09-12 18:48:2424// The distance from last received chunk, when we wait requesting data, using
25// current connection (like playing a cassette tape) and do not send new range
26// request (like rewind a cassette tape, and continue playing after).
27// Experimentally chosen value.
Henrique Nakashima1c5da8e2018-10-05 21:56:4328constexpr int kChunkCloseDistance = 10;
thestig945cd0cb2015-05-28 01:58:0529
raymesa8563ff2016-11-15 23:50:0130// Return true if the HTTP response of |loader| is a successful one and loading
31// should continue. 4xx error indicate subsequent requests will fail too.
32// e.g. resource has been removed from the server while loading it. 301
33// indicates a redirect was returned which won't be successful because we
34// disable following redirects for PDF loading (we assume they are already
35// resolved by the browser.
Artem Stryginaf1b43422017-09-12 18:48:2436bool ResponseStatusSuccess(const URLLoaderWrapper* loader) {
37 int32_t http_code = loader->GetStatusCode();
raymesa8563ff2016-11-15 23:50:0138 return (http_code < 400 && http_code != 301) || http_code >= 500;
39}
40
thestig488102f2015-05-29 03:25:2641bool IsValidContentType(const std::string& type) {
Artem Stryginaf1b43422017-09-12 18:48:2442 return (
43 base::EndsWith(type, "/pdf", base::CompareCase::INSENSITIVE_ASCII) ||
44 base::EndsWith(type, ".pdf", base::CompareCase::INSENSITIVE_ASCII) ||
45 base::EndsWith(type, "/x-pdf", base::CompareCase::INSENSITIVE_ASCII) ||
46 base::EndsWith(type, "/*", base::CompareCase::INSENSITIVE_ASCII) ||
47 base::EndsWith(type, "/octet-stream",
48 base::CompareCase::INSENSITIVE_ASCII) ||
49 base::EndsWith(type, "/acrobat", base::CompareCase::INSENSITIVE_ASCII) ||
50 base::EndsWith(type, "/unknown", base::CompareCase::INSENSITIVE_ASCII));
thestig1d7b16f92017-05-30 20:25:3451}
52
thestig945cd0cb2015-05-28 01:58:0553} // namespace
54
Lei Zhange98901a2018-04-25 23:23:5955DocumentLoaderImpl::Chunk::Chunk() = default;
Artem Stryginaf1b43422017-09-12 18:48:2456
Lei Zhange98901a2018-04-25 23:23:5957DocumentLoaderImpl::Chunk::~Chunk() = default;
Artem Stryginaf1b43422017-09-12 18:48:2458
Lei Zhange98901a2018-04-25 23:23:5959void DocumentLoaderImpl::Chunk::Clear() {
Artem Stryginaf1b43422017-09-12 18:48:2460 chunk_index = 0;
61 data_size = 0;
62 chunk_data.reset();
dsinclair07a72bd2017-08-14 23:27:0463}
Artem Strygin9a6d1482017-07-26 21:08:5664
Lei Zhange98901a2018-04-25 23:23:5965DocumentLoaderImpl::DocumentLoaderImpl(Client* client)
Artem Stryginaf1b43422017-09-12 18:48:2466 : client_(client), loader_factory_(this) {}
67
Lei Zhange98901a2018-04-25 23:23:5968DocumentLoaderImpl::~DocumentLoaderImpl() = default;
[email protected]1b1e9eff2014-05-20 01:56:4069
Lei Zhange98901a2018-04-25 23:23:5970bool DocumentLoaderImpl::Init(std::unique_ptr<URLLoaderWrapper> loader,
71 const std::string& url) {
[email protected]1b1e9eff2014-05-20 01:56:4072 DCHECK(url_.empty());
Artem Stryginaf1b43422017-09-12 18:48:2473 DCHECK(!loader_);
[email protected]1b1e9eff2014-05-20 01:56:4074
raymesa8563ff2016-11-15 23:50:0175 // Check that the initial response status is a valid one.
Artem Stryginaf1b43422017-09-12 18:48:2476 if (!ResponseStatusSuccess(loader.get()))
raymesa8563ff2016-11-15 23:50:0177 return false;
78
Artem Stryginaf1b43422017-09-12 18:48:2479 std::string type = loader->GetContentType();
thestig488102f2015-05-29 03:25:2680
81 // This happens for PDFs not loaded from http(s) sources.
Artem Stryginaf1b43422017-09-12 18:48:2482 if (type == "text/plain") {
brettw95509312015-07-16 23:57:3383 if (!base::StartsWith(url, "http://",
84 base::CompareCase::INSENSITIVE_ASCII) &&
85 !base::StartsWith(url, "https://",
86 base::CompareCase::INSENSITIVE_ASCII)) {
thestig488102f2015-05-29 03:25:2687 type = "application/pdf";
88 }
89 }
thestig488102f2015-05-29 03:25:2690 if (!type.empty() && !IsValidContentType(type))
[email protected]1b1e9eff2014-05-20 01:56:4091 return false;
Artem Stryginaf1b43422017-09-12 18:48:2492
93 if (base::StartsWith(loader->GetContentDisposition(), "attachment",
brettw95509312015-07-16 23:57:3394 base::CompareCase::INSENSITIVE_ASCII))
[email protected]1b1e9eff2014-05-20 01:56:4095 return false;
[email protected]1b1e9eff2014-05-20 01:56:4096
Artem Stryginaf1b43422017-09-12 18:48:2497 url_ = url;
98 loader_ = std::move(loader);
[email protected]1b1e9eff2014-05-20 01:56:4099
Lei Zhang6fbfb092018-03-28 16:58:56100 if (!loader_->IsContentEncoded())
101 SetDocumentSize(std::max(0, loader_->GetContentLength()));
102
Artem Stryginaf1b43422017-09-12 18:48:24103 int64_t bytes_received = 0;
104 int64_t total_bytes_to_be_received = 0;
Lei Zhang6fbfb092018-03-28 16:58:56105 if (GetDocumentSize() == 0 &&
Artem Stryginaf1b43422017-09-12 18:48:24106 loader_->GetDownloadProgress(&bytes_received,
107 &total_bytes_to_be_received)) {
Lei Zhang6fbfb092018-03-28 16:58:56108 SetDocumentSize(std::max(0, static_cast<int>(total_bytes_to_be_received)));
Artem Stryginaf1b43422017-09-12 18:48:24109 }
110
111 SetPartialLoadingEnabled(
112 partial_loading_enabled_ &&
113 !base::StartsWith(url, "file://", base::CompareCase::INSENSITIVE_ASCII) &&
114 loader_->IsAcceptRangesBytes() && !loader_->IsContentEncoded() &&
115 GetDocumentSize());
116
117 ReadMore();
[email protected]1b1e9eff2014-05-20 01:56:40118 return true;
119}
120
Lei Zhange98901a2018-04-25 23:23:59121bool DocumentLoaderImpl::IsDocumentComplete() const {
Artem Stryginaf1b43422017-09-12 18:48:24122 return chunk_stream_.IsComplete();
dsinclair07a72bd2017-08-14 23:27:04123}
124
Lei Zhange98901a2018-04-25 23:23:59125void DocumentLoaderImpl::SetDocumentSize(uint32_t size) {
Lei Zhang6fbfb092018-03-28 16:58:56126 chunk_stream_.set_eof_pos(size);
127}
128
Lei Zhange98901a2018-04-25 23:23:59129uint32_t DocumentLoaderImpl::GetDocumentSize() const {
Artem Stryginaf1b43422017-09-12 18:48:24130 return chunk_stream_.eof_pos();
[email protected]1b1e9eff2014-05-20 01:56:40131}
132
Lei Zhange98901a2018-04-25 23:23:59133uint32_t DocumentLoaderImpl::BytesReceived() const {
134 return bytes_received_;
135}
136
137void DocumentLoaderImpl::ClearPendingRequests() {
Artem Stryginaf1b43422017-09-12 18:48:24138 pending_requests_.Clear();
[email protected]1b1e9eff2014-05-20 01:56:40139}
140
Lei Zhange98901a2018-04-25 23:23:59141bool DocumentLoaderImpl::GetBlock(uint32_t position,
142 uint32_t size,
143 void* buf) const {
Artem Stryginaf1b43422017-09-12 18:48:24144 base::CheckedNumeric<uint32_t> addition_result = position;
145 addition_result += size;
146 if (!addition_result.IsValid())
147 return false;
148 return chunk_stream_.ReadData(
149 gfx::Range(position, addition_result.ValueOrDie()), buf);
[email protected]1b1e9eff2014-05-20 01:56:40150}
151
Lei Zhange98901a2018-04-25 23:23:59152bool DocumentLoaderImpl::IsDataAvailable(uint32_t position,
153 uint32_t size) const {
Artem Stryginaf1b43422017-09-12 18:48:24154 base::CheckedNumeric<uint32_t> addition_result = position;
155 addition_result += size;
156 if (!addition_result.IsValid())
157 return false;
158 return chunk_stream_.IsRangeAvailable(
159 gfx::Range(position, addition_result.ValueOrDie()));
[email protected]1b1e9eff2014-05-20 01:56:40160}
161
Lei Zhange98901a2018-04-25 23:23:59162void DocumentLoaderImpl::RequestData(uint32_t position, uint32_t size) {
Lei Zhang6fbfb092018-03-28 16:58:56163 if (size == 0 || IsDataAvailable(position, size))
Artem Stryginaf1b43422017-09-12 18:48:24164 return;
Lei Zhang6fbfb092018-03-28 16:58:56165
166 const uint32_t document_size = GetDocumentSize();
167 if (document_size != 0) {
168 // Check for integer overflow.
Artem Stryginaf1b43422017-09-12 18:48:24169 base::CheckedNumeric<uint32_t> addition_result = position;
170 addition_result += size;
171 if (!addition_result.IsValid())
172 return;
Lei Zhang6fbfb092018-03-28 16:58:56173
174 if (addition_result.ValueOrDie() > document_size)
175 return;
Artem Stryginaf1b43422017-09-12 18:48:24176 }
177
Lei Zhang6fbfb092018-03-28 16:58:56178 // We have some artifact request from
[email protected]1b1e9eff2014-05-20 01:56:40179 // PDFiumEngine::OnDocumentComplete() -> FPDFAvail_IsPageAvail after
180 // document is complete.
181 // We need this fix in PDFIum. Adding this as a work around.
182 // Bug: http://code.google.com/p/chromium/issues/detail?id=79996
183 // Test url:
184 // http://www.icann.org/en/correspondence/holtzman-to-jeffrey-02mar11-en.pdf
Artem Stryginaf1b43422017-09-12 18:48:24185 if (!loader_)
[email protected]1b1e9eff2014-05-20 01:56:40186 return;
187
Artem Stryginaf1b43422017-09-12 18:48:24188 RangeSet requested_chunks(chunk_stream_.GetChunksRange(position, size));
189 requested_chunks.Subtract(chunk_stream_.filled_chunks());
190 if (requested_chunks.IsEmpty()) {
191 NOTREACHED();
192 return;
193 }
194 pending_requests_.Union(requested_chunks);
dsinclair07a72bd2017-08-14 23:27:04195}
196
Lei Zhange98901a2018-04-25 23:23:59197void DocumentLoaderImpl::SetPartialLoadingEnabled(bool enabled) {
Artem Stryginaf1b43422017-09-12 18:48:24198 partial_loading_enabled_ = enabled;
199 if (!enabled) {
200 is_partial_loader_active_ = false;
dsinclair07a72bd2017-08-14 23:27:04201 }
202}
203
Lei Zhange98901a2018-04-25 23:23:59204bool DocumentLoaderImpl::ShouldCancelLoading() const {
Artem Stryginaf1b43422017-09-12 18:48:24205 if (!loader_)
206 return true;
Artem Strygin2cf20af42018-06-14 14:26:53207
208 if (!partial_loading_enabled_)
Artem Stryginaf1b43422017-09-12 18:48:24209 return false;
Artem Strygin2cf20af42018-06-14 14:26:53210
211 if (pending_requests_.IsEmpty()) {
212 // Cancel loading if this is unepected data from server.
213 return !chunk_stream_.IsValidChunkIndex(chunk_.chunk_index) ||
214 chunk_stream_.IsChunkAvailable(chunk_.chunk_index);
215 }
216
Artem Stryginaf1b43422017-09-12 18:48:24217 const gfx::Range current_range(chunk_.chunk_index,
218 chunk_.chunk_index + kChunkCloseDistance);
219 return !pending_requests_.Intersects(current_range);
220}
art-snake3704ccf02016-11-03 02:13:20221
Lei Zhange98901a2018-04-25 23:23:59222void DocumentLoaderImpl::ContinueDownload() {
Artem Stryginaf1b43422017-09-12 18:48:24223 if (!ShouldCancelLoading())
224 return ReadMore();
Lei Zhang6fbfb092018-03-28 16:58:56225
Artem Stryginaf1b43422017-09-12 18:48:24226 DCHECK(partial_loading_enabled_);
227 DCHECK(!IsDocumentComplete());
Lei Zhang6fbfb092018-03-28 16:58:56228 DCHECK_GT(GetDocumentSize(), 0U);
Artem Strygin9a6d1482017-07-26 21:08:56229
Artem Stryginaf1b43422017-09-12 18:48:24230 const uint32_t range_start =
231 pending_requests_.IsEmpty() ? 0 : pending_requests_.First().start();
232 RangeSet candidates_for_request(
233 gfx::Range(range_start, chunk_stream_.total_chunks_count()));
234 candidates_for_request.Subtract(chunk_stream_.filled_chunks());
235 DCHECK(!candidates_for_request.IsEmpty());
236 gfx::Range next_request = candidates_for_request.First();
237 if (candidates_for_request.Size() == 1 &&
238 next_request.length() < kChunkCloseDistance) {
239 // We have only request at the end, try to enlarge it to improve back order
240 // reading.
241 const int additional_chunks_count =
242 kChunkCloseDistance - next_request.length();
243 int new_start = std::max(
244 0, static_cast<int>(next_request.start()) - additional_chunks_count);
245 candidates_for_request =
246 RangeSet(gfx::Range(new_start, next_request.end()));
247 candidates_for_request.Subtract(chunk_stream_.filled_chunks());
248 next_request = candidates_for_request.Last();
dsinclairadf55f3f2016-12-07 14:00:41249 }
gene7cafb2ce62014-10-24 00:56:53250
Artem Stryginaf1b43422017-09-12 18:48:24251 loader_.reset();
252 chunk_.Clear();
Robbie McElrath44e2d302019-07-15 23:28:55253 is_partial_loader_active_ = true;
dsinclairadf55f3f2016-12-07 14:00:41254
Artem Stryginaf1b43422017-09-12 18:48:24255 const uint32_t start = next_request.start() * DataStream::kChunkSize;
256 const uint32_t length =
Lei Zhang6fbfb092018-03-28 16:58:56257 std::min(GetDocumentSize() - start,
Artem Stryginaf1b43422017-09-12 18:48:24258 next_request.length() * DataStream::kChunkSize);
dsinclairadf55f3f2016-12-07 14:00:41259
Artem Strygin9a6d1482017-07-26 21:08:56260 loader_ = client_->CreateURLLoader();
Artem Stryginaf1b43422017-09-12 18:48:24261
262 loader_->OpenRange(
263 url_, url_, start, length,
Lei Zhange98901a2018-04-25 23:23:59264 loader_factory_.NewCallback(&DocumentLoaderImpl::DidOpenPartial));
Artem Strygin9a6d1482017-07-26 21:08:56265}
266
Lei Zhange98901a2018-04-25 23:23:59267void DocumentLoaderImpl::DidOpenPartial(int32_t result) {
Artem Strygin9a6d1482017-07-26 21:08:56268 if (result != PP_OK) {
Artem Stryginaf1b43422017-09-12 18:48:24269 return ReadComplete();
Artem Strygin9a6d1482017-07-26 21:08:56270 }
271
Artem Stryginaf1b43422017-09-12 18:48:24272 if (!ResponseStatusSuccess(loader_.get()))
273 return ReadComplete();
Artem Strygin9a6d1482017-07-26 21:08:56274
Artem Stryginaf1b43422017-09-12 18:48:24275 // Leave position untouched for multiparted responce for now, when we read the
276 // data we'll get it.
Lei Zhang02a3a0f2017-12-11 20:25:48277 if (loader_->IsMultipart()) {
278 // Needs more data to calc chunk index.
279 return ReadMore();
[email protected]1b1e9eff2014-05-20 01:56:40280 }
Lei Zhang02a3a0f2017-12-11 20:25:48281
282 // Need to make sure that the server returned a byte-range, since it's
283 // possible for a server to just ignore our byte-range request and just
284 // return the entire document even if it supports byte-range requests.
285 // i.e. sniff response to
286 // http://www.act.org/compass/sample/pdf/geometry.pdf
287 int start_pos = 0;
288 if (loader_->GetByteRangeStart(&start_pos)) {
289 if (start_pos % DataStream::kChunkSize != 0)
290 return ReadComplete();
291
292 DCHECK(!chunk_.chunk_data);
293 chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
294 } else {
295 SetPartialLoadingEnabled(false);
296 }
297 return ContinueDownload();
[email protected]1b1e9eff2014-05-20 01:56:40298}
299
Lei Zhange98901a2018-04-25 23:23:59300void DocumentLoaderImpl::ReadMore() {
Artem Stryginaf1b43422017-09-12 18:48:24301 loader_->ReadResponseBody(
302 buffer_, sizeof(buffer_),
Lei Zhange98901a2018-04-25 23:23:59303 loader_factory_.NewCallback(&DocumentLoaderImpl::DidRead));
[email protected]1b1e9eff2014-05-20 01:56:40304}
305
Lei Zhange98901a2018-04-25 23:23:59306void DocumentLoaderImpl::DidRead(int32_t result) {
Artem Stryginaf1b43422017-09-12 18:48:24307 if (result < 0) {
308 // An error occurred.
309 // The renderer will detect that we're missing data and will display a
310 // message.
311 return ReadComplete();
[email protected]1b1e9eff2014-05-20 01:56:40312 }
Artem Stryginaf1b43422017-09-12 18:48:24313 if (result == 0) {
314 loader_.reset();
315 if (!is_partial_loader_active_)
316 return ReadComplete();
317 return ContinueDownload();
318 }
319 if (loader_->IsMultipart()) {
320 int start_pos = 0;
Lei Zhang02a3a0f2017-12-11 20:25:48321 if (!loader_->GetByteRangeStart(&start_pos))
Artem Stryginaf1b43422017-09-12 18:48:24322 return ReadComplete();
Lei Zhang02a3a0f2017-12-11 20:25:48323
Artem Stryginaf1b43422017-09-12 18:48:24324 DCHECK(!chunk_.chunk_data);
325 chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
spelchat5efbaf822016-05-24 23:29:56326 }
Lei Zhang32094aac2018-03-28 18:29:15327 if (!SaveBuffer(buffer_, result))
Artem Stryginaf1b43422017-09-12 18:48:24328 return ReadMore();
Lei Zhang02a3a0f2017-12-11 20:25:48329 if (IsDocumentComplete())
Artem Stryginaf1b43422017-09-12 18:48:24330 return ReadComplete();
Artem Stryginaf1b43422017-09-12 18:48:24331 return ContinueDownload();
[email protected]1b1e9eff2014-05-20 01:56:40332}
333
Lei Zhange98901a2018-04-25 23:23:59334bool DocumentLoaderImpl::SaveBuffer(char* input, uint32_t input_size) {
Lei Zhang32094aac2018-03-28 18:29:15335 const uint32_t document_size = GetDocumentSize();
Lei Zhang6fbfb092018-03-28 16:58:56336 bytes_received_ += input_size;
Artem Stryginaf1b43422017-09-12 18:48:24337 bool chunk_saved = false;
338 bool loading_pending_request = pending_requests_.Contains(chunk_.chunk_index);
339 while (input_size > 0) {
Lei Zhang6fbfb092018-03-28 16:58:56340 if (chunk_.data_size == 0)
Lei Zhang64aa552c2017-10-17 18:35:53341 chunk_.chunk_data = std::make_unique<DataStream::ChunkData>();
Lei Zhang6fbfb092018-03-28 16:58:56342
Artem Stryginaf1b43422017-09-12 18:48:24343 const uint32_t new_chunk_data_len =
344 std::min(DataStream::kChunkSize - chunk_.data_size, input_size);
345 memcpy(chunk_.chunk_data->data() + chunk_.data_size, input,
346 new_chunk_data_len);
347 chunk_.data_size += new_chunk_data_len;
348 if (chunk_.data_size == DataStream::kChunkSize ||
Artem Strygin2cf20af42018-06-14 14:26:53349 (document_size > 0 && document_size <= EndOfCurrentChunk())) {
Artem Stryginaf1b43422017-09-12 18:48:24350 pending_requests_.Subtract(
351 gfx::Range(chunk_.chunk_index, chunk_.chunk_index + 1));
Lei Zhang32094aac2018-03-28 18:29:15352 SaveChunkData();
Artem Stryginaf1b43422017-09-12 18:48:24353 chunk_saved = true;
354 }
355
356 input += new_chunk_data_len;
357 input_size -= new_chunk_data_len;
358 }
359
360 client_->OnNewDataReceived();
361
362 if (IsDocumentComplete())
363 return true;
364
365 if (!chunk_saved)
366 return false;
367
368 if (loading_pending_request &&
369 !pending_requests_.Contains(chunk_.chunk_index)) {
370 client_->OnPendingRequestComplete();
371 }
372 return true;
spelchat3ba2a2812015-12-10 00:44:15373}
374
Lei Zhange98901a2018-04-25 23:23:59375void DocumentLoaderImpl::SaveChunkData() {
Lei Zhang32094aac2018-03-28 18:29:15376 chunk_stream_.SetChunkData(chunk_.chunk_index, std::move(chunk_.chunk_data));
377 chunk_.data_size = 0;
378 ++chunk_.chunk_index;
379}
380
Lei Zhange98901a2018-04-25 23:23:59381uint32_t DocumentLoaderImpl::EndOfCurrentChunk() const {
Lei Zhang6fbfb092018-03-28 16:58:56382 return chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size;
383}
384
Lei Zhange98901a2018-04-25 23:23:59385void DocumentLoaderImpl::ReadComplete() {
Lei Zhang32094aac2018-03-28 18:29:15386 if (GetDocumentSize() != 0) {
387 // If there is remaining data in |chunk_|, then save whatever can be saved.
388 // e.g. In the underrun case.
389 if (chunk_.data_size != 0)
390 SaveChunkData();
391 } else {
Lei Zhang6fbfb092018-03-28 16:58:56392 uint32_t eof = EndOfCurrentChunk();
Artem Stryginaf1b43422017-09-12 18:48:24393 if (!chunk_stream_.filled_chunks().IsEmpty()) {
394 eof = std::max(
395 chunk_stream_.filled_chunks().Last().end() * DataStream::kChunkSize,
396 eof);
[email protected]1b1e9eff2014-05-20 01:56:40397 }
Lei Zhang6fbfb092018-03-28 16:58:56398 SetDocumentSize(eof);
Lei Zhang32094aac2018-03-28 18:29:15399 if (eof == EndOfCurrentChunk())
400 SaveChunkData();
[email protected]1b1e9eff2014-05-20 01:56:40401 }
Artem Stryginaf1b43422017-09-12 18:48:24402 loader_.reset();
[email protected]1b1e9eff2014-05-20 01:56:40403 if (IsDocumentComplete()) {
404 client_->OnDocumentComplete();
Artem Stryginaf1b43422017-09-12 18:48:24405 } else {
406 client_->OnDocumentCanceled();
[email protected]1b1e9eff2014-05-20 01:56:40407 }
[email protected]1b1e9eff2014-05-20 01:56:40408}
409
410} // namespace chrome_pdf