blob: 3f403d08012417f9b849b4382aafc452fe400faa [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
K. Moon544e09532020-07-07 17:08:5513#include "base/bind.h"
14#include "base/callback.h"
Hans Wennborg5078d102020-04-29 18:26:4615#include "base/check_op.h"
K. Moonf2a14142020-08-12 19:39:4016#include "base/feature_list.h"
Hans Wennborg5078d102020-04-29 18:26:4617#include "base/notreached.h"
Artem Stryginaf1b43422017-09-12 18:48:2418#include "base/numerics/safe_math.h"
[email protected]1b1e9eff2014-05-20 01:56:4019#include "base/strings/string_util.h"
K. Moonf2a14142020-08-12 19:39:4020#include "pdf/pdf_features.h"
Artem Stryginaf1b43422017-09-12 18:48:2421#include "pdf/url_loader_wrapper.h"
[email protected]1b1e9eff2014-05-20 01:56:4022#include "ppapi/c/pp_errors.h"
Artem Stryginaf1b43422017-09-12 18:48:2423#include "ui/gfx/range/range.h"
[email protected]1b1e9eff2014-05-20 01:56:4024
25namespace chrome_pdf {
26
thestig945cd0cb2015-05-28 01:58:0527namespace {
28
Artem Stryginaf1b43422017-09-12 18:48:2429// The distance from last received chunk, when we wait requesting data, using
30// current connection (like playing a cassette tape) and do not send new range
31// request (like rewind a cassette tape, and continue playing after).
32// Experimentally chosen value.
Henrique Nakashima1c5da8e2018-10-05 21:56:4333constexpr int kChunkCloseDistance = 10;
thestig945cd0cb2015-05-28 01:58:0534
raymesa8563ff2016-11-15 23:50:0135// Return true if the HTTP response of |loader| is a successful one and loading
36// should continue. 4xx error indicate subsequent requests will fail too.
37// e.g. resource has been removed from the server while loading it. 301
38// indicates a redirect was returned which won't be successful because we
39// disable following redirects for PDF loading (we assume they are already
40// resolved by the browser.
Artem Stryginaf1b43422017-09-12 18:48:2441bool ResponseStatusSuccess(const URLLoaderWrapper* loader) {
42 int32_t http_code = loader->GetStatusCode();
raymesa8563ff2016-11-15 23:50:0143 return (http_code < 400 && http_code != 301) || http_code >= 500;
44}
45
thestig488102f2015-05-29 03:25:2646bool IsValidContentType(const std::string& type) {
Artem Stryginaf1b43422017-09-12 18:48:2447 return (
48 base::EndsWith(type, "/pdf", base::CompareCase::INSENSITIVE_ASCII) ||
49 base::EndsWith(type, ".pdf", base::CompareCase::INSENSITIVE_ASCII) ||
50 base::EndsWith(type, "/x-pdf", base::CompareCase::INSENSITIVE_ASCII) ||
51 base::EndsWith(type, "/*", base::CompareCase::INSENSITIVE_ASCII) ||
52 base::EndsWith(type, "/octet-stream",
53 base::CompareCase::INSENSITIVE_ASCII) ||
54 base::EndsWith(type, "/acrobat", base::CompareCase::INSENSITIVE_ASCII) ||
55 base::EndsWith(type, "/unknown", base::CompareCase::INSENSITIVE_ASCII));
thestig1d7b16f92017-05-30 20:25:3456}
57
thestig945cd0cb2015-05-28 01:58:0558} // namespace
59
Lei Zhange98901a2018-04-25 23:23:5960DocumentLoaderImpl::Chunk::Chunk() = default;
Artem Stryginaf1b43422017-09-12 18:48:2461
Lei Zhange98901a2018-04-25 23:23:5962DocumentLoaderImpl::Chunk::~Chunk() = default;
Artem Stryginaf1b43422017-09-12 18:48:2463
Lei Zhange98901a2018-04-25 23:23:5964void DocumentLoaderImpl::Chunk::Clear() {
Artem Stryginaf1b43422017-09-12 18:48:2465 chunk_index = 0;
66 data_size = 0;
67 chunk_data.reset();
dsinclair07a72bd2017-08-14 23:27:0468}
Artem Strygin9a6d1482017-07-26 21:08:5669
K. Moonf2a14142020-08-12 19:39:4070DocumentLoaderImpl::DocumentLoaderImpl(Client* client)
71 : client_(client),
72 partial_loading_enabled_(
73 base::FeatureList::IsEnabled(features::kPdfPartialLoading)) {}
Artem Stryginaf1b43422017-09-12 18:48:2474
Lei Zhange98901a2018-04-25 23:23:5975DocumentLoaderImpl::~DocumentLoaderImpl() = default;
[email protected]1b1e9eff2014-05-20 01:56:4076
Lei Zhange98901a2018-04-25 23:23:5977bool DocumentLoaderImpl::Init(std::unique_ptr<URLLoaderWrapper> loader,
78 const std::string& url) {
[email protected]1b1e9eff2014-05-20 01:56:4079 DCHECK(url_.empty());
Artem Stryginaf1b43422017-09-12 18:48:2480 DCHECK(!loader_);
[email protected]1b1e9eff2014-05-20 01:56:4081
raymesa8563ff2016-11-15 23:50:0182 // Check that the initial response status is a valid one.
Artem Stryginaf1b43422017-09-12 18:48:2483 if (!ResponseStatusSuccess(loader.get()))
raymesa8563ff2016-11-15 23:50:0184 return false;
85
Artem Stryginaf1b43422017-09-12 18:48:2486 std::string type = loader->GetContentType();
thestig488102f2015-05-29 03:25:2687
88 // This happens for PDFs not loaded from http(s) sources.
Artem Stryginaf1b43422017-09-12 18:48:2489 if (type == "text/plain") {
brettw95509312015-07-16 23:57:3390 if (!base::StartsWith(url, "http://",
91 base::CompareCase::INSENSITIVE_ASCII) &&
92 !base::StartsWith(url, "https://",
93 base::CompareCase::INSENSITIVE_ASCII)) {
thestig488102f2015-05-29 03:25:2694 type = "application/pdf";
95 }
96 }
thestig488102f2015-05-29 03:25:2697 if (!type.empty() && !IsValidContentType(type))
[email protected]1b1e9eff2014-05-20 01:56:4098 return false;
Artem Stryginaf1b43422017-09-12 18:48:2499
100 if (base::StartsWith(loader->GetContentDisposition(), "attachment",
brettw95509312015-07-16 23:57:33101 base::CompareCase::INSENSITIVE_ASCII))
[email protected]1b1e9eff2014-05-20 01:56:40102 return false;
[email protected]1b1e9eff2014-05-20 01:56:40103
Artem Stryginaf1b43422017-09-12 18:48:24104 url_ = url;
105 loader_ = std::move(loader);
[email protected]1b1e9eff2014-05-20 01:56:40106
Lei Zhang6fbfb092018-03-28 16:58:56107 if (!loader_->IsContentEncoded())
K. Moon6f6c6e42020-03-19 04:21:53108 chunk_stream_.set_eof_pos(std::max(0, loader_->GetContentLength()));
Lei Zhang6fbfb092018-03-28 16:58:56109
Artem Stryginaf1b43422017-09-12 18:48:24110 SetPartialLoadingEnabled(
111 partial_loading_enabled_ &&
112 !base::StartsWith(url, "file://", base::CompareCase::INSENSITIVE_ASCII) &&
113 loader_->IsAcceptRangesBytes() && !loader_->IsContentEncoded() &&
114 GetDocumentSize());
115
116 ReadMore();
[email protected]1b1e9eff2014-05-20 01:56:40117 return true;
118}
119
Lei Zhange98901a2018-04-25 23:23:59120bool DocumentLoaderImpl::IsDocumentComplete() const {
Artem Stryginaf1b43422017-09-12 18:48:24121 return chunk_stream_.IsComplete();
dsinclair07a72bd2017-08-14 23:27:04122}
123
Lei Zhange98901a2018-04-25 23:23:59124uint32_t DocumentLoaderImpl::GetDocumentSize() const {
Artem Stryginaf1b43422017-09-12 18:48:24125 return chunk_stream_.eof_pos();
[email protected]1b1e9eff2014-05-20 01:56:40126}
127
Lei Zhange98901a2018-04-25 23:23:59128uint32_t DocumentLoaderImpl::BytesReceived() const {
129 return bytes_received_;
130}
131
132void DocumentLoaderImpl::ClearPendingRequests() {
Artem Stryginaf1b43422017-09-12 18:48:24133 pending_requests_.Clear();
[email protected]1b1e9eff2014-05-20 01:56:40134}
135
Lei Zhange98901a2018-04-25 23:23:59136bool DocumentLoaderImpl::GetBlock(uint32_t position,
137 uint32_t size,
138 void* buf) const {
Artem Stryginaf1b43422017-09-12 18:48:24139 base::CheckedNumeric<uint32_t> addition_result = position;
140 addition_result += size;
141 if (!addition_result.IsValid())
142 return false;
143 return chunk_stream_.ReadData(
144 gfx::Range(position, addition_result.ValueOrDie()), buf);
[email protected]1b1e9eff2014-05-20 01:56:40145}
146
Lei Zhange98901a2018-04-25 23:23:59147bool DocumentLoaderImpl::IsDataAvailable(uint32_t position,
148 uint32_t size) const {
Artem Stryginaf1b43422017-09-12 18:48:24149 base::CheckedNumeric<uint32_t> addition_result = position;
150 addition_result += size;
151 if (!addition_result.IsValid())
152 return false;
153 return chunk_stream_.IsRangeAvailable(
154 gfx::Range(position, addition_result.ValueOrDie()));
[email protected]1b1e9eff2014-05-20 01:56:40155}
156
Lei Zhange98901a2018-04-25 23:23:59157void DocumentLoaderImpl::RequestData(uint32_t position, uint32_t size) {
Lei Zhang6fbfb092018-03-28 16:58:56158 if (size == 0 || IsDataAvailable(position, size))
Artem Stryginaf1b43422017-09-12 18:48:24159 return;
Lei Zhang6fbfb092018-03-28 16:58:56160
161 const uint32_t document_size = GetDocumentSize();
162 if (document_size != 0) {
163 // Check for integer overflow.
Artem Stryginaf1b43422017-09-12 18:48:24164 base::CheckedNumeric<uint32_t> addition_result = position;
165 addition_result += size;
166 if (!addition_result.IsValid())
167 return;
Lei Zhang6fbfb092018-03-28 16:58:56168
169 if (addition_result.ValueOrDie() > document_size)
170 return;
Artem Stryginaf1b43422017-09-12 18:48:24171 }
172
Lei Zhang6fbfb092018-03-28 16:58:56173 // We have some artifact request from
[email protected]1b1e9eff2014-05-20 01:56:40174 // PDFiumEngine::OnDocumentComplete() -> FPDFAvail_IsPageAvail after
175 // document is complete.
176 // We need this fix in PDFIum. Adding this as a work around.
177 // Bug: http://code.google.com/p/chromium/issues/detail?id=79996
178 // Test url:
179 // http://www.icann.org/en/correspondence/holtzman-to-jeffrey-02mar11-en.pdf
Artem Stryginaf1b43422017-09-12 18:48:24180 if (!loader_)
[email protected]1b1e9eff2014-05-20 01:56:40181 return;
182
Artem Stryginaf1b43422017-09-12 18:48:24183 RangeSet requested_chunks(chunk_stream_.GetChunksRange(position, size));
184 requested_chunks.Subtract(chunk_stream_.filled_chunks());
185 if (requested_chunks.IsEmpty()) {
186 NOTREACHED();
187 return;
188 }
189 pending_requests_.Union(requested_chunks);
dsinclair07a72bd2017-08-14 23:27:04190}
191
Lei Zhange98901a2018-04-25 23:23:59192void DocumentLoaderImpl::SetPartialLoadingEnabled(bool enabled) {
Artem Stryginaf1b43422017-09-12 18:48:24193 partial_loading_enabled_ = enabled;
194 if (!enabled) {
195 is_partial_loader_active_ = false;
dsinclair07a72bd2017-08-14 23:27:04196 }
197}
198
Lei Zhange98901a2018-04-25 23:23:59199bool DocumentLoaderImpl::ShouldCancelLoading() const {
Artem Stryginaf1b43422017-09-12 18:48:24200 if (!loader_)
201 return true;
Artem Strygin2cf20af42018-06-14 14:26:53202
203 if (!partial_loading_enabled_)
Artem Stryginaf1b43422017-09-12 18:48:24204 return false;
Artem Strygin2cf20af42018-06-14 14:26:53205
206 if (pending_requests_.IsEmpty()) {
207 // Cancel loading if this is unepected data from server.
208 return !chunk_stream_.IsValidChunkIndex(chunk_.chunk_index) ||
209 chunk_stream_.IsChunkAvailable(chunk_.chunk_index);
210 }
211
Artem Stryginaf1b43422017-09-12 18:48:24212 const gfx::Range current_range(chunk_.chunk_index,
213 chunk_.chunk_index + kChunkCloseDistance);
214 return !pending_requests_.Intersects(current_range);
215}
art-snake3704ccf02016-11-03 02:13:20216
Lei Zhange98901a2018-04-25 23:23:59217void DocumentLoaderImpl::ContinueDownload() {
Artem Stryginaf1b43422017-09-12 18:48:24218 if (!ShouldCancelLoading())
219 return ReadMore();
Lei Zhang6fbfb092018-03-28 16:58:56220
Artem Stryginaf1b43422017-09-12 18:48:24221 DCHECK(partial_loading_enabled_);
222 DCHECK(!IsDocumentComplete());
Lei Zhang6fbfb092018-03-28 16:58:56223 DCHECK_GT(GetDocumentSize(), 0U);
Artem Strygin9a6d1482017-07-26 21:08:56224
Artem Stryginaf1b43422017-09-12 18:48:24225 const uint32_t range_start =
226 pending_requests_.IsEmpty() ? 0 : pending_requests_.First().start();
227 RangeSet candidates_for_request(
228 gfx::Range(range_start, chunk_stream_.total_chunks_count()));
229 candidates_for_request.Subtract(chunk_stream_.filled_chunks());
230 DCHECK(!candidates_for_request.IsEmpty());
231 gfx::Range next_request = candidates_for_request.First();
232 if (candidates_for_request.Size() == 1 &&
233 next_request.length() < kChunkCloseDistance) {
234 // We have only request at the end, try to enlarge it to improve back order
235 // reading.
236 const int additional_chunks_count =
237 kChunkCloseDistance - next_request.length();
238 int new_start = std::max(
239 0, static_cast<int>(next_request.start()) - additional_chunks_count);
240 candidates_for_request =
241 RangeSet(gfx::Range(new_start, next_request.end()));
242 candidates_for_request.Subtract(chunk_stream_.filled_chunks());
243 next_request = candidates_for_request.Last();
dsinclairadf55f3f2016-12-07 14:00:41244 }
gene7cafb2ce62014-10-24 00:56:53245
Artem Stryginaf1b43422017-09-12 18:48:24246 loader_.reset();
247 chunk_.Clear();
Robbie McElrath44e2d302019-07-15 23:28:55248 is_partial_loader_active_ = true;
dsinclairadf55f3f2016-12-07 14:00:41249
Artem Stryginaf1b43422017-09-12 18:48:24250 const uint32_t start = next_request.start() * DataStream::kChunkSize;
251 const uint32_t length =
Lei Zhang6fbfb092018-03-28 16:58:56252 std::min(GetDocumentSize() - start,
Artem Stryginaf1b43422017-09-12 18:48:24253 next_request.length() * DataStream::kChunkSize);
dsinclairadf55f3f2016-12-07 14:00:41254
Artem Strygin9a6d1482017-07-26 21:08:56255 loader_ = client_->CreateURLLoader();
Artem Stryginaf1b43422017-09-12 18:48:24256
K. Moon544e09532020-07-07 17:08:55257 loader_->OpenRange(url_, url_, start, length,
258 base::BindOnce(&DocumentLoaderImpl::DidOpenPartial,
259 weak_factory_.GetWeakPtr()));
Artem Strygin9a6d1482017-07-26 21:08:56260}
261
Lei Zhange98901a2018-04-25 23:23:59262void DocumentLoaderImpl::DidOpenPartial(int32_t result) {
Artem Strygin9a6d1482017-07-26 21:08:56263 if (result != PP_OK) {
Artem Stryginaf1b43422017-09-12 18:48:24264 return ReadComplete();
Artem Strygin9a6d1482017-07-26 21:08:56265 }
266
Artem Stryginaf1b43422017-09-12 18:48:24267 if (!ResponseStatusSuccess(loader_.get()))
268 return ReadComplete();
Artem Strygin9a6d1482017-07-26 21:08:56269
Artem Stryginaf1b43422017-09-12 18:48:24270 // Leave position untouched for multiparted responce for now, when we read the
271 // data we'll get it.
Lei Zhang02a3a0f2017-12-11 20:25:48272 if (loader_->IsMultipart()) {
273 // Needs more data to calc chunk index.
274 return ReadMore();
[email protected]1b1e9eff2014-05-20 01:56:40275 }
Lei Zhang02a3a0f2017-12-11 20:25:48276
277 // Need to make sure that the server returned a byte-range, since it's
278 // possible for a server to just ignore our byte-range request and just
279 // return the entire document even if it supports byte-range requests.
280 // i.e. sniff response to
281 // http://www.act.org/compass/sample/pdf/geometry.pdf
282 int start_pos = 0;
283 if (loader_->GetByteRangeStart(&start_pos)) {
284 if (start_pos % DataStream::kChunkSize != 0)
285 return ReadComplete();
286
287 DCHECK(!chunk_.chunk_data);
288 chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
289 } else {
290 SetPartialLoadingEnabled(false);
291 }
292 return ContinueDownload();
[email protected]1b1e9eff2014-05-20 01:56:40293}
294
Lei Zhange98901a2018-04-25 23:23:59295void DocumentLoaderImpl::ReadMore() {
Artem Stryginaf1b43422017-09-12 18:48:24296 loader_->ReadResponseBody(
297 buffer_, sizeof(buffer_),
K. Moon544e09532020-07-07 17:08:55298 base::BindOnce(&DocumentLoaderImpl::DidRead, weak_factory_.GetWeakPtr()));
[email protected]1b1e9eff2014-05-20 01:56:40299}
300
Lei Zhange98901a2018-04-25 23:23:59301void DocumentLoaderImpl::DidRead(int32_t result) {
Artem Stryginaf1b43422017-09-12 18:48:24302 if (result < 0) {
303 // An error occurred.
304 // The renderer will detect that we're missing data and will display a
305 // message.
306 return ReadComplete();
[email protected]1b1e9eff2014-05-20 01:56:40307 }
Artem Stryginaf1b43422017-09-12 18:48:24308 if (result == 0) {
309 loader_.reset();
310 if (!is_partial_loader_active_)
311 return ReadComplete();
312 return ContinueDownload();
313 }
314 if (loader_->IsMultipart()) {
315 int start_pos = 0;
Lei Zhang02a3a0f2017-12-11 20:25:48316 if (!loader_->GetByteRangeStart(&start_pos))
Artem Stryginaf1b43422017-09-12 18:48:24317 return ReadComplete();
Lei Zhang02a3a0f2017-12-11 20:25:48318
Artem Stryginaf1b43422017-09-12 18:48:24319 DCHECK(!chunk_.chunk_data);
320 chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
spelchat5efbaf822016-05-24 23:29:56321 }
Lei Zhang32094aac2018-03-28 18:29:15322 if (!SaveBuffer(buffer_, result))
Artem Stryginaf1b43422017-09-12 18:48:24323 return ReadMore();
Lei Zhang02a3a0f2017-12-11 20:25:48324 if (IsDocumentComplete())
Artem Stryginaf1b43422017-09-12 18:48:24325 return ReadComplete();
Artem Stryginaf1b43422017-09-12 18:48:24326 return ContinueDownload();
[email protected]1b1e9eff2014-05-20 01:56:40327}
328
Lei Zhange98901a2018-04-25 23:23:59329bool DocumentLoaderImpl::SaveBuffer(char* input, uint32_t input_size) {
Lei Zhang32094aac2018-03-28 18:29:15330 const uint32_t document_size = GetDocumentSize();
Lei Zhang6fbfb092018-03-28 16:58:56331 bytes_received_ += input_size;
Artem Stryginaf1b43422017-09-12 18:48:24332 bool chunk_saved = false;
333 bool loading_pending_request = pending_requests_.Contains(chunk_.chunk_index);
334 while (input_size > 0) {
Lei Zhang6fbfb092018-03-28 16:58:56335 if (chunk_.data_size == 0)
Lei Zhang64aa552c2017-10-17 18:35:53336 chunk_.chunk_data = std::make_unique<DataStream::ChunkData>();
Lei Zhang6fbfb092018-03-28 16:58:56337
Artem Stryginaf1b43422017-09-12 18:48:24338 const uint32_t new_chunk_data_len =
339 std::min(DataStream::kChunkSize - chunk_.data_size, input_size);
340 memcpy(chunk_.chunk_data->data() + chunk_.data_size, input,
341 new_chunk_data_len);
342 chunk_.data_size += new_chunk_data_len;
343 if (chunk_.data_size == DataStream::kChunkSize ||
Artem Strygin2cf20af42018-06-14 14:26:53344 (document_size > 0 && document_size <= EndOfCurrentChunk())) {
Artem Stryginaf1b43422017-09-12 18:48:24345 pending_requests_.Subtract(
346 gfx::Range(chunk_.chunk_index, chunk_.chunk_index + 1));
Lei Zhang32094aac2018-03-28 18:29:15347 SaveChunkData();
Artem Stryginaf1b43422017-09-12 18:48:24348 chunk_saved = true;
349 }
350
351 input += new_chunk_data_len;
352 input_size -= new_chunk_data_len;
353 }
354
355 client_->OnNewDataReceived();
356
357 if (IsDocumentComplete())
358 return true;
359
360 if (!chunk_saved)
361 return false;
362
363 if (loading_pending_request &&
364 !pending_requests_.Contains(chunk_.chunk_index)) {
365 client_->OnPendingRequestComplete();
366 }
367 return true;
spelchat3ba2a2812015-12-10 00:44:15368}
369
Lei Zhange98901a2018-04-25 23:23:59370void DocumentLoaderImpl::SaveChunkData() {
Lei Zhang32094aac2018-03-28 18:29:15371 chunk_stream_.SetChunkData(chunk_.chunk_index, std::move(chunk_.chunk_data));
372 chunk_.data_size = 0;
373 ++chunk_.chunk_index;
374}
375
Lei Zhange98901a2018-04-25 23:23:59376uint32_t DocumentLoaderImpl::EndOfCurrentChunk() const {
Lei Zhang6fbfb092018-03-28 16:58:56377 return chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size;
378}
379
Lei Zhange98901a2018-04-25 23:23:59380void DocumentLoaderImpl::ReadComplete() {
Lei Zhang32094aac2018-03-28 18:29:15381 if (GetDocumentSize() != 0) {
382 // If there is remaining data in |chunk_|, then save whatever can be saved.
383 // e.g. In the underrun case.
384 if (chunk_.data_size != 0)
385 SaveChunkData();
386 } else {
Lei Zhang6fbfb092018-03-28 16:58:56387 uint32_t eof = EndOfCurrentChunk();
Artem Stryginaf1b43422017-09-12 18:48:24388 if (!chunk_stream_.filled_chunks().IsEmpty()) {
389 eof = std::max(
390 chunk_stream_.filled_chunks().Last().end() * DataStream::kChunkSize,
391 eof);
[email protected]1b1e9eff2014-05-20 01:56:40392 }
K. Moon6f6c6e42020-03-19 04:21:53393 chunk_stream_.set_eof_pos(eof);
Lei Zhang32094aac2018-03-28 18:29:15394 if (eof == EndOfCurrentChunk())
395 SaveChunkData();
[email protected]1b1e9eff2014-05-20 01:56:40396 }
Artem Stryginaf1b43422017-09-12 18:48:24397 loader_.reset();
[email protected]1b1e9eff2014-05-20 01:56:40398 if (IsDocumentComplete()) {
399 client_->OnDocumentComplete();
Artem Stryginaf1b43422017-09-12 18:48:24400 } else {
401 client_->OnDocumentCanceled();
[email protected]1b1e9eff2014-05-20 01:56:40402 }
[email protected]1b1e9eff2014-05-20 01:56:40403}
404
405} // namespace chrome_pdf