document_loader_impl.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. // 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. #include "pdf/loader/document_loader_impl.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <algorithm>
  8. #include <utility>
  9. #include "base/bind.h"
  10. #include "base/callback.h"
  11. #include "base/check_op.h"
  12. #include "base/feature_list.h"
  13. #include "base/numerics/safe_math.h"
  14. #include "base/strings/string_util.h"
  15. #include "pdf/loader/result_codes.h"
  16. #include "pdf/loader/url_loader_wrapper.h"
  17. #include "pdf/pdf_features.h"
  18. #include "ui/gfx/range/range.h"
  19. namespace chrome_pdf {
  20. namespace {
  21. // The distance from last received chunk, when we wait requesting data, using
  22. // current connection (like playing a cassette tape) and do not send new range
  23. // request (like rewind a cassette tape, and continue playing after).
  24. // Experimentally chosen value.
  25. constexpr int kChunkCloseDistance = 10;
  26. // Return true if the HTTP response of `loader` is a successful one and loading
  27. // should continue. 4xx error indicate subsequent requests will fail too.
  28. // e.g. resource has been removed from the server while loading it. 301
  29. // indicates a redirect was returned which won't be successful because we
  30. // disable following redirects for PDF loading (we assume they are already
  31. // resolved by the browser.
  32. bool ResponseStatusSuccess(const URLLoaderWrapper* loader) {
  33. int32_t http_code = loader->GetStatusCode();
  34. return (http_code < 400 && http_code != 301) || http_code >= 500;
  35. }
  36. bool IsValidContentType(const std::string& type) {
  37. return (
  38. base::EndsWith(type, "/pdf", base::CompareCase::INSENSITIVE_ASCII) ||
  39. base::EndsWith(type, ".pdf", base::CompareCase::INSENSITIVE_ASCII) ||
  40. base::EndsWith(type, "/x-pdf", base::CompareCase::INSENSITIVE_ASCII) ||
  41. base::EndsWith(type, "/*", base::CompareCase::INSENSITIVE_ASCII) ||
  42. base::EndsWith(type, "/octet-stream",
  43. base::CompareCase::INSENSITIVE_ASCII) ||
  44. base::EndsWith(type, "/acrobat", base::CompareCase::INSENSITIVE_ASCII) ||
  45. base::EndsWith(type, "/unknown", base::CompareCase::INSENSITIVE_ASCII));
  46. }
  47. } // namespace
  48. DocumentLoaderImpl::Chunk::Chunk() = default;
  49. DocumentLoaderImpl::Chunk::~Chunk() = default;
  50. void DocumentLoaderImpl::Chunk::Clear() {
  51. chunk_index = 0;
  52. data_size = 0;
  53. chunk_data.reset();
  54. }
  55. DocumentLoaderImpl::DocumentLoaderImpl(Client* client)
  56. : client_(client),
  57. partial_loading_enabled_(
  58. base::FeatureList::IsEnabled(features::kPdfPartialLoading)) {}
  59. DocumentLoaderImpl::~DocumentLoaderImpl() = default;
  60. bool DocumentLoaderImpl::Init(std::unique_ptr<URLLoaderWrapper> loader,
  61. const std::string& url) {
  62. DCHECK(url_.empty());
  63. DCHECK(!loader_);
  64. // Check that the initial response status is a valid one.
  65. if (!ResponseStatusSuccess(loader.get()))
  66. return false;
  67. std::string type = loader->GetContentType();
  68. // This happens for PDFs not loaded from http(s) sources.
  69. if (type == "text/plain") {
  70. if (!base::StartsWith(url, "http://",
  71. base::CompareCase::INSENSITIVE_ASCII) &&
  72. !base::StartsWith(url, "https://",
  73. base::CompareCase::INSENSITIVE_ASCII)) {
  74. type = "application/pdf";
  75. }
  76. }
  77. if (!type.empty() && !IsValidContentType(type))
  78. return false;
  79. if (base::StartsWith(loader->GetContentDisposition(), "attachment",
  80. base::CompareCase::INSENSITIVE_ASCII))
  81. return false;
  82. url_ = url;
  83. loader_ = std::move(loader);
  84. if (!loader_->IsContentEncoded())
  85. chunk_stream_.set_eof_pos(std::max(0, loader_->GetContentLength()));
  86. SetPartialLoadingEnabled(
  87. partial_loading_enabled_ &&
  88. !base::StartsWith(url, "file://", base::CompareCase::INSENSITIVE_ASCII) &&
  89. loader_->IsAcceptRangesBytes() && !loader_->IsContentEncoded() &&
  90. GetDocumentSize());
  91. ReadMore();
  92. return true;
  93. }
  94. bool DocumentLoaderImpl::IsDocumentComplete() const {
  95. return chunk_stream_.IsComplete();
  96. }
  97. uint32_t DocumentLoaderImpl::GetDocumentSize() const {
  98. return chunk_stream_.eof_pos();
  99. }
  100. uint32_t DocumentLoaderImpl::BytesReceived() const {
  101. return bytes_received_;
  102. }
  103. void DocumentLoaderImpl::ClearPendingRequests() {
  104. pending_requests_.Clear();
  105. }
  106. bool DocumentLoaderImpl::GetBlock(uint32_t position,
  107. uint32_t size,
  108. void* buf) const {
  109. base::CheckedNumeric<uint32_t> addition_result = position;
  110. addition_result += size;
  111. if (!addition_result.IsValid())
  112. return false;
  113. return chunk_stream_.ReadData(
  114. gfx::Range(position, addition_result.ValueOrDie()), buf);
  115. }
  116. bool DocumentLoaderImpl::IsDataAvailable(uint32_t position,
  117. uint32_t size) const {
  118. base::CheckedNumeric<uint32_t> addition_result = position;
  119. addition_result += size;
  120. if (!addition_result.IsValid())
  121. return false;
  122. return chunk_stream_.IsRangeAvailable(
  123. gfx::Range(position, addition_result.ValueOrDie()));
  124. }
  125. void DocumentLoaderImpl::RequestData(uint32_t position, uint32_t size) {
  126. if (size == 0 || IsDataAvailable(position, size))
  127. return;
  128. const uint32_t document_size = GetDocumentSize();
  129. if (document_size != 0) {
  130. // Check for integer overflow.
  131. base::CheckedNumeric<uint32_t> addition_result = position;
  132. addition_result += size;
  133. if (!addition_result.IsValid())
  134. return;
  135. if (addition_result.ValueOrDie() > document_size)
  136. return;
  137. }
  138. // We have some artifact request from
  139. // PDFiumEngine::OnDocumentComplete() -> FPDFAvail_IsPageAvail after
  140. // document is complete.
  141. // We need this fix in PDFIum. Adding this as a work around.
  142. // Bug: http://code.google.com/p/chromium/issues/detail?id=79996
  143. // Test url:
  144. // http://www.icann.org/en/correspondence/holtzman-to-jeffrey-02mar11-en.pdf
  145. if (!loader_)
  146. return;
  147. RangeSet requested_chunks(chunk_stream_.GetChunksRange(position, size));
  148. requested_chunks.Subtract(chunk_stream_.filled_chunks());
  149. DCHECK(!requested_chunks.IsEmpty());
  150. pending_requests_.Union(requested_chunks);
  151. }
  152. void DocumentLoaderImpl::SetPartialLoadingEnabled(bool enabled) {
  153. partial_loading_enabled_ = enabled;
  154. if (!enabled) {
  155. is_partial_loader_active_ = false;
  156. }
  157. }
  158. bool DocumentLoaderImpl::ShouldCancelLoading() const {
  159. if (!loader_)
  160. return true;
  161. if (!partial_loading_enabled_)
  162. return false;
  163. if (pending_requests_.IsEmpty()) {
  164. // Cancel loading if this is unepected data from server.
  165. return !chunk_stream_.IsValidChunkIndex(chunk_.chunk_index) ||
  166. chunk_stream_.IsChunkAvailable(chunk_.chunk_index);
  167. }
  168. const gfx::Range current_range(chunk_.chunk_index,
  169. chunk_.chunk_index + kChunkCloseDistance);
  170. return !pending_requests_.Intersects(current_range);
  171. }
  172. void DocumentLoaderImpl::ContinueDownload() {
  173. if (!ShouldCancelLoading())
  174. return ReadMore();
  175. DCHECK(partial_loading_enabled_);
  176. DCHECK(!IsDocumentComplete());
  177. DCHECK_GT(GetDocumentSize(), 0U);
  178. const size_t range_start =
  179. pending_requests_.IsEmpty() ? 0 : pending_requests_.First().start();
  180. RangeSet candidates_for_request(
  181. gfx::Range(range_start, chunk_stream_.total_chunks_count()));
  182. candidates_for_request.Subtract(chunk_stream_.filled_chunks());
  183. DCHECK(!candidates_for_request.IsEmpty());
  184. gfx::Range next_request = candidates_for_request.First();
  185. if (candidates_for_request.Size() == 1 &&
  186. next_request.length() < kChunkCloseDistance) {
  187. // We have only request at the end, try to enlarge it to improve back order
  188. // reading.
  189. const int additional_chunks_count =
  190. kChunkCloseDistance - next_request.length();
  191. int new_start = std::max(
  192. 0, static_cast<int>(next_request.start()) - additional_chunks_count);
  193. candidates_for_request =
  194. RangeSet(gfx::Range(new_start, next_request.end()));
  195. candidates_for_request.Subtract(chunk_stream_.filled_chunks());
  196. next_request = candidates_for_request.Last();
  197. }
  198. loader_.reset();
  199. chunk_.Clear();
  200. is_partial_loader_active_ = true;
  201. const size_t start = next_request.start() * DataStream::kChunkSize;
  202. const size_t length =
  203. std::min(GetDocumentSize() - start,
  204. next_request.length() * DataStream::kChunkSize);
  205. loader_ = client_->CreateURLLoader();
  206. loader_->OpenRange(url_, url_, start, length,
  207. base::BindOnce(&DocumentLoaderImpl::DidOpenPartial,
  208. weak_factory_.GetWeakPtr()));
  209. }
  210. void DocumentLoaderImpl::DidOpenPartial(int32_t result) {
  211. if (result != Result::kSuccess)
  212. return ReadComplete();
  213. if (!ResponseStatusSuccess(loader_.get()))
  214. return ReadComplete();
  215. // Leave position untouched for multiparted responce for now, when we read the
  216. // data we'll get it.
  217. if (loader_->IsMultipart()) {
  218. // Needs more data to calc chunk index.
  219. return ReadMore();
  220. }
  221. // Need to make sure that the server returned a byte-range, since it's
  222. // possible for a server to just ignore our byte-range request and just
  223. // return the entire document even if it supports byte-range requests.
  224. // i.e. sniff response to
  225. // http://www.act.org/compass/sample/pdf/geometry.pdf
  226. int start_pos = 0;
  227. if (loader_->GetByteRangeStart(&start_pos)) {
  228. if (start_pos % DataStream::kChunkSize != 0)
  229. return ReadComplete();
  230. DCHECK(!chunk_.chunk_data);
  231. chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
  232. } else {
  233. SetPartialLoadingEnabled(false);
  234. }
  235. return ContinueDownload();
  236. }
  237. void DocumentLoaderImpl::ReadMore() {
  238. loader_->ReadResponseBody(
  239. buffer_, sizeof(buffer_),
  240. base::BindOnce(&DocumentLoaderImpl::DidRead, weak_factory_.GetWeakPtr()));
  241. }
  242. void DocumentLoaderImpl::DidRead(int32_t result) {
  243. if (result < 0) {
  244. // An error occurred.
  245. // The renderer will detect that we're missing data and will display a
  246. // message.
  247. return ReadComplete();
  248. }
  249. if (result == 0) {
  250. loader_.reset();
  251. if (!is_partial_loader_active_)
  252. return ReadComplete();
  253. return ContinueDownload();
  254. }
  255. if (loader_->IsMultipart()) {
  256. int start_pos = 0;
  257. if (!loader_->GetByteRangeStart(&start_pos))
  258. return ReadComplete();
  259. DCHECK(!chunk_.chunk_data);
  260. chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
  261. }
  262. if (!SaveBuffer(buffer_, result))
  263. return ReadMore();
  264. if (IsDocumentComplete())
  265. return ReadComplete();
  266. return ContinueDownload();
  267. }
  268. bool DocumentLoaderImpl::SaveBuffer(char* input, uint32_t input_size) {
  269. const uint32_t document_size = GetDocumentSize();
  270. bytes_received_ += input_size;
  271. bool chunk_saved = false;
  272. bool loading_pending_request = pending_requests_.Contains(chunk_.chunk_index);
  273. while (input_size > 0) {
  274. if (chunk_.data_size == 0)
  275. chunk_.chunk_data = std::make_unique<DataStream::ChunkData>();
  276. const size_t new_chunk_data_len =
  277. std::min(DataStream::kChunkSize - chunk_.data_size, size_t{input_size});
  278. memcpy(chunk_.chunk_data->data() + chunk_.data_size, input,
  279. new_chunk_data_len);
  280. chunk_.data_size += new_chunk_data_len;
  281. if (chunk_.data_size == DataStream::kChunkSize ||
  282. (document_size > 0 && document_size <= EndOfCurrentChunk())) {
  283. pending_requests_.Subtract(
  284. gfx::Range(chunk_.chunk_index, chunk_.chunk_index + 1));
  285. SaveChunkData();
  286. chunk_saved = true;
  287. }
  288. input += new_chunk_data_len;
  289. input_size -= new_chunk_data_len;
  290. }
  291. client_->OnNewDataReceived();
  292. if (IsDocumentComplete())
  293. return true;
  294. if (!chunk_saved)
  295. return false;
  296. if (loading_pending_request &&
  297. !pending_requests_.Contains(chunk_.chunk_index)) {
  298. client_->OnPendingRequestComplete();
  299. }
  300. return true;
  301. }
  302. void DocumentLoaderImpl::SaveChunkData() {
  303. chunk_stream_.SetChunkData(chunk_.chunk_index, std::move(chunk_.chunk_data));
  304. chunk_.data_size = 0;
  305. ++chunk_.chunk_index;
  306. }
  307. uint32_t DocumentLoaderImpl::EndOfCurrentChunk() const {
  308. return chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size;
  309. }
  310. void DocumentLoaderImpl::ReadComplete() {
  311. if (GetDocumentSize() != 0) {
  312. // If there is remaining data in `chunk_`, then save whatever can be saved.
  313. // e.g. In the underrun case.
  314. if (chunk_.data_size != 0)
  315. SaveChunkData();
  316. } else {
  317. size_t eof = EndOfCurrentChunk();
  318. if (!chunk_stream_.filled_chunks().IsEmpty()) {
  319. eof = std::max(
  320. chunk_stream_.filled_chunks().Last().end() * DataStream::kChunkSize,
  321. eof);
  322. }
  323. chunk_stream_.set_eof_pos(eof);
  324. if (eof == EndOfCurrentChunk())
  325. SaveChunkData();
  326. }
  327. loader_.reset();
  328. if (IsDocumentComplete()) {
  329. client_->OnDocumentComplete();
  330. } else {
  331. client_->OnDocumentCanceled();
  332. }
  333. }
  334. } // namespace chrome_pdf