Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_system_provider / fileapi / file_stream_reader.cc
1 // Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
6
7 #include "base/debug/trace_event.h"
8 #include "base/files/file.h"
9 #include "base/memory/ref_counted.h"
10 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
11 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
12 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16
17 using content::BrowserThread;
18
19 namespace chromeos {
20 namespace file_system_provider {
21 namespace {
22
23 // Dicards the callback from CloseFile().
24 void EmptyStatusCallback(base::File::Error /* result */) {
25 }
26
27 // Converts net::CompletionCallback to net::Int64CompletionCallback.
28 void Int64ToIntCompletionCallback(net::CompletionCallback callback,
29                                   int64 result) {
30   callback.Run(static_cast<int>(result));
31 }
32
33 // Opens a file for reading and calls the completion callback. Must be called
34 // on UI thread.
35 void OpenFileOnUIThread(
36     const fileapi::FileSystemURL& url,
37     const FileStreamReader::OpenFileCompletedCallback& callback) {
38   DCHECK_CURRENTLY_ON(BrowserThread::UI);
39
40   util::FileSystemURLParser parser(url);
41   if (!parser.Parse()) {
42     callback.Run(base::WeakPtr<ProvidedFileSystemInterface>(),
43                  base::FilePath(),
44                  0 /* file_handle */,
45                  base::File::FILE_ERROR_SECURITY);
46     return;
47   }
48
49   parser.file_system()->OpenFile(
50       parser.file_path(),
51       ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
52       base::Bind(
53           callback, parser.file_system()->GetWeakPtr(), parser.file_path()));
54 }
55
56 // Forwards results of calling OpenFileOnUIThread back to the IO thread.
57 void OnOpenFileCompletedOnUIThread(
58     const FileStreamReader::OpenFileCompletedCallback& callback,
59     base::WeakPtr<ProvidedFileSystemInterface> file_system,
60     const base::FilePath& file_path,
61     int file_handle,
62     base::File::Error result) {
63   DCHECK_CURRENTLY_ON(BrowserThread::UI);
64   BrowserThread::PostTask(
65       BrowserThread::IO,
66       FROM_HERE,
67       base::Bind(callback, file_system, file_path, file_handle, result));
68 }
69
70 // Closes a file. Ignores result, since it is called from a constructor.
71 // Must be called on UI thread.
72 void CloseFileOnUIThread(base::WeakPtr<ProvidedFileSystemInterface> file_system,
73                          int file_handle) {
74   DCHECK_CURRENTLY_ON(BrowserThread::UI);
75   if (file_system.get())
76     file_system->CloseFile(file_handle, base::Bind(&EmptyStatusCallback));
77 }
78
79 // Requests reading contents of a file. In case of either success or a failure
80 // |callback| is executed. It can be called many times, until |has_more| is set
81 // to false. This function guarantees that it will succeed only if the file has
82 // not been changed while reading. Must be called on UI thread.
83 void ReadFileOnUIThread(
84     base::WeakPtr<ProvidedFileSystemInterface> file_system,
85     int file_handle,
86     scoped_refptr<net::IOBuffer> buffer,
87     int64 offset,
88     int length,
89     const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
90   DCHECK_CURRENTLY_ON(BrowserThread::UI);
91
92   // If the file system got unmounted, then abort the reading operation.
93   if (!file_system.get()) {
94     callback.Run(0, false /* has_more */, base::File::FILE_ERROR_ABORT);
95     return;
96   }
97
98   file_system->ReadFile(file_handle, buffer, offset, length, callback);
99 }
100
101 // Forward the completion callback to IO thread.
102 void OnReadChunkReceivedOnUIThread(
103     const ProvidedFileSystemInterface::ReadChunkReceivedCallback&
104         chunk_received_callback,
105     int chunk_length,
106     bool has_more,
107     base::File::Error result) {
108   DCHECK_CURRENTLY_ON(BrowserThread::UI);
109   BrowserThread::PostTask(
110       BrowserThread::IO,
111       FROM_HERE,
112       base::Bind(chunk_received_callback, chunk_length, has_more, result));
113 }
114
115 // Requests metadata of a file. In case of either succes or a failure,
116 // |callback is executed. Must be called on UI thread.
117 void GetMetadataOnUIThread(
118     base::WeakPtr<ProvidedFileSystemInterface> file_system,
119     const base::FilePath& file_path,
120     const ProvidedFileSystemInterface::GetMetadataCallback& callback) {
121   DCHECK_CURRENTLY_ON(BrowserThread::UI);
122
123   // If the file system got unmounted, then abort the get length operation.
124   if (!file_system.get()) {
125     callback.Run(EntryMetadata(), base::File::FILE_ERROR_ABORT);
126     return;
127   }
128
129   file_system->GetMetadata(file_path, callback);
130 }
131
132 // Forward the completion callback to IO thread.
133 void OnGetMetadataReceivedOnUIThread(
134     const ProvidedFileSystemInterface::GetMetadataCallback& callback,
135     const EntryMetadata& metadata,
136     base::File::Error result) {
137   DCHECK_CURRENTLY_ON(BrowserThread::UI);
138   BrowserThread::PostTask(
139       BrowserThread::IO, FROM_HERE, base::Bind(callback, metadata, result));
140 }
141
142 }  // namespace
143
144 FileStreamReader::FileStreamReader(fileapi::FileSystemContext* context,
145                                    const fileapi::FileSystemURL& url,
146                                    int64 initial_offset,
147                                    const base::Time& expected_modification_time)
148     : url_(url),
149       current_offset_(initial_offset),
150       current_length_(0),
151       expected_modification_time_(expected_modification_time),
152       state_(NOT_INITIALIZED),
153       file_handle_(0),
154       weak_ptr_factory_(this) {
155 }
156
157 FileStreamReader::~FileStreamReader() {
158   BrowserThread::PostTask(
159       BrowserThread::UI,
160       FROM_HERE,
161       base::Bind(&CloseFileOnUIThread, file_system_, file_handle_));
162 }
163
164 void FileStreamReader::Initialize(
165     const base::Closure& pending_closure,
166     const net::Int64CompletionCallback& error_callback) {
167   DCHECK_EQ(NOT_INITIALIZED, state_);
168   state_ = INITIALIZING;
169
170   BrowserThread::PostTask(
171       BrowserThread::UI,
172       FROM_HERE,
173       base::Bind(&OpenFileOnUIThread,
174                  url_,
175                  base::Bind(&OnOpenFileCompletedOnUIThread,
176                             base::Bind(&FileStreamReader::OnOpenFileCompleted,
177                                        weak_ptr_factory_.GetWeakPtr(),
178                                        pending_closure,
179                                        error_callback))));
180 }
181
182 void FileStreamReader::OnOpenFileCompleted(
183     const base::Closure& pending_closure,
184     const net::Int64CompletionCallback& error_callback,
185     base::WeakPtr<ProvidedFileSystemInterface> file_system,
186     const base::FilePath& file_path,
187     int file_handle,
188     base::File::Error result) {
189   DCHECK_CURRENTLY_ON(BrowserThread::IO);
190   DCHECK_EQ(INITIALIZING, state_);
191
192   // In case of an error, return immediately using the |error_callback| of the
193   // Read() or GetLength() pending request.
194   if (result != base::File::FILE_OK) {
195     state_ = FAILED;
196     error_callback.Run(net::FileErrorToNetError(result));
197     return;
198   }
199
200   file_system_ = file_system;
201   file_path_ = file_path;
202   file_handle_ = file_handle;
203   DCHECK_LT(0, file_handle);
204
205   // Verify the last modification time.
206   BrowserThread::PostTask(
207       BrowserThread::UI,
208       FROM_HERE,
209       base::Bind(&GetMetadataOnUIThread,
210                  file_system_,
211                  file_path_,
212                  base::Bind(&OnGetMetadataReceivedOnUIThread,
213                             base::Bind(&FileStreamReader::OnInitializeCompleted,
214                                        weak_ptr_factory_.GetWeakPtr(),
215                                        pending_closure,
216                                        error_callback))));
217 }
218
219 void FileStreamReader::OnInitializeCompleted(
220     const base::Closure& pending_closure,
221     const net::Int64CompletionCallback& error_callback,
222     const EntryMetadata& metadata,
223     base::File::Error result) {
224   DCHECK_CURRENTLY_ON(BrowserThread::IO);
225   DCHECK_EQ(INITIALIZING, state_);
226
227   // In case of an error, abort.
228   if (result != base::File::FILE_OK) {
229     state_ = FAILED;
230     error_callback.Run(net::FileErrorToNetError(result));
231     return;
232   }
233
234   // If the file modification time has changed, then abort. Note, that the file
235   // may be changed without affecting the modification time.
236   if (!expected_modification_time_.is_null() &&
237       metadata.modification_time != expected_modification_time_) {
238     state_ = FAILED;
239     error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
240     return;
241   }
242
243   DCHECK_EQ(base::File::FILE_OK, result);
244   state_ = INITIALIZED;
245
246   // Run the task waiting for the initialization to be completed.
247   pending_closure.Run();
248 }
249
250 int FileStreamReader::Read(net::IOBuffer* buffer,
251                            int buffer_length,
252                            const net::CompletionCallback& callback) {
253   DCHECK_CURRENTLY_ON(BrowserThread::IO);
254   TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
255                            "FileStreamReader::Read",
256                            this,
257                            "buffer_length",
258                            buffer_length);
259
260   switch (state_) {
261     case NOT_INITIALIZED:
262       // Lazily initialize with the first call to Read().
263     Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized,
264                           weak_ptr_factory_.GetWeakPtr(),
265                           make_scoped_refptr(buffer),
266                           buffer_length,
267                           base::Bind(&FileStreamReader::OnReadCompleted,
268                                      weak_ptr_factory_.GetWeakPtr(),
269                                      callback)),
270                base::Bind(&Int64ToIntCompletionCallback,
271                           base::Bind(&FileStreamReader::OnReadCompleted,
272                                      weak_ptr_factory_.GetWeakPtr(),
273                                      callback)));
274     break;
275
276     case INITIALIZING:
277       NOTREACHED();
278       break;
279
280     case INITIALIZED:
281       ReadAfterInitialized(buffer,
282                            buffer_length,
283                            base::Bind(&FileStreamReader::OnReadCompleted,
284                                       weak_ptr_factory_.GetWeakPtr(),
285                                       callback));
286       break;
287
288     case FAILED:
289       NOTREACHED();
290       break;
291   }
292
293   return net::ERR_IO_PENDING;
294 }
295
296 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback,
297                                        int result) {
298   DCHECK_CURRENTLY_ON(BrowserThread::IO);
299   callback.Run(static_cast<int>(result));
300   TRACE_EVENT_ASYNC_END0(
301       "file_system_provider", "FileStreamReader::Read", this);
302 }
303
304 int64 FileStreamReader::GetLength(
305     const net::Int64CompletionCallback& callback) {
306   DCHECK_CURRENTLY_ON(BrowserThread::IO);
307
308   switch (state_) {
309     case NOT_INITIALIZED:
310       // Lazily initialize with the first call to GetLength().
311     Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized,
312                           weak_ptr_factory_.GetWeakPtr(),
313                           callback),
314                callback);
315     break;
316
317     case INITIALIZING:
318       NOTREACHED();
319       break;
320
321     case INITIALIZED:
322       GetLengthAfterInitialized(callback);
323       break;
324
325     case FAILED:
326       NOTREACHED();
327       break;
328   }
329
330   return net::ERR_IO_PENDING;
331 }
332
333 void FileStreamReader::ReadAfterInitialized(
334     scoped_refptr<net::IOBuffer> buffer,
335     int buffer_length,
336     const net::CompletionCallback& callback) {
337   DCHECK_CURRENTLY_ON(BrowserThread::IO);
338   DCHECK_EQ(INITIALIZED, state_);
339
340   current_length_ = 0;
341   BrowserThread::PostTask(
342       BrowserThread::UI,
343       FROM_HERE,
344       base::Bind(&ReadFileOnUIThread,
345                  file_system_,
346                  file_handle_,
347                  buffer,
348                  current_offset_,
349                  buffer_length,
350                  base::Bind(&OnReadChunkReceivedOnUIThread,
351                             base::Bind(&FileStreamReader::OnReadChunkReceived,
352                                        weak_ptr_factory_.GetWeakPtr(),
353                                        callback))));
354 }
355
356 void FileStreamReader::GetLengthAfterInitialized(
357     const net::Int64CompletionCallback& callback) {
358   DCHECK_CURRENTLY_ON(BrowserThread::IO);
359   DCHECK_EQ(INITIALIZED, state_);
360
361   BrowserThread::PostTask(
362       BrowserThread::UI,
363       FROM_HERE,
364       base::Bind(
365           &GetMetadataOnUIThread,
366           file_system_,
367           file_path_,
368           base::Bind(
369               &OnGetMetadataReceivedOnUIThread,
370               base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived,
371                          weak_ptr_factory_.GetWeakPtr(),
372                          callback))));
373 }
374
375 void FileStreamReader::OnReadChunkReceived(
376     const net::CompletionCallback& callback,
377     int chunk_length,
378     bool has_more,
379     base::File::Error result) {
380   DCHECK_CURRENTLY_ON(BrowserThread::IO);
381   DCHECK_EQ(INITIALIZED, state_);
382
383   current_length_ += chunk_length;
384
385   // If this is the last chunk with a success, then finalize.
386   if (!has_more && result == base::File::FILE_OK) {
387     current_offset_ += current_length_;
388     callback.Run(current_length_);
389     return;
390   }
391
392   // In case of an error, abort.
393   if (result != base::File::FILE_OK) {
394     DCHECK(!has_more);
395     state_ = FAILED;
396     callback.Run(net::FileErrorToNetError(result));
397     return;
398   }
399
400   // More data is about to come, so do not call the callback yet.
401   DCHECK(has_more);
402 }
403
404 void FileStreamReader::OnGetMetadataForGetLengthReceived(
405     const net::Int64CompletionCallback& callback,
406     const EntryMetadata& metadata,
407     base::File::Error result) {
408   DCHECK_CURRENTLY_ON(BrowserThread::IO);
409   DCHECK_EQ(INITIALIZED, state_);
410
411   // In case of an error, abort.
412   if (result != base::File::FILE_OK) {
413     state_ = FAILED;
414     callback.Run(net::FileErrorToNetError(result));
415     return;
416   }
417
418   // If the file modification time has changed, then abort. Note, that the file
419   // may be changed without affecting the modification time.
420   if (!expected_modification_time_.is_null() &&
421       metadata.modification_time != expected_modification_time_) {
422     callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
423     return;
424   }
425
426   DCHECK_EQ(base::File::FILE_OK, result);
427   callback.Run(metadata.size);
428 }
429
430 }  // namespace file_system_provider
431 }  // namespace chromeos