1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #ifdef OS_NACL
31 
32 #include "chrome/nacl/url_loader_util.h"
33 
34 #include <algorithm>
35 #include <memory>
36 
37 #include <ppapi/c/pp_file_info.h>
38 #include <ppapi/c/ppb_file_io.h>
39 #include <ppapi/cpp/file_io.h>
40 #include <ppapi/cpp/file_ref.h>
41 #include <ppapi/cpp/file_system.h>
42 #include <ppapi/cpp/instance.h>
43 #include <ppapi/cpp/url_loader.h>
44 #include <ppapi/cpp/url_request_info.h>
45 #include <ppapi/cpp/url_response_info.h>
46 #include <ppapi/utility/completion_callback_factory.h>
47 
48 #include "base/logging.h"
49 #include "base/port.h"
50 
51 using std::unique_ptr;
52 
53 namespace mozc {
54 namespace chrome {
55 namespace nacl {
56 
57 namespace {
58 const int32_t kReadBufferSize = 32768;
59 
60 class URLLoaderStreamToFileHandler {
61  public:
62   URLLoaderStreamToFileHandler(pp::Instance *instance,
63                                const string &url,
64                                const string &file_name,
65                                pp::CompletionCallback callback);
66   void Start();
67 
68  private:
69   // "delete this" is called in URLLoaderStreamToFileHandler::Complete().
70   ~URLLoaderStreamToFileHandler();
71   void StartImpl(int32_t result);
72   void OnOpen(int32 result);
73   void OnStreamComplete(int32 result);
74   void OnInputFileOpen(int32_t result);
75   void OnInputFileQuery(int32_t result);
76   void OnFileSystemOpen(int32_t result);
77   void OnDeleteOutputFile(int32_t result);
78   void OnOutputFileOpen(int32_t result);
79   void OnInputFileRead(int32_t bytes_read);
80   void OnOutputFileWrite(int32_t bytes_written);
81   void OnOutputFileFlush(int32_t result);
82   void Complete(bool result);
83 
84   pp::Instance *instance_;
85   const string url_;
86   const string file_name_;
87   pp::CompletionCallback callback_;
88   unique_ptr<pp::URLRequestInfo> url_request_;
89   unique_ptr<pp::URLLoader> url_loader_;
90   pp::URLResponseInfo url_response_;
91   pp::FileRef body_file_ref_;
92   pp::CompletionCallbackFactory<URLLoaderStreamToFileHandler> callback_factory_;
93   unique_ptr<pp::FileSystem> file_system_;
94   unique_ptr<pp::FileRef> output_file_ref_;
95   unique_ptr<pp::FileIO> output_file_io_;
96   unique_ptr<pp::FileIO> input_file_io_;
97   PP_FileInfo input_file_info_;
98   int64_t total_read_bytes_;
99   int64_t total_written_bytes_;
100   int64_t buffer_written_bytes_;
101   unique_ptr<char[]> tmp_buffer_;
102 
103   DISALLOW_COPY_AND_ASSIGN(URLLoaderStreamToFileHandler);
104 };
105 
URLLoaderStreamToFileHandler(pp::Instance * instance,const string & url,const string & file_name,pp::CompletionCallback callback)106 URLLoaderStreamToFileHandler::URLLoaderStreamToFileHandler(
107     pp::Instance *instance,
108     const string &url,
109     const string &file_name,
110     pp::CompletionCallback callback)
111     : instance_(instance),
112       url_(url),
113       file_name_(file_name),
114       callback_(callback),
115       total_read_bytes_(0),
116       total_written_bytes_(0),
117       buffer_written_bytes_(0) {
118 }
119 
~URLLoaderStreamToFileHandler()120 URLLoaderStreamToFileHandler::~URLLoaderStreamToFileHandler() {
121 }
122 
Start()123 void URLLoaderStreamToFileHandler::Start() {
124   callback_factory_.Initialize(this);
125   if (pp::Module::Get()->core()->IsMainThread()) {
126     StartImpl(0);
127     return;
128   }
129   pp::Module::Get()->core()->CallOnMainThread(
130       0,
131       callback_factory_.NewCallback(&URLLoaderStreamToFileHandler::StartImpl));
132 }
133 
StartImpl(int32_t result)134 void URLLoaderStreamToFileHandler::StartImpl(int32_t result) {
135   CHECK(pp::Module::Get()->core()->IsMainThread());
136   CHECK(!url_request_.get());
137   CHECK(!url_loader_.get());
138   url_request_.reset(new pp::URLRequestInfo(instance_));
139   url_loader_.reset(new pp::URLLoader(instance_));
140   url_request_->SetAllowCrossOriginRequests(true);
141   url_request_->SetURL(url_);
142   url_request_->SetMethod("GET");
143   url_request_->SetStreamToFile(true);
144   url_loader_->Open(*url_request_,
145                     callback_factory_.NewCallback(
146                         &URLLoaderStreamToFileHandler::OnOpen));
147 }
148 
OnOpen(int32 result)149 void URLLoaderStreamToFileHandler::OnOpen(int32 result) {
150   if (result != PP_OK) {
151     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnOpen error";
152     Complete(false);
153     return;
154   }
155   const pp::URLResponseInfo response = url_loader_->GetResponseInfo();
156   if (response.GetStatusCode() != 200) {
157     DLOG(ERROR) << "pp::URLLoader::Open() failed: " << url_
158                 << " Status code: " << response.GetStatusCode();
159     Complete(false);
160     return;
161   }
162   url_loader_->FinishStreamingToFile(
163       callback_factory_.NewCallback(
164           &URLLoaderStreamToFileHandler::OnStreamComplete));
165   url_response_ = url_loader_->GetResponseInfo();
166   body_file_ref_ = url_response_.GetBodyAsFileRef();
167 }
168 
OnStreamComplete(int32 result)169 void URLLoaderStreamToFileHandler::OnStreamComplete(int32 result) {
170   if (result != PP_OK) {
171     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnStreamComplete error";
172     Complete(false);
173     return;
174   }
175   input_file_io_.reset(new pp::FileIO(instance_));
176   const int32_t ret = input_file_io_->Open(
177       body_file_ref_, PP_FILEOPENFLAG_READ,
178       callback_factory_.NewCallback(
179           &URLLoaderStreamToFileHandler::OnInputFileOpen));
180   if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
181     DLOG(ERROR) << "input_file_io_->Open error";
182     Complete(false);
183     return;
184   }
185 }
186 
OnInputFileOpen(int32_t result)187 void URLLoaderStreamToFileHandler::OnInputFileOpen(int32_t result) {
188   if (result != PP_OK) {
189     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnInputFileOpen error";
190     Complete(false);
191     return;
192   }
193   const int32_t ret = input_file_io_->Query(
194       &input_file_info_,
195       callback_factory_.NewCallback(
196           &URLLoaderStreamToFileHandler::OnInputFileQuery));
197   if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
198     DLOG(ERROR) << "input_file_io_->Query error";
199     Complete(false);
200     return;
201   }
202 }
OnInputFileQuery(int32_t result)203 void URLLoaderStreamToFileHandler::OnInputFileQuery(int32_t result) {
204   if (result != PP_OK) {
205     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnInputFileQuery error";
206     Complete(false);
207     return;
208   }
209   file_system_.reset(
210       new pp::FileSystem(instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT));
211   const int32_t ret = file_system_->Open(
212       input_file_info_.size,
213       callback_factory_.NewCallback(
214           &URLLoaderStreamToFileHandler::OnFileSystemOpen));
215   if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
216     DLOG(ERROR) << "file_system_->Open error";
217     Complete(false);
218     return;
219   }
220 }
221 
OnFileSystemOpen(int32_t result)222 void URLLoaderStreamToFileHandler::OnFileSystemOpen(int32_t result) {
223   if (result != PP_OK) {
224     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnFileSystemOpen error";
225     Complete(false);
226     return;
227   }
228   output_file_ref_.reset(new pp::FileRef(*file_system_, file_name_.c_str()));
229   const int32_t ret = output_file_ref_->Delete(
230       callback_factory_.NewCallback(
231           &URLLoaderStreamToFileHandler::OnDeleteOutputFile));
232   if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
233     DLOG(ERROR) << "output_file_ref_->Delete error";
234     Complete(false);
235     return;
236   }
237 }
238 
OnDeleteOutputFile(int32_t result)239 void URLLoaderStreamToFileHandler::OnDeleteOutputFile(int32_t result) {
240   output_file_io_.reset(new pp::FileIO(instance_));
241   const int32_t ret = output_file_io_->Open(
242       *output_file_ref_, PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE,
243       callback_factory_.NewCallback(
244           &URLLoaderStreamToFileHandler::OnOutputFileOpen));
245   if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
246     DLOG(ERROR) << "output_file_io_->Open error";
247     Complete(false);
248     return;
249   }
250 }
251 
OnOutputFileOpen(int32_t result)252 void URLLoaderStreamToFileHandler::OnOutputFileOpen(int32_t result) {
253   if (result != PP_OK) {
254     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnOutputFileOpen error";
255     Complete(false);
256     return;
257   }
258   tmp_buffer_.reset(new char[kReadBufferSize]);
259   OnInputFileRead(0);
260 }
261 
OnInputFileRead(int32_t bytes_read)262 void URLLoaderStreamToFileHandler::OnInputFileRead(int32_t bytes_read) {
263   if (bytes_read < 0) {
264     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnInputFileRead error";
265     Complete(false);
266     return;
267   }
268   total_read_bytes_ += bytes_read;
269   if (bytes_read == 0) {
270     const int32_t ret = input_file_io_->Read(
271         total_read_bytes_, tmp_buffer_.get(),
272         std::min(
273             kReadBufferSize,
274             static_cast<int32_t>(input_file_info_.size - total_read_bytes_)),
275         callback_factory_.NewCallback(
276             &URLLoaderStreamToFileHandler::OnInputFileRead));
277     if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
278       DLOG(ERROR) << "input_file_io_->Read error";
279       Complete(false);
280       return;
281     }
282   } else {
283     buffer_written_bytes_ = 0;
284     const int32_t ret = output_file_io_->Write(
285         total_written_bytes_, tmp_buffer_.get(), bytes_read,
286         callback_factory_.NewCallback(
287             &URLLoaderStreamToFileHandler::OnOutputFileWrite));
288     if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
289       DLOG(ERROR) << "output_file_io_->Write error";
290       Complete(false);
291       return;
292     }
293   }
294 }
295 
OnOutputFileWrite(int32_t bytes_written)296 void URLLoaderStreamToFileHandler::OnOutputFileWrite(int32_t bytes_written) {
297   if (bytes_written < 0) {
298     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnOutputFileWrite error";
299     Complete(false);
300     return;
301   }
302   total_written_bytes_ += bytes_written;
303   buffer_written_bytes_ += bytes_written;
304   if (total_read_bytes_ == total_written_bytes_) {
305     if (total_read_bytes_ == input_file_info_.size) {
306       // Finish writing
307       const int32_t ret = output_file_io_->Flush(
308           callback_factory_.NewCallback(
309               &URLLoaderStreamToFileHandler::OnOutputFileFlush));
310       if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
311         DLOG(ERROR) << "output_file_io_->Flush error";
312         Complete(false);
313         return;
314       }
315     } else {
316       // Read more
317       const int32_t ret = input_file_io_->Read(
318           total_read_bytes_, tmp_buffer_.get(),
319           std::min(
320               kReadBufferSize,
321               static_cast<int32_t>(input_file_info_.size - total_read_bytes_)),
322           callback_factory_.NewCallback(
323               &URLLoaderStreamToFileHandler::OnInputFileRead));
324       if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
325         DLOG(ERROR) << "input_file_io_->Read error";
326         Complete(false);
327         return;
328       }
329     }
330   } else {
331     // Writes more
332     const int32_t ret = output_file_io_->Write(
333         total_written_bytes_,
334         &tmp_buffer_[buffer_written_bytes_],
335         total_read_bytes_ - total_written_bytes_,
336         callback_factory_.NewCallback(
337             &URLLoaderStreamToFileHandler::OnOutputFileWrite));
338     if ((ret != PP_OK_COMPLETIONPENDING) && (ret != PP_OK)) {
339       DLOG(ERROR) << "output_file_io_->Write error";
340       Complete(false);
341       return;
342     }
343   }
344 }
345 
OnOutputFileFlush(int32_t result)346 void URLLoaderStreamToFileHandler::OnOutputFileFlush(int32_t result) {
347   if (result != PP_OK) {
348     DLOG(ERROR) << "URLLoaderStreamToFileHandler::OnOutputFileFlush error";
349     Complete(false);
350     return;
351   }
352   Complete(true);
353 }
354 
Complete(bool result)355 void URLLoaderStreamToFileHandler::Complete(bool result) {
356   callback_.Run(result ? PP_OK : PP_ERROR_FAILED);
357   delete this;
358 }
359 
360 }  // namespace
361 
StartDownloadToFile(pp::Instance * instance,const string & url,const string & file_name,pp::CompletionCallback callback)362 void URLLoaderUtil::StartDownloadToFile(pp::Instance *instance,
363                                         const string &url,
364                                         const string &file_name,
365                                         pp::CompletionCallback callback) {
366   URLLoaderStreamToFileHandler *handler =
367       new URLLoaderStreamToFileHandler(instance,
368                                        url,
369                                        file_name,
370                                        callback);
371   DCHECK(handler);
372   handler->Start();
373 }
374 
375 }  // namespace nacl
376 }  // namespace chrome
377 }  // namespace mozc
378 
379 #endif  // OS_NACL
380