cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / ppapi / native_client / src / trusted / plugin / file_downloader.cc
blob5da04c686b9a7d7306cdb73e1f2bdfd3ae2e9041
1 // Copyright (c) 2012 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.
5 #include "ppapi/native_client/src/trusted/plugin/file_downloader.h"
7 #include <stdio.h>
8 #include <string.h>
9 #include <string>
11 #include "native_client/src/include/portability_io.h"
12 #include "native_client/src/shared/platform/nacl_check.h"
13 #include "native_client/src/shared/platform/nacl_time.h"
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/ppb_file_io.h"
16 #include "ppapi/cpp/file_io.h"
17 #include "ppapi/cpp/file_ref.h"
18 #include "ppapi/cpp/url_request_info.h"
19 #include "ppapi/cpp/url_response_info.h"
20 #include "ppapi/native_client/src/trusted/plugin/callback_source.h"
21 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
22 #include "ppapi/native_client/src/trusted/plugin/utility.h"
24 namespace {
26 const int32_t kExtensionUrlRequestStatusOk = 200;
27 const int32_t kDataUriRequestStatusOk = 0;
29 struct NaClFileInfo NoFileInfo() {
30 struct NaClFileInfo info;
31 memset(&info, 0, sizeof(info));
32 info.desc = -1;
33 return info;
38 namespace plugin {
40 void FileDownloader::Initialize(Plugin* instance) {
41 PLUGIN_PRINTF(("FileDownloader::FileDownloader (this=%p)\n",
42 static_cast<void*>(this)));
43 CHECK(instance != NULL);
44 CHECK(instance_ == NULL); // Can only initialize once.
45 instance_ = instance;
46 callback_factory_.Initialize(this);
47 file_io_trusted_interface_ = static_cast<const PPB_FileIOTrusted*>(
48 pp::Module::Get()->GetBrowserInterface(PPB_FILEIOTRUSTED_INTERFACE));
49 url_loader_trusted_interface_ = static_cast<const PPB_URLLoaderTrusted*>(
50 pp::Module::Get()->GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE));
51 temp_buffer_.resize(kTempBufferSize);
54 bool FileDownloader::OpenStream(
55 const nacl::string& url,
56 const pp::CompletionCallback& callback,
57 StreamCallbackSource* stream_callback_source) {
58 open_and_stream_ = false;
59 data_stream_callback_source_ = stream_callback_source;
60 return Open(url, DOWNLOAD_STREAM, callback, true, NULL);
63 bool FileDownloader::Open(
64 const nacl::string& url,
65 DownloadMode mode,
66 const pp::CompletionCallback& callback,
67 bool record_progress,
68 PP_URLLoaderTrusted_StatusCallback progress_callback) {
69 PLUGIN_PRINTF(("FileDownloader::Open (url=%s)\n", url.c_str()));
70 if (callback.pp_completion_callback().func == NULL ||
71 instance_ == NULL ||
72 file_io_trusted_interface_ == NULL)
73 return false;
75 CHECK(instance_ != NULL);
76 open_time_ = NaClGetTimeOfDayMicroseconds();
77 status_code_ = -1;
78 url_to_open_ = url;
79 url_ = url;
80 file_open_notify_callback_ = callback;
81 mode_ = mode;
82 buffer_.clear();
83 pp::URLRequestInfo url_request(instance_);
85 // Allow CORS.
86 // Note that "SetAllowCrossOriginRequests" (currently) has the side effect of
87 // preventing credentials from being sent on same-origin requests. We
88 // therefore avoid setting this flag unless we know for sure it is a
89 // cross-origin request, resulting in behavior similar to XMLHttpRequest.
90 if (!instance_->DocumentCanRequest(url))
91 url_request.SetAllowCrossOriginRequests(true);
93 do {
94 // Reset the url loader and file reader.
95 // Note that we have the only reference to the underlying objects, so
96 // this will implicitly close any pending IO and destroy them.
97 url_loader_ = pp::URLLoader(instance_);
98 url_scheme_ = instance_->GetUrlScheme(url);
99 bool grant_universal_access = false;
100 if (url_scheme_ == SCHEME_DATA) {
101 // TODO(elijahtaylor) Remove this when data URIs can be read without
102 // universal access.
103 // https://bugs.webkit.org/show_bug.cgi?id=17352
104 if (streaming_to_buffer()) {
105 grant_universal_access = true;
106 } else {
107 // Open is to invoke a callback on success or failure. Schedule
108 // it asynchronously to follow PPAPI's convention and avoid reentrancy.
109 pp::Core* core = pp::Module::Get()->core();
110 core->CallOnMainThread(0, callback, PP_ERROR_NOACCESS);
111 PLUGIN_PRINTF(("FileDownloader::Open (pp_error=PP_ERROR_NOACCESS)\n"));
112 return true;
116 url_request.SetRecordDownloadProgress(record_progress);
118 if (url_loader_trusted_interface_ != NULL) {
119 if (grant_universal_access) {
120 // TODO(sehr,jvoung): See if we can remove this -- currently
121 // only used for data URIs.
122 url_loader_trusted_interface_->GrantUniversalAccess(
123 url_loader_.pp_resource());
125 if (progress_callback != NULL) {
126 url_loader_trusted_interface_->RegisterStatusCallback(
127 url_loader_.pp_resource(), progress_callback);
131 // Prepare the url request.
132 url_request.SetURL(url_);
134 if (streaming_to_file()) {
135 file_reader_ = pp::FileIO(instance_);
136 url_request.SetStreamToFile(true);
138 } while (0);
140 void (FileDownloader::*start_notify)(int32_t);
141 if (streaming_to_file())
142 start_notify = &FileDownloader::URLLoadStartNotify;
143 else
144 start_notify = &FileDownloader::URLBufferStartNotify;
146 // Request asynchronous download of the url providing an on-load callback.
147 // As long as this step is guaranteed to be asynchronous, we can call
148 // synchronously all other internal callbacks that eventually result in the
149 // invocation of the user callback. The user code will not be reentered.
150 pp::CompletionCallback onload_callback =
151 callback_factory_.NewCallback(start_notify);
152 int32_t pp_error = url_loader_.Open(url_request, onload_callback);
153 PLUGIN_PRINTF(("FileDownloader::Open (pp_error=%" NACL_PRId32 ")\n",
154 pp_error));
155 CHECK(pp_error == PP_OK_COMPLETIONPENDING);
156 return true;
159 void FileDownloader::OpenFast(const nacl::string& url,
160 PP_FileHandle file_handle,
161 uint64_t file_token_lo, uint64_t file_token_hi) {
162 PLUGIN_PRINTF(("FileDownloader::OpenFast (url=%s)\n", url.c_str()));
163 CHECK(instance_ != NULL);
164 open_time_ = NaClGetTimeOfDayMicroseconds();
165 status_code_ = NACL_HTTP_STATUS_OK;
166 url_to_open_ = url;
167 url_ = url;
168 mode_ = DOWNLOAD_NONE;
169 file_handle_ = file_handle;
170 file_token_.lo = file_token_lo;
171 file_token_.hi = file_token_hi;
174 struct NaClFileInfo FileDownloader::GetFileInfo() {
175 struct NaClFileInfo info = NoFileInfo();
176 int32_t file_desc = NACL_NO_FILE_DESC;
177 if (not_streaming() && file_handle_ != PP_kInvalidFileHandle) {
178 #if NACL_WINDOWS
179 // On Windows, valid handles are 32 bit unsigned integers so this is safe.
180 file_desc = reinterpret_cast<uintptr_t>(file_handle_);
181 #else
182 file_desc = file_handle_;
183 #endif
184 info.file_token = file_token_;
185 } else {
186 if (!streaming_to_file()) {
187 return NoFileInfo();
189 // Use the trusted interface to get the file descriptor.
190 if (file_io_trusted_interface_ == NULL) {
191 return NoFileInfo();
193 file_desc = file_io_trusted_interface_->GetOSFileDescriptor(
194 file_reader_.pp_resource());
197 #if NACL_WINDOWS
198 // Convert the Windows HANDLE from Pepper to a POSIX file descriptor.
199 int32_t posix_desc = _open_osfhandle(file_desc, _O_RDWR | _O_BINARY);
200 if (posix_desc == -1) {
201 // Close the Windows HANDLE if it can't be converted.
202 CloseHandle(reinterpret_cast<HANDLE>(file_desc));
203 return NoFileInfo();
205 file_desc = posix_desc;
206 #endif
208 info.desc = file_desc;
209 return info;
212 int64_t FileDownloader::TimeSinceOpenMilliseconds() const {
213 int64_t now = NaClGetTimeOfDayMicroseconds();
214 // If Open() wasn't called or we somehow return an earlier time now, just
215 // return the 0 rather than worse nonsense values.
216 if (open_time_ < 0 || now < open_time_)
217 return 0;
218 return (now - open_time_) / NACL_MICROS_PER_MILLI;
221 bool FileDownloader::InitialResponseIsValid(int32_t pp_error) {
222 if (pp_error != PP_OK) { // Url loading failed.
223 file_open_notify_callback_.RunAndClear(pp_error);
224 return false;
227 // Process the response, validating the headers to confirm successful loading.
228 url_response_ = url_loader_.GetResponseInfo();
229 if (url_response_.is_null()) {
230 PLUGIN_PRINTF((
231 "FileDownloader::InitialResponseIsValid (url_response_=NULL)\n"));
232 file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
233 return false;
236 pp::Var full_url = url_response_.GetURL();
237 if (!full_url.is_string()) {
238 PLUGIN_PRINTF((
239 "FileDownloader::InitialResponseIsValid (url is not a string)\n"));
240 file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
241 return false;
243 url_ = full_url.AsString();
245 // Note that URLs in the data-URI scheme produce different error
246 // codes than other schemes. This is because data-URI are really a
247 // special kind of file scheme, and therefore do not produce HTTP
248 // status codes.
249 bool status_ok = false;
250 status_code_ = url_response_.GetStatusCode();
251 switch (url_scheme_) {
252 case SCHEME_CHROME_EXTENSION:
253 PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid (chrome-extension "
254 "response status_code=%" NACL_PRId32 ")\n", status_code_));
255 status_ok = (status_code_ == kExtensionUrlRequestStatusOk);
256 break;
257 case SCHEME_DATA:
258 PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid (data URI "
259 "response status_code=%" NACL_PRId32 ")\n", status_code_));
260 status_ok = (status_code_ == kDataUriRequestStatusOk);
261 break;
262 case SCHEME_OTHER:
263 PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid (HTTP response "
264 "status_code=%" NACL_PRId32 ")\n", status_code_));
265 status_ok = (status_code_ == NACL_HTTP_STATUS_OK);
266 break;
269 if (!status_ok) {
270 file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
271 return false;
274 return true;
277 void FileDownloader::URLLoadStartNotify(int32_t pp_error) {
278 PLUGIN_PRINTF(("FileDownloader::URLLoadStartNotify (pp_error=%"
279 NACL_PRId32")\n", pp_error));
281 if (!InitialResponseIsValid(pp_error)) {
282 // InitialResponseIsValid() calls file_open_notify_callback_ on errors.
283 return;
286 if (open_and_stream_)
287 return FinishStreaming(file_open_notify_callback_);
289 file_open_notify_callback_.RunAndClear(pp_error);
292 void FileDownloader::URLBufferStartNotify(int32_t pp_error) {
293 PLUGIN_PRINTF(("FileDownloader::URLBufferStartNotify (pp_error=%"
294 NACL_PRId32")\n", pp_error));
296 if (!InitialResponseIsValid(pp_error)) {
297 // InitialResponseIsValid() calls file_open_notify_callback_ on errors.
298 return;
301 if (open_and_stream_)
302 return FinishStreaming(file_open_notify_callback_);
304 file_open_notify_callback_.RunAndClear(pp_error);
307 void FileDownloader::FinishStreaming(
308 const pp::CompletionCallback& callback) {
309 stream_finish_callback_ = callback;
311 // Finish streaming the body providing an optional callback.
312 if (streaming_to_file()) {
313 pp::CompletionCallback onload_callback =
314 callback_factory_.NewOptionalCallback(
315 &FileDownloader::URLLoadFinishNotify);
316 int32_t pp_error = url_loader_.FinishStreamingToFile(onload_callback);
317 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
318 PLUGIN_PRINTF(("FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
319 async_notify_ok));
320 if (!async_notify_ok) {
321 // Call manually to free allocated memory and report errors. This calls
322 // |stream_finish_callback_| with |pp_error| as the parameter.
323 onload_callback.RunAndClear(pp_error);
325 } else {
326 pp::CompletionCallback onread_callback =
327 callback_factory_.NewOptionalCallback(
328 &FileDownloader::URLReadBodyNotify);
329 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
330 int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
331 temp_size,
332 onread_callback);
333 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
334 PLUGIN_PRINTF((
335 "FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
336 async_notify_ok));
337 if (!async_notify_ok) {
338 onread_callback.RunAndClear(pp_error);
343 void FileDownloader::URLLoadFinishNotify(int32_t pp_error) {
344 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (pp_error=%"
345 NACL_PRId32")\n", pp_error));
346 if (pp_error != PP_OK) { // Streaming failed.
347 stream_finish_callback_.RunAndClear(pp_error);
348 return;
351 // Validate response again on load (though it should be the same
352 // as it was during InitialResponseIsValid?).
353 url_response_ = url_loader_.GetResponseInfo();
354 CHECK(url_response_.GetStatusCode() == NACL_HTTP_STATUS_OK ||
355 url_response_.GetStatusCode() == kExtensionUrlRequestStatusOk);
357 // Record the full url from the response.
358 pp::Var full_url = url_response_.GetURL();
359 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (full_url=%s)\n",
360 full_url.DebugString().c_str()));
361 if (!full_url.is_string()) {
362 stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
363 return;
365 url_ = full_url.AsString();
367 // The file is now fully downloaded.
368 pp::FileRef file(url_response_.GetBodyAsFileRef());
369 if (file.is_null()) {
370 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (file=NULL)\n"));
371 stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
372 return;
375 // Open the file providing an optional callback.
376 pp::CompletionCallback onopen_callback =
377 callback_factory_.NewOptionalCallback(
378 &FileDownloader::StreamFinishNotify);
379 pp_error = file_reader_.Open(file, PP_FILEOPENFLAG_READ, onopen_callback);
380 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
381 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (async_notify_ok=%d)\n",
382 async_notify_ok));
383 if (!async_notify_ok) {
384 // Call manually to free allocated memory and report errors. This calls
385 // |stream_finish_callback_| with |pp_error| as the parameter.
386 onopen_callback.RunAndClear(pp_error);
390 void FileDownloader::URLReadBodyNotify(int32_t pp_error) {
391 PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%"
392 NACL_PRId32")\n", pp_error));
393 if (pp_error < PP_OK) {
394 stream_finish_callback_.RunAndClear(pp_error);
395 } else if (pp_error == PP_OK) {
396 if (streaming_to_user()) {
397 data_stream_callback_source_->GetCallback().RunAndClear(PP_OK);
399 StreamFinishNotify(PP_OK);
400 } else {
401 if (streaming_to_buffer()) {
402 buffer_.insert(buffer_.end(), &temp_buffer_[0], &temp_buffer_[pp_error]);
403 } else if (streaming_to_user()) {
404 PLUGIN_PRINTF(("Running data_stream_callback, temp_buffer_=%p\n",
405 &temp_buffer_[0]));
406 StreamCallback cb = data_stream_callback_source_->GetCallback();
407 *(cb.output()) = &temp_buffer_;
408 cb.RunAndClear(pp_error);
410 pp::CompletionCallback onread_callback =
411 callback_factory_.NewOptionalCallback(
412 &FileDownloader::URLReadBodyNotify);
413 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
414 pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
415 temp_size,
416 onread_callback);
417 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
418 if (!async_notify_ok) {
419 onread_callback.RunAndClear(pp_error);
424 bool FileDownloader::GetDownloadProgress(
425 int64_t* bytes_received,
426 int64_t* total_bytes_to_be_received) const {
427 return url_loader_.GetDownloadProgress(bytes_received,
428 total_bytes_to_be_received);
431 nacl::string FileDownloader::GetResponseHeaders() const {
432 pp::Var headers = url_response_.GetHeaders();
433 if (!headers.is_string()) {
434 PLUGIN_PRINTF((
435 "FileDownloader::GetResponseHeaders (headers are not a string)\n"));
436 return nacl::string();
438 return headers.AsString();
441 void FileDownloader::StreamFinishNotify(int32_t pp_error) {
442 PLUGIN_PRINTF((
443 "FileDownloader::StreamFinishNotify (pp_error=%" NACL_PRId32 ")\n",
444 pp_error));
445 stream_finish_callback_.RunAndClear(pp_error);
448 bool FileDownloader::streaming_to_file() const {
449 return mode_ == DOWNLOAD_TO_FILE;
452 bool FileDownloader::streaming_to_buffer() const {
453 return mode_ == DOWNLOAD_TO_BUFFER;
456 bool FileDownloader::streaming_to_user() const {
457 return mode_ == DOWNLOAD_STREAM;
460 bool FileDownloader::not_streaming() const {
461 return mode_ == DOWNLOAD_NONE;
464 } // namespace plugin