Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / file_downloader.cc
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.
4
5 #include "ppapi/native_client/src/trusted/plugin/file_downloader.h"
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <string>
10
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"
23
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 // Converts a PP_FileHandle to a POSIX file descriptor.
34 int32_t ConvertFileDescriptor(PP_FileHandle handle) {
35   PLUGIN_PRINTF(("ConvertFileDescriptor, handle=%d\n", handle));
36 #if NACL_WINDOWS
37   int32_t file_desc = NACL_NO_FILE_DESC;
38   // On Windows, valid handles are 32 bit unsigned integers so this is safe.
39   file_desc = reinterpret_cast<uintptr_t>(handle);
40   // Convert the Windows HANDLE from Pepper to a POSIX file descriptor.
41   int32_t posix_desc = _open_osfhandle(file_desc, _O_RDWR | _O_BINARY);
42   if (posix_desc == -1) {
43     // Close the Windows HANDLE if it can't be converted.
44     CloseHandle(reinterpret_cast<HANDLE>(file_desc));
45     return -1;
46   }
47   return posix_desc;
48 #else
49   return handle;
50 #endif
51 }
52
53 }  // namespace
54
55 namespace plugin {
56
57 NaClFileInfoAutoCloser::NaClFileInfoAutoCloser()
58     : info_(NoFileInfo()) {}
59
60 NaClFileInfoAutoCloser::NaClFileInfoAutoCloser(NaClFileInfo* pass_ownership)
61     : info_(*pass_ownership) {
62   *pass_ownership = NoFileInfo();
63 }
64
65 void NaClFileInfoAutoCloser::FreeResources() {
66   if (-1 != get_desc()) {
67     PLUGIN_PRINTF(("NaClFileInfoAutoCloser::FreeResources close(%d)\n",
68                    get_desc()));
69     close(get_desc());
70   }
71   info_.desc = -1;
72 }
73
74 void NaClFileInfoAutoCloser::TakeOwnership(NaClFileInfo* pass_ownership) {
75   PLUGIN_PRINTF(("NaClFileInfoAutoCloser::set: taking ownership of %d\n",
76                  pass_ownership->desc));
77   CHECK(pass_ownership->desc == -1 || pass_ownership->desc != get_desc());
78   FreeResources();
79   info_ = *pass_ownership;
80   *pass_ownership = NoFileInfo();
81 }
82
83 NaClFileInfo NaClFileInfoAutoCloser::Release() {
84   NaClFileInfo info_to_return = info_;
85   info_ = NoFileInfo();
86   return info_to_return;
87 }
88
89 void FileDownloader::Initialize(Plugin* instance) {
90   PLUGIN_PRINTF(("FileDownloader::FileDownloader (this=%p)\n",
91                  static_cast<void*>(this)));
92   CHECK(instance != NULL);
93   CHECK(instance_ == NULL);  // Can only initialize once.
94   instance_ = instance;
95   callback_factory_.Initialize(this);
96   file_io_private_interface_ = static_cast<const PPB_FileIO_Private*>(
97       pp::Module::Get()->GetBrowserInterface(PPB_FILEIO_PRIVATE_INTERFACE));
98   url_loader_trusted_interface_ = static_cast<const PPB_URLLoaderTrusted*>(
99       pp::Module::Get()->GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE));
100   temp_buffer_.resize(kTempBufferSize);
101   file_info_.FreeResources();
102 }
103
104 bool FileDownloader::OpenStream(
105     const nacl::string& url,
106     const pp::CompletionCallback& callback,
107     StreamCallbackSource* stream_callback_source) {
108   data_stream_callback_source_ = stream_callback_source;
109   return Open(url, DOWNLOAD_TO_BUFFER_AND_STREAM, callback, true, NULL);
110 }
111
112 bool FileDownloader::Open(
113     const nacl::string& url,
114     DownloadMode mode,
115     const pp::CompletionCallback& callback,
116     bool record_progress,
117     PP_URLLoaderTrusted_StatusCallback progress_callback) {
118   PLUGIN_PRINTF(("FileDownloader::Open (url=%s)\n", url.c_str()));
119   if (callback.pp_completion_callback().func == NULL ||
120       instance_ == NULL ||
121       file_io_private_interface_ == NULL)
122     return false;
123
124   CHECK(instance_ != NULL);
125   status_code_ = -1;
126   url_ = url;
127   file_open_notify_callback_ = callback;
128   mode_ = mode;
129   file_info_.FreeResources();
130   pp::URLRequestInfo url_request(instance_);
131
132   // Allow CORS.
133   // Note that "SetAllowCrossOriginRequests" (currently) has the side effect of
134   // preventing credentials from being sent on same-origin requests.  We
135   // therefore avoid setting this flag unless we know for sure it is a
136   // cross-origin request, resulting in behavior similar to XMLHttpRequest.
137   if (!instance_->DocumentCanRequest(url))
138     url_request.SetAllowCrossOriginRequests(true);
139
140   if (!extra_request_headers_.empty())
141     url_request.SetHeaders(extra_request_headers_);
142
143   // Reset the url loader and file reader.
144   // Note that we have the only reference to the underlying objects, so
145   // this will implicitly close any pending IO and destroy them.
146   url_loader_ = pp::URLLoader(instance_);
147
148   url_request.SetRecordDownloadProgress(record_progress);
149
150   if (url_loader_trusted_interface_ != NULL) {
151     if (progress_callback != NULL) {
152       url_loader_trusted_interface_->RegisterStatusCallback(
153           url_loader_.pp_resource(), progress_callback);
154     }
155   }
156
157   // Prepare the url request.
158   url_request.SetURL(url_);
159
160   if (mode_ == DOWNLOAD_TO_FILE) {
161     file_reader_ = pp::FileIO(instance_);
162     url_request.SetStreamToFile(true);
163   }
164
165   // Request asynchronous download of the url providing an on-load callback.
166   // As long as this step is guaranteed to be asynchronous, we can call
167   // synchronously all other internal callbacks that eventually result in the
168   // invocation of the user callback. The user code will not be reentered.
169   pp::CompletionCallback onload_callback =
170       callback_factory_.NewCallback(&FileDownloader::URLLoadStartNotify);
171   int32_t pp_error = url_loader_.Open(url_request, onload_callback);
172   PLUGIN_PRINTF(("FileDownloader::Open (pp_error=%" NACL_PRId32 ")\n",
173                  pp_error));
174   CHECK(pp_error == PP_OK_COMPLETIONPENDING);
175   return true;
176 }
177
178 void FileDownloader::OpenFast(const nacl::string& url,
179                               PP_FileHandle file_handle,
180                               uint64_t file_token_lo, uint64_t file_token_hi) {
181   PLUGIN_PRINTF(("FileDownloader::OpenFast (url=%s)\n", url.c_str()));
182
183   file_info_.FreeResources();
184   CHECK(instance_ != NULL);
185   status_code_ = NACL_HTTP_STATUS_OK;
186   url_ = url;
187   mode_ = DOWNLOAD_NONE;
188   if (file_handle != PP_kInvalidFileHandle) {
189     NaClFileInfo tmp_info = NoFileInfo();
190     tmp_info.desc = ConvertFileDescriptor(file_handle);
191     tmp_info.file_token.lo = file_token_lo;
192     tmp_info.file_token.hi = file_token_hi;
193     file_info_.TakeOwnership(&tmp_info);
194   }
195 }
196
197 NaClFileInfo FileDownloader::GetFileInfo() {
198   NaClFileInfo info_to_return = NoFileInfo();
199
200   PLUGIN_PRINTF(("FileDownloader::GetFileInfo, this %p\n", this));
201   if (file_info_.get_desc() != -1) {
202     info_to_return = file_info_.Release();
203   }
204   PLUGIN_PRINTF(("FileDownloader::GetFileInfo -- returning %d\n",
205                  info_to_return.desc));
206   return info_to_return;
207 }
208
209 bool FileDownloader::InitialResponseIsValid() {
210   // Process the response, validating the headers to confirm successful loading.
211   url_response_ = url_loader_.GetResponseInfo();
212   if (url_response_.is_null()) {
213     PLUGIN_PRINTF((
214         "FileDownloader::InitialResponseIsValid (url_response_=NULL)\n"));
215     return false;
216   }
217
218   pp::Var full_url = url_response_.GetURL();
219   if (!full_url.is_string()) {
220     PLUGIN_PRINTF((
221         "FileDownloader::InitialResponseIsValid (url is not a string)\n"));
222     return false;
223   }
224   full_url_ = full_url.AsString();
225
226   status_code_ = url_response_.GetStatusCode();
227   PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid ("
228                  "response status_code=%" NACL_PRId32 ")\n", status_code_));
229   return status_code_ == NACL_HTTP_STATUS_OK;
230 }
231
232 void FileDownloader::URLLoadStartNotify(int32_t pp_error) {
233   PLUGIN_PRINTF(("FileDownloader::URLLoadStartNotify (pp_error=%"
234                  NACL_PRId32")\n", pp_error));
235   if (pp_error != PP_OK) {
236     file_open_notify_callback_.RunAndClear(pp_error);
237     return;
238   }
239
240   if (!InitialResponseIsValid()) {
241     file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
242     return;
243   }
244
245   if (mode_ != DOWNLOAD_TO_BUFFER_AND_STREAM) {
246     FinishStreaming(file_open_notify_callback_);
247     return;
248   }
249
250   file_open_notify_callback_.RunAndClear(PP_OK);
251 }
252
253 void FileDownloader::FinishStreaming(
254     const pp::CompletionCallback& callback) {
255   stream_finish_callback_ = callback;
256
257   // Finish streaming the body providing an optional callback.
258   if (mode_ == DOWNLOAD_TO_FILE) {
259     pp::CompletionCallback onload_callback =
260         callback_factory_.NewOptionalCallback(
261             &FileDownloader::URLLoadFinishNotify);
262     int32_t pp_error = url_loader_.FinishStreamingToFile(onload_callback);
263     bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
264     PLUGIN_PRINTF(("FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
265                    async_notify_ok));
266     if (!async_notify_ok) {
267       // Call manually to free allocated memory and report errors.  This calls
268       // |stream_finish_callback_| with |pp_error| as the parameter.
269       onload_callback.RunAndClear(pp_error);
270     }
271   } else {
272     pp::CompletionCallback onread_callback =
273         callback_factory_.NewOptionalCallback(
274             &FileDownloader::URLReadBodyNotify);
275     int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
276     int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
277                                                     temp_size,
278                                                     onread_callback);
279     bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
280     PLUGIN_PRINTF((
281         "FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
282         async_notify_ok));
283     if (!async_notify_ok) {
284       onread_callback.RunAndClear(pp_error);
285     }
286   }
287 }
288
289 void FileDownloader::URLLoadFinishNotify(int32_t pp_error) {
290   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (pp_error=%"
291                  NACL_PRId32")\n", pp_error));
292   if (pp_error != PP_OK) {  // Streaming failed.
293     stream_finish_callback_.RunAndClear(pp_error);
294     return;
295   }
296
297   // Validate response again on load (though it should be the same
298   // as it was during InitialResponseIsValid?).
299   url_response_ = url_loader_.GetResponseInfo();
300   CHECK(url_response_.GetStatusCode() == NACL_HTTP_STATUS_OK);
301
302   // Record the full url from the response.
303   pp::Var full_url = url_response_.GetURL();
304   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (full_url=%s)\n",
305                  full_url.DebugString().c_str()));
306   if (!full_url.is_string()) {
307     stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
308     return;
309   }
310   full_url_ = full_url.AsString();
311
312   // The file is now fully downloaded.
313   pp::FileRef file(url_response_.GetBodyAsFileRef());
314   if (file.is_null()) {
315     PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (file=NULL)\n"));
316     stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
317     return;
318   }
319
320   // Open the file providing an optional callback.
321   pp::CompletionCallback onopen_callback =
322       callback_factory_.NewOptionalCallback(
323           &FileDownloader::StreamFinishNotify);
324   pp_error = file_reader_.Open(file, PP_FILEOPENFLAG_READ, onopen_callback);
325   bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
326   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (async_notify_ok=%d)\n",
327                  async_notify_ok));
328   if (!async_notify_ok) {
329     // Call manually to free allocated memory and report errors.  This calls
330     // |stream_finish_callback_| with |pp_error| as the parameter.
331     onopen_callback.RunAndClear(pp_error);
332   }
333 }
334
335 void FileDownloader::URLReadBodyNotify(int32_t pp_error) {
336   PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%"
337                  NACL_PRId32")\n", pp_error));
338   if (pp_error < PP_OK) {
339     stream_finish_callback_.RunAndClear(pp_error);
340   } else if (pp_error == PP_OK) {
341     if (mode_ == DOWNLOAD_TO_BUFFER_AND_STREAM) {
342       data_stream_callback_source_->GetCallback().RunAndClear(PP_OK);
343     }
344     StreamFinishNotify(PP_OK);
345   } else {
346     if (mode_ == DOWNLOAD_TO_BUFFER_AND_STREAM) {
347       PLUGIN_PRINTF(("Running data_stream_callback, temp_buffer_=%p\n",
348                      &temp_buffer_[0]));
349       StreamCallback cb = data_stream_callback_source_->GetCallback();
350       *(cb.output()) = &temp_buffer_;
351       cb.RunAndClear(pp_error);
352     }
353     pp::CompletionCallback onread_callback =
354         callback_factory_.NewOptionalCallback(
355             &FileDownloader::URLReadBodyNotify);
356     int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
357     pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
358                                             temp_size,
359                                             onread_callback);
360     bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
361     if (!async_notify_ok) {
362       onread_callback.RunAndClear(pp_error);
363     }
364   }
365 }
366
367 bool FileDownloader::GetDownloadProgress(
368     int64_t* bytes_received,
369     int64_t* total_bytes_to_be_received) const {
370   return url_loader_.GetDownloadProgress(bytes_received,
371                                          total_bytes_to_be_received);
372 }
373
374 nacl::string FileDownloader::GetResponseHeaders() const {
375   pp::Var headers = url_response_.GetHeaders();
376   if (!headers.is_string()) {
377     PLUGIN_PRINTF((
378         "FileDownloader::GetResponseHeaders (headers are not a string)\n"));
379     return nacl::string();
380   }
381   return headers.AsString();
382 }
383
384 void FileDownloader::StreamFinishNotify(int32_t pp_error) {
385   PLUGIN_PRINTF((
386       "FileDownloader::StreamFinishNotify (pp_error=%" NACL_PRId32 ")\n",
387       pp_error));
388
389   // Run the callback if we have an error, or if we don't have a file_reader_
390   // to get a file handle for.
391   if (pp_error != PP_OK || file_reader_.pp_resource() == 0) {
392     stream_finish_callback_.RunAndClear(pp_error);
393     return;
394   }
395
396   pp::CompletionCallbackWithOutput<PP_FileHandle> cb =
397       callback_factory_.NewCallbackWithOutput(
398           &FileDownloader::GotFileHandleNotify);
399   file_io_private_interface_->RequestOSFileHandle(
400       file_reader_.pp_resource(), cb.output(), cb.pp_completion_callback());
401 }
402
403 void FileDownloader::GotFileHandleNotify(int32_t pp_error,
404                                          PP_FileHandle handle) {
405   PLUGIN_PRINTF((
406       "FileDownloader::GotFileHandleNotify (pp_error=%" NACL_PRId32 ")\n",
407       pp_error));
408   if (pp_error == PP_OK) {
409     NaClFileInfo tmp_info = NoFileInfo();
410     tmp_info.desc = ConvertFileDescriptor(handle);
411     file_info_.TakeOwnership(&tmp_info);
412   }
413
414   stream_finish_callback_.RunAndClear(pp_error);
415 }
416
417 }  // namespace plugin