| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ppapi/native_client/src/trusted/plugin/file_downloader.h" | 5 #include "ppapi/native_client/src/trusted/plugin/file_downloader.h" |
| 6 | 6 |
| 7 #include <stdio.h> | 7 #include <stdio.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| 11 #include "native_client/src/include/portability_io.h" | 11 #include "native_client/src/include/portability_io.h" |
| 12 #include "native_client/src/shared/platform/nacl_check.h" | 12 #include "native_client/src/shared/platform/nacl_check.h" |
| 13 #include "native_client/src/shared/platform/nacl_time.h" | 13 #include "native_client/src/shared/platform/nacl_time.h" |
| 14 #include "ppapi/c/pp_errors.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" | 15 #include "ppapi/cpp/url_request_info.h" |
| 19 #include "ppapi/cpp/url_response_info.h" | 16 #include "ppapi/cpp/url_response_info.h" |
| 20 #include "ppapi/native_client/src/trusted/plugin/callback_source.h" | 17 #include "ppapi/native_client/src/trusted/plugin/callback_source.h" |
| 21 #include "ppapi/native_client/src/trusted/plugin/plugin.h" | 18 #include "ppapi/native_client/src/trusted/plugin/plugin.h" |
| 22 #include "ppapi/native_client/src/trusted/plugin/utility.h" | 19 #include "ppapi/native_client/src/trusted/plugin/utility.h" |
| 23 | 20 |
| 24 namespace { | |
| 25 | |
| 26 struct NaClFileInfo NoFileInfo() { | |
| 27 struct NaClFileInfo info; | |
| 28 memset(&info, 0, sizeof(info)); | |
| 29 info.desc = -1; | |
| 30 return info; | |
| 31 } | |
| 32 | |
| 33 } // namespace | |
| 34 | |
| 35 namespace plugin { | 21 namespace plugin { |
| 36 | 22 |
| 37 NaClFileInfoAutoCloser::NaClFileInfoAutoCloser() | |
| 38 : info_(NoFileInfo()) {} | |
| 39 | |
| 40 NaClFileInfoAutoCloser::NaClFileInfoAutoCloser(NaClFileInfo* pass_ownership) | |
| 41 : info_(*pass_ownership) { | |
| 42 *pass_ownership = NoFileInfo(); | |
| 43 } | |
| 44 | |
| 45 void NaClFileInfoAutoCloser::FreeResources() { | |
| 46 if (-1 != get_desc()) { | |
| 47 PLUGIN_PRINTF(("NaClFileInfoAutoCloser::FreeResources close(%d)\n", | |
| 48 get_desc())); | |
| 49 close(get_desc()); | |
| 50 } | |
| 51 info_.desc = -1; | |
| 52 } | |
| 53 | |
| 54 void NaClFileInfoAutoCloser::TakeOwnership(NaClFileInfo* pass_ownership) { | |
| 55 PLUGIN_PRINTF(("NaClFileInfoAutoCloser::set: taking ownership of %d\n", | |
| 56 pass_ownership->desc)); | |
| 57 CHECK(pass_ownership->desc == -1 || pass_ownership->desc != get_desc()); | |
| 58 FreeResources(); | |
| 59 info_ = *pass_ownership; | |
| 60 *pass_ownership = NoFileInfo(); | |
| 61 } | |
| 62 | |
| 63 NaClFileInfo NaClFileInfoAutoCloser::Release() { | |
| 64 NaClFileInfo info_to_return = info_; | |
| 65 info_ = NoFileInfo(); | |
| 66 return info_to_return; | |
| 67 } | |
| 68 | |
| 69 void FileDownloader::Initialize(Plugin* instance) { | 23 void FileDownloader::Initialize(Plugin* instance) { |
| 70 PLUGIN_PRINTF(("FileDownloader::FileDownloader (this=%p)\n", | 24 PLUGIN_PRINTF(("FileDownloader::FileDownloader (this=%p)\n", |
| 71 static_cast<void*>(this))); | 25 static_cast<void*>(this))); |
| 72 CHECK(instance != NULL); | 26 CHECK(instance != NULL); |
| 73 CHECK(instance_ == NULL); // Can only initialize once. | 27 CHECK(instance_ == NULL); // Can only initialize once. |
| 74 instance_ = instance; | 28 instance_ = instance; |
| 75 callback_factory_.Initialize(this); | 29 callback_factory_.Initialize(this); |
| 76 file_io_private_interface_ = static_cast<const PPB_FileIO_Private*>( | |
| 77 pp::Module::Get()->GetBrowserInterface(PPB_FILEIO_PRIVATE_INTERFACE)); | |
| 78 url_loader_trusted_interface_ = static_cast<const PPB_URLLoaderTrusted*>( | |
| 79 pp::Module::Get()->GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE)); | |
| 80 temp_buffer_.resize(kTempBufferSize); | 30 temp_buffer_.resize(kTempBufferSize); |
| 81 file_info_.FreeResources(); | |
| 82 } | 31 } |
| 83 | 32 |
| 84 bool FileDownloader::OpenStream( | 33 bool FileDownloader::OpenStream( |
| 85 const nacl::string& url, | 34 const nacl::string& url, |
| 86 const pp::CompletionCallback& callback, | 35 const pp::CompletionCallback& callback, |
| 87 StreamCallbackSource* stream_callback_source) { | 36 StreamCallbackSource* stream_callback_source) { |
| 88 data_stream_callback_source_ = stream_callback_source; | 37 data_stream_callback_source_ = stream_callback_source; |
| 89 return Open(url, DOWNLOAD_TO_BUFFER_AND_STREAM, callback, true, NULL); | |
| 90 } | |
| 91 | |
| 92 bool FileDownloader::Open( | |
| 93 const nacl::string& url, | |
| 94 DownloadMode mode, | |
| 95 const pp::CompletionCallback& callback, | |
| 96 bool record_progress, | |
| 97 PP_URLLoaderTrusted_StatusCallback progress_callback) { | |
| 98 PLUGIN_PRINTF(("FileDownloader::Open (url=%s)\n", url.c_str())); | 38 PLUGIN_PRINTF(("FileDownloader::Open (url=%s)\n", url.c_str())); |
| 99 if (callback.pp_completion_callback().func == NULL || | 39 if (callback.pp_completion_callback().func == NULL || instance_ == NULL) |
| 100 instance_ == NULL || | |
| 101 file_io_private_interface_ == NULL) | |
| 102 return false; | 40 return false; |
| 103 | 41 |
| 104 CHECK(instance_ != NULL); | |
| 105 status_code_ = -1; | 42 status_code_ = -1; |
| 106 url_ = url; | 43 url_ = url; |
| 107 file_open_notify_callback_ = callback; | 44 file_open_notify_callback_ = callback; |
| 108 mode_ = mode; | 45 mode_ = DOWNLOAD_TO_BUFFER_AND_STREAM; |
| 109 file_info_.FreeResources(); | |
| 110 pp::URLRequestInfo url_request(instance_); | 46 pp::URLRequestInfo url_request(instance_); |
| 111 | 47 |
| 112 // Allow CORS. | 48 // Allow CORS. |
| 113 // Note that "SetAllowCrossOriginRequests" (currently) has the side effect of | 49 // Note that "SetAllowCrossOriginRequests" (currently) has the side effect of |
| 114 // preventing credentials from being sent on same-origin requests. We | 50 // preventing credentials from being sent on same-origin requests. We |
| 115 // therefore avoid setting this flag unless we know for sure it is a | 51 // therefore avoid setting this flag unless we know for sure it is a |
| 116 // cross-origin request, resulting in behavior similar to XMLHttpRequest. | 52 // cross-origin request, resulting in behavior similar to XMLHttpRequest. |
| 117 if (!instance_->DocumentCanRequest(url)) | 53 if (!instance_->DocumentCanRequest(url)) |
| 118 url_request.SetAllowCrossOriginRequests(true); | 54 url_request.SetAllowCrossOriginRequests(true); |
| 119 | 55 |
| 120 if (!extra_request_headers_.empty()) | 56 if (!extra_request_headers_.empty()) |
| 121 url_request.SetHeaders(extra_request_headers_); | 57 url_request.SetHeaders(extra_request_headers_); |
| 122 | 58 |
| 123 // Reset the url loader and file reader. | 59 // Reset the url loader and file reader. |
| 124 // Note that we have the only reference to the underlying objects, so | 60 // Note that we have the only reference to the underlying objects, so |
| 125 // this will implicitly close any pending IO and destroy them. | 61 // this will implicitly close any pending IO and destroy them. |
| 126 url_loader_ = pp::URLLoader(instance_); | 62 url_loader_ = pp::URLLoader(instance_); |
| 127 | 63 url_request.SetRecordDownloadProgress(true); |
| 128 url_request.SetRecordDownloadProgress(record_progress); | |
| 129 | |
| 130 if (url_loader_trusted_interface_ != NULL) { | |
| 131 if (progress_callback != NULL) { | |
| 132 url_loader_trusted_interface_->RegisterStatusCallback( | |
| 133 url_loader_.pp_resource(), progress_callback); | |
| 134 } | |
| 135 } | |
| 136 | 64 |
| 137 // Prepare the url request. | 65 // Prepare the url request. |
| 138 url_request.SetURL(url_); | 66 url_request.SetURL(url_); |
| 139 | 67 |
| 140 if (mode_ == DOWNLOAD_TO_FILE) { | |
| 141 file_reader_ = pp::FileIO(instance_); | |
| 142 url_request.SetStreamToFile(true); | |
| 143 } | |
| 144 | |
| 145 // Request asynchronous download of the url providing an on-load callback. | 68 // Request asynchronous download of the url providing an on-load callback. |
| 146 // As long as this step is guaranteed to be asynchronous, we can call | 69 // As long as this step is guaranteed to be asynchronous, we can call |
| 147 // synchronously all other internal callbacks that eventually result in the | 70 // synchronously all other internal callbacks that eventually result in the |
| 148 // invocation of the user callback. The user code will not be reentered. | 71 // invocation of the user callback. The user code will not be reentered. |
| 149 pp::CompletionCallback onload_callback = | 72 pp::CompletionCallback onload_callback = |
| 150 callback_factory_.NewCallback(&FileDownloader::URLLoadStartNotify); | 73 callback_factory_.NewCallback(&FileDownloader::URLLoadStartNotify); |
| 151 int32_t pp_error = url_loader_.Open(url_request, onload_callback); | 74 int32_t pp_error = url_loader_.Open(url_request, onload_callback); |
| 152 PLUGIN_PRINTF(("FileDownloader::Open (pp_error=%" NACL_PRId32 ")\n", | 75 PLUGIN_PRINTF(("FileDownloader::Open (pp_error=%" NACL_PRId32 ")\n", |
| 153 pp_error)); | 76 pp_error)); |
| 154 CHECK(pp_error == PP_OK_COMPLETIONPENDING); | 77 CHECK(pp_error == PP_OK_COMPLETIONPENDING); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 184 if (pp_error != PP_OK) { | 107 if (pp_error != PP_OK) { |
| 185 file_open_notify_callback_.RunAndClear(pp_error); | 108 file_open_notify_callback_.RunAndClear(pp_error); |
| 186 return; | 109 return; |
| 187 } | 110 } |
| 188 | 111 |
| 189 if (!InitialResponseIsValid()) { | 112 if (!InitialResponseIsValid()) { |
| 190 file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED); | 113 file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED); |
| 191 return; | 114 return; |
| 192 } | 115 } |
| 193 | 116 |
| 194 if (mode_ != DOWNLOAD_TO_BUFFER_AND_STREAM) { | |
| 195 FinishStreaming(file_open_notify_callback_); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 file_open_notify_callback_.RunAndClear(PP_OK); | 117 file_open_notify_callback_.RunAndClear(PP_OK); |
| 200 } | 118 } |
| 201 | 119 |
| 202 void FileDownloader::FinishStreaming( | 120 void FileDownloader::FinishStreaming( |
| 203 const pp::CompletionCallback& callback) { | 121 const pp::CompletionCallback& callback) { |
| 204 stream_finish_callback_ = callback; | 122 stream_finish_callback_ = callback; |
| 205 | 123 |
| 206 // Finish streaming the body providing an optional callback. | 124 // Finish streaming the body providing an optional callback. |
| 207 if (mode_ == DOWNLOAD_TO_FILE) { | 125 pp::CompletionCallback onread_callback = |
| 208 pp::CompletionCallback onload_callback = | |
| 209 callback_factory_.NewOptionalCallback( | |
| 210 &FileDownloader::URLLoadFinishNotify); | |
| 211 int32_t pp_error = url_loader_.FinishStreamingToFile(onload_callback); | |
| 212 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING); | |
| 213 PLUGIN_PRINTF(("FileDownloader::FinishStreaming (async_notify_ok=%d)\n", | |
| 214 async_notify_ok)); | |
| 215 if (!async_notify_ok) { | |
| 216 // Call manually to free allocated memory and report errors. This calls | |
| 217 // |stream_finish_callback_| with |pp_error| as the parameter. | |
| 218 onload_callback.RunAndClear(pp_error); | |
| 219 } | |
| 220 } else { | |
| 221 pp::CompletionCallback onread_callback = | |
| 222 callback_factory_.NewOptionalCallback( | |
| 223 &FileDownloader::URLReadBodyNotify); | |
| 224 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size()); | |
| 225 int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0], | |
| 226 temp_size, | |
| 227 onread_callback); | |
| 228 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING); | |
| 229 PLUGIN_PRINTF(( | |
| 230 "FileDownloader::FinishStreaming (async_notify_ok=%d)\n", | |
| 231 async_notify_ok)); | |
| 232 if (!async_notify_ok) { | |
| 233 onread_callback.RunAndClear(pp_error); | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 void FileDownloader::URLLoadFinishNotify(int32_t pp_error) { | |
| 239 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (pp_error=%" | |
| 240 NACL_PRId32")\n", pp_error)); | |
| 241 if (pp_error != PP_OK) { // Streaming failed. | |
| 242 stream_finish_callback_.RunAndClear(pp_error); | |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 // Validate response again on load (though it should be the same | |
| 247 // as it was during InitialResponseIsValid?). | |
| 248 url_response_ = url_loader_.GetResponseInfo(); | |
| 249 CHECK(url_response_.GetStatusCode() == NACL_HTTP_STATUS_OK); | |
| 250 | |
| 251 // Record the full url from the response. | |
| 252 pp::Var full_url = url_response_.GetURL(); | |
| 253 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (full_url=%s)\n", | |
| 254 full_url.DebugString().c_str())); | |
| 255 if (!full_url.is_string()) { | |
| 256 stream_finish_callback_.RunAndClear(PP_ERROR_FAILED); | |
| 257 return; | |
| 258 } | |
| 259 full_url_ = full_url.AsString(); | |
| 260 | |
| 261 // The file is now fully downloaded. | |
| 262 pp::FileRef file(url_response_.GetBodyAsFileRef()); | |
| 263 if (file.is_null()) { | |
| 264 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (file=NULL)\n")); | |
| 265 stream_finish_callback_.RunAndClear(PP_ERROR_FAILED); | |
| 266 return; | |
| 267 } | |
| 268 | |
| 269 // Open the file providing an optional callback. | |
| 270 pp::CompletionCallback onopen_callback = | |
| 271 callback_factory_.NewOptionalCallback( | 126 callback_factory_.NewOptionalCallback( |
| 272 &FileDownloader::StreamFinishNotify); | 127 &FileDownloader::URLReadBodyNotify); |
| 273 pp_error = file_reader_.Open(file, PP_FILEOPENFLAG_READ, onopen_callback); | 128 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size()); |
| 274 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING); | 129 int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0], |
| 275 PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (async_notify_ok=%d)\n", | 130 temp_size, |
| 276 async_notify_ok)); | 131 onread_callback); |
| 277 if (!async_notify_ok) { | 132 if (pp_error != PP_OK_COMPLETIONPENDING) |
| 278 // Call manually to free allocated memory and report errors. This calls | 133 onread_callback.RunAndClear(pp_error); |
| 279 // |stream_finish_callback_| with |pp_error| as the parameter. | |
| 280 onopen_callback.RunAndClear(pp_error); | |
| 281 } | |
| 282 } | 134 } |
| 283 | 135 |
| 284 void FileDownloader::URLReadBodyNotify(int32_t pp_error) { | 136 void FileDownloader::URLReadBodyNotify(int32_t pp_error) { |
| 285 PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%" | 137 PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%" |
| 286 NACL_PRId32")\n", pp_error)); | 138 NACL_PRId32")\n", pp_error)); |
| 287 if (pp_error < PP_OK) { | 139 if (pp_error < PP_OK) { |
| 288 stream_finish_callback_.RunAndClear(pp_error); | 140 stream_finish_callback_.RunAndClear(pp_error); |
| 289 } else if (pp_error == PP_OK) { | 141 } else if (pp_error == PP_OK) { |
| 290 if (mode_ == DOWNLOAD_TO_BUFFER_AND_STREAM) { | 142 data_stream_callback_source_->GetCallback().RunAndClear(PP_OK); |
| 291 data_stream_callback_source_->GetCallback().RunAndClear(PP_OK); | 143 stream_finish_callback_.RunAndClear(PP_OK); |
| 292 } | |
| 293 StreamFinishNotify(PP_OK); | |
| 294 } else { | 144 } else { |
| 295 if (mode_ == DOWNLOAD_TO_BUFFER_AND_STREAM) { | 145 PLUGIN_PRINTF(("Running data_stream_callback, temp_buffer_=%p\n", |
| 296 PLUGIN_PRINTF(("Running data_stream_callback, temp_buffer_=%p\n", | 146 &temp_buffer_[0])); |
| 297 &temp_buffer_[0])); | 147 StreamCallback cb = data_stream_callback_source_->GetCallback(); |
| 298 StreamCallback cb = data_stream_callback_source_->GetCallback(); | 148 *(cb.output()) = &temp_buffer_; |
| 299 *(cb.output()) = &temp_buffer_; | 149 cb.RunAndClear(pp_error); |
| 300 cb.RunAndClear(pp_error); | 150 |
| 301 } | |
| 302 pp::CompletionCallback onread_callback = | 151 pp::CompletionCallback onread_callback = |
| 303 callback_factory_.NewOptionalCallback( | 152 callback_factory_.NewOptionalCallback( |
| 304 &FileDownloader::URLReadBodyNotify); | 153 &FileDownloader::URLReadBodyNotify); |
| 305 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size()); | 154 int32_t temp_size = static_cast<int32_t>(temp_buffer_.size()); |
| 306 pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0], | 155 pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0], |
| 307 temp_size, | 156 temp_size, |
| 308 onread_callback); | 157 onread_callback); |
| 309 bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING); | 158 if (pp_error != PP_OK_COMPLETIONPENDING) |
| 310 if (!async_notify_ok) { | |
| 311 onread_callback.RunAndClear(pp_error); | 159 onread_callback.RunAndClear(pp_error); |
| 312 } | |
| 313 } | 160 } |
| 314 } | 161 } |
| 315 | 162 |
| 316 bool FileDownloader::GetDownloadProgress( | 163 bool FileDownloader::GetDownloadProgress( |
| 317 int64_t* bytes_received, | 164 int64_t* bytes_received, |
| 318 int64_t* total_bytes_to_be_received) const { | 165 int64_t* total_bytes_to_be_received) const { |
| 319 return url_loader_.GetDownloadProgress(bytes_received, | 166 return url_loader_.GetDownloadProgress(bytes_received, |
| 320 total_bytes_to_be_received); | 167 total_bytes_to_be_received); |
| 321 } | 168 } |
| 322 | 169 |
| 323 nacl::string FileDownloader::GetResponseHeaders() const { | 170 nacl::string FileDownloader::GetResponseHeaders() const { |
| 324 pp::Var headers = url_response_.GetHeaders(); | 171 pp::Var headers = url_response_.GetHeaders(); |
| 325 if (!headers.is_string()) { | 172 if (!headers.is_string()) { |
| 326 PLUGIN_PRINTF(( | 173 PLUGIN_PRINTF(( |
| 327 "FileDownloader::GetResponseHeaders (headers are not a string)\n")); | 174 "FileDownloader::GetResponseHeaders (headers are not a string)\n")); |
| 328 return nacl::string(); | 175 return nacl::string(); |
| 329 } | 176 } |
| 330 return headers.AsString(); | 177 return headers.AsString(); |
| 331 } | 178 } |
| 332 | 179 |
| 333 void FileDownloader::StreamFinishNotify(int32_t pp_error) { | |
| 334 PLUGIN_PRINTF(( | |
| 335 "FileDownloader::StreamFinishNotify (pp_error=%" NACL_PRId32 ")\n", | |
| 336 pp_error)); | |
| 337 | |
| 338 // Run the callback if we have an error, or if we don't have a file_reader_ | |
| 339 // to get a file handle for. | |
| 340 if (pp_error != PP_OK || file_reader_.pp_resource() == 0) { | |
| 341 stream_finish_callback_.RunAndClear(pp_error); | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 pp::CompletionCallbackWithOutput<PP_FileHandle> cb = | |
| 346 callback_factory_.NewCallbackWithOutput( | |
| 347 &FileDownloader::GotFileHandleNotify); | |
| 348 file_io_private_interface_->RequestOSFileHandle( | |
| 349 file_reader_.pp_resource(), cb.output(), cb.pp_completion_callback()); | |
| 350 } | |
| 351 | |
| 352 void FileDownloader::GotFileHandleNotify(int32_t pp_error, | |
| 353 PP_FileHandle handle) { | |
| 354 PLUGIN_PRINTF(( | |
| 355 "FileDownloader::GotFileHandleNotify (pp_error=%" NACL_PRId32 ")\n", | |
| 356 pp_error)); | |
| 357 if (pp_error == PP_OK) { | |
| 358 NaClFileInfo tmp_info = NoFileInfo(); | |
| 359 tmp_info.desc = ConvertFileDescriptor(handle, false); | |
| 360 file_info_.TakeOwnership(&tmp_info); | |
| 361 } | |
| 362 | |
| 363 stream_finish_callback_.RunAndClear(pp_error); | |
| 364 } | |
| 365 | |
| 366 } // namespace plugin | 180 } // namespace plugin |
| OLD | NEW |