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