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 |