ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / fileapi / file_stream_reader.cc
blob95766d2190894aa6dc43035c157d24cd6481cc61
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.
5 #include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
7 #include "base/files/file.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/trace_event/trace_event.h"
10 #include "chrome/browser/chromeos/file_system_provider/abort_callback.h"
11 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
12 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
13 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
18 using content::BrowserThread;
20 namespace chromeos {
21 namespace file_system_provider {
22 namespace {
24 // Dicards the callback from CloseFile().
25 void EmptyStatusCallback(base::File::Error /* result */) {
28 // Converts net::CompletionCallback to net::Int64CompletionCallback.
29 void Int64ToIntCompletionCallback(net::CompletionCallback callback,
30 int64 result) {
31 callback.Run(static_cast<int>(result));
34 } // namespace
36 class FileStreamReader::OperationRunner
37 : public base::RefCountedThreadSafe<FileStreamReader::OperationRunner> {
38 public:
39 OperationRunner() : file_handle_(-1) {}
41 // Opens a file for reading and calls the completion callback. Must be called
42 // on UI thread.
43 void OpenFileOnUIThread(
44 const storage::FileSystemURL& url,
45 const storage::AsyncFileUtil::StatusCallback& callback) {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI);
47 DCHECK(abort_callback_.is_null());
49 util::FileSystemURLParser parser(url);
50 if (!parser.Parse()) {
51 BrowserThread::PostTask(
52 BrowserThread::IO,
53 FROM_HERE,
54 base::Bind(callback, base::File::FILE_ERROR_SECURITY));
55 return;
58 file_system_ = parser.file_system()->GetWeakPtr();
59 file_path_ = parser.file_path();
60 abort_callback_ = parser.file_system()->OpenFile(
61 file_path_, OPEN_FILE_MODE_READ,
62 base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread, this,
63 callback));
66 // Closes a file. Ignores result, since it is called from a constructor.
67 // Must be called on UI thread.
68 void CloseFileOnUIThread() {
69 DCHECK_CURRENTLY_ON(BrowserThread::UI);
70 DCHECK(abort_callback_.is_null());
72 if (file_system_.get() && file_handle_ != -1) {
73 // Closing a file must not be aborted, since we could end up on files
74 // which are never closed.
75 file_system_->CloseFile(file_handle_, base::Bind(&EmptyStatusCallback));
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
81 // set to false. This function guarantees that it will succeed only if the
82 // file has not been changed while reading. Must be called on UI thread.
83 void ReadFileOnUIThread(
84 scoped_refptr<net::IOBuffer> buffer,
85 int64 offset,
86 int length,
87 const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
88 DCHECK_CURRENTLY_ON(BrowserThread::UI);
89 DCHECK(abort_callback_.is_null());
91 // If the file system got unmounted, then abort the reading operation.
92 if (!file_system_.get()) {
93 BrowserThread::PostTask(
94 BrowserThread::IO,
95 FROM_HERE,
96 base::Bind(
97 callback, 0, false /* has_more */, base::File::FILE_ERROR_ABORT));
98 return;
101 abort_callback_ = file_system_->ReadFile(
102 file_handle_,
103 buffer.get(),
104 offset,
105 length,
106 base::Bind(
107 &OperationRunner::OnReadFileCompletedOnUIThread, this, callback));
110 // Requests metadata of a file. In case of either succes or a failure,
111 // |callback| is executed. Must be called on UI thread.
112 void GetMetadataOnUIThread(
113 const ProvidedFileSystemInterface::GetMetadataCallback& callback) {
114 DCHECK_CURRENTLY_ON(BrowserThread::UI);
115 DCHECK(abort_callback_.is_null());
117 // If the file system got unmounted, then abort the get length operation.
118 if (!file_system_.get()) {
119 BrowserThread::PostTask(
120 BrowserThread::IO,
121 FROM_HERE,
122 base::Bind(callback,
123 base::Passed(make_scoped_ptr<EntryMetadata>(NULL)),
124 base::File::FILE_ERROR_ABORT));
125 return;
128 abort_callback_ = file_system_->GetMetadata(
129 file_path_,
130 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT,
131 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread,
132 this,
133 callback));
136 // Aborts the most recent operation (if exists), and calls the callback.
137 void AbortOnUIThread() {
138 DCHECK_CURRENTLY_ON(BrowserThread::UI);
139 if (abort_callback_.is_null())
140 return;
142 const AbortCallback last_abort_callback = abort_callback_;
143 abort_callback_ = AbortCallback();
144 last_abort_callback.Run();
147 private:
148 friend class base::RefCountedThreadSafe<OperationRunner>;
150 virtual ~OperationRunner() {}
152 // Remembers a file handle for further operations and forwards the result to
153 // the IO thread.
154 void OnOpenFileCompletedOnUIThread(
155 const storage::AsyncFileUtil::StatusCallback& callback,
156 int file_handle,
157 base::File::Error result) {
158 DCHECK_CURRENTLY_ON(BrowserThread::UI);
159 abort_callback_ = AbortCallback();
161 if (result == base::File::FILE_OK)
162 file_handle_ = file_handle;
164 BrowserThread::PostTask(
165 BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
168 // Forwards a metadata to the IO thread.
169 void OnGetMetadataCompletedOnUIThread(
170 const ProvidedFileSystemInterface::GetMetadataCallback& callback,
171 scoped_ptr<EntryMetadata> metadata,
172 base::File::Error result) {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI);
174 abort_callback_ = AbortCallback();
176 BrowserThread::PostTask(
177 BrowserThread::IO,
178 FROM_HERE,
179 base::Bind(callback, base::Passed(&metadata), result));
182 // Forwards a response of reading from a file to the IO thread.
183 void OnReadFileCompletedOnUIThread(
184 const ProvidedFileSystemInterface::ReadChunkReceivedCallback&
185 chunk_received_callback,
186 int chunk_length,
187 bool has_more,
188 base::File::Error result) {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI);
190 if (!has_more)
191 abort_callback_ = AbortCallback();
193 BrowserThread::PostTask(
194 BrowserThread::IO,
195 FROM_HERE,
196 base::Bind(chunk_received_callback, chunk_length, has_more, result));
199 AbortCallback abort_callback_;
200 base::WeakPtr<ProvidedFileSystemInterface> file_system_;
201 base::FilePath file_path_;
202 int file_handle_;
204 DISALLOW_COPY_AND_ASSIGN(OperationRunner);
207 FileStreamReader::FileStreamReader(storage::FileSystemContext* context,
208 const storage::FileSystemURL& url,
209 int64 initial_offset,
210 const base::Time& expected_modification_time)
211 : url_(url),
212 current_offset_(initial_offset),
213 current_length_(0),
214 expected_modification_time_(expected_modification_time),
215 runner_(new OperationRunner),
216 state_(NOT_INITIALIZED),
217 weak_ptr_factory_(this) {
220 FileStreamReader::~FileStreamReader() {
221 // FileStreamReader doesn't have a Cancel() method like in FileStreamWriter.
222 // Therefore, aborting is done from the destructor.
223 BrowserThread::PostTask(
224 BrowserThread::UI, FROM_HERE,
225 base::Bind(&OperationRunner::AbortOnUIThread, runner_));
227 BrowserThread::PostTask(
228 BrowserThread::UI,
229 FROM_HERE,
230 base::Bind(&OperationRunner::CloseFileOnUIThread, runner_));
233 void FileStreamReader::Initialize(
234 const base::Closure& pending_closure,
235 const net::Int64CompletionCallback& error_callback) {
236 DCHECK_EQ(NOT_INITIALIZED, state_);
237 state_ = INITIALIZING;
239 BrowserThread::PostTask(
240 BrowserThread::UI,
241 FROM_HERE,
242 base::Bind(&OperationRunner::OpenFileOnUIThread,
243 runner_,
244 url_,
245 base::Bind(&FileStreamReader::OnOpenFileCompleted,
246 weak_ptr_factory_.GetWeakPtr(),
247 pending_closure,
248 error_callback)));
251 void FileStreamReader::OnOpenFileCompleted(
252 const base::Closure& pending_closure,
253 const net::Int64CompletionCallback& error_callback,
254 base::File::Error result) {
255 DCHECK_CURRENTLY_ON(BrowserThread::IO);
256 DCHECK_EQ(INITIALIZING, state_);
258 // In case of an error, return immediately using the |error_callback| of the
259 // Read() or GetLength() pending request.
260 if (result != base::File::FILE_OK) {
261 state_ = FAILED;
262 error_callback.Run(net::FileErrorToNetError(result));
263 return;
266 DCHECK_EQ(base::File::FILE_OK, result);
268 // Verify the last modification time.
269 BrowserThread::PostTask(
270 BrowserThread::UI,
271 FROM_HERE,
272 base::Bind(&OperationRunner::GetMetadataOnUIThread,
273 runner_,
274 base::Bind(&FileStreamReader::OnInitializeCompleted,
275 weak_ptr_factory_.GetWeakPtr(),
276 pending_closure,
277 error_callback)));
280 void FileStreamReader::OnInitializeCompleted(
281 const base::Closure& pending_closure,
282 const net::Int64CompletionCallback& error_callback,
283 scoped_ptr<EntryMetadata> metadata,
284 base::File::Error result) {
285 DCHECK_CURRENTLY_ON(BrowserThread::IO);
286 DCHECK_EQ(INITIALIZING, state_);
288 // In case of an error, abort.
289 if (result != base::File::FILE_OK) {
290 state_ = FAILED;
291 error_callback.Run(net::FileErrorToNetError(result));
292 return;
295 // If the file modification time has changed, then abort. Note, that the file
296 // may be changed without affecting the modification time.
297 DCHECK(metadata.get());
298 if (!expected_modification_time_.is_null() &&
299 metadata->modification_time != expected_modification_time_) {
300 state_ = FAILED;
301 error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
302 return;
305 DCHECK_EQ(base::File::FILE_OK, result);
306 state_ = INITIALIZED;
308 // Run the task waiting for the initialization to be completed.
309 pending_closure.Run();
312 int FileStreamReader::Read(net::IOBuffer* buffer,
313 int buffer_length,
314 const net::CompletionCallback& callback) {
315 DCHECK_CURRENTLY_ON(BrowserThread::IO);
316 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
317 "FileStreamReader::Read",
318 this,
319 "buffer_length",
320 buffer_length);
322 switch (state_) {
323 case NOT_INITIALIZED:
324 // Lazily initialize with the first call to Read().
325 Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized,
326 weak_ptr_factory_.GetWeakPtr(),
327 make_scoped_refptr(buffer),
328 buffer_length,
329 base::Bind(&FileStreamReader::OnReadCompleted,
330 weak_ptr_factory_.GetWeakPtr(),
331 callback)),
332 base::Bind(&Int64ToIntCompletionCallback,
333 base::Bind(&FileStreamReader::OnReadCompleted,
334 weak_ptr_factory_.GetWeakPtr(),
335 callback)));
336 break;
338 case INITIALIZING:
339 NOTREACHED();
340 break;
342 case INITIALIZED:
343 ReadAfterInitialized(buffer,
344 buffer_length,
345 base::Bind(&FileStreamReader::OnReadCompleted,
346 weak_ptr_factory_.GetWeakPtr(),
347 callback));
348 break;
350 case FAILED:
351 NOTREACHED();
352 break;
355 return net::ERR_IO_PENDING;
358 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback,
359 int result) {
360 DCHECK_CURRENTLY_ON(BrowserThread::IO);
361 callback.Run(static_cast<int>(result));
362 TRACE_EVENT_ASYNC_END0(
363 "file_system_provider", "FileStreamReader::Read", this);
366 int64 FileStreamReader::GetLength(
367 const net::Int64CompletionCallback& callback) {
368 DCHECK_CURRENTLY_ON(BrowserThread::IO);
370 switch (state_) {
371 case NOT_INITIALIZED:
372 // Lazily initialize with the first call to GetLength().
373 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized,
374 weak_ptr_factory_.GetWeakPtr(),
375 callback),
376 callback);
377 break;
379 case INITIALIZING:
380 NOTREACHED();
381 break;
383 case INITIALIZED:
384 GetLengthAfterInitialized(callback);
385 break;
387 case FAILED:
388 NOTREACHED();
389 break;
392 return net::ERR_IO_PENDING;
395 void FileStreamReader::ReadAfterInitialized(
396 scoped_refptr<net::IOBuffer> buffer,
397 int buffer_length,
398 const net::CompletionCallback& callback) {
399 DCHECK_CURRENTLY_ON(BrowserThread::IO);
400 DCHECK_EQ(INITIALIZED, state_);
402 current_length_ = 0;
403 BrowserThread::PostTask(
404 BrowserThread::UI,
405 FROM_HERE,
406 base::Bind(&OperationRunner::ReadFileOnUIThread,
407 runner_,
408 buffer,
409 current_offset_,
410 buffer_length,
411 base::Bind(&FileStreamReader::OnReadChunkReceived,
412 weak_ptr_factory_.GetWeakPtr(),
413 callback)));
416 void FileStreamReader::GetLengthAfterInitialized(
417 const net::Int64CompletionCallback& callback) {
418 DCHECK_CURRENTLY_ON(BrowserThread::IO);
419 DCHECK_EQ(INITIALIZED, state_);
421 BrowserThread::PostTask(
422 BrowserThread::UI,
423 FROM_HERE,
424 base::Bind(
425 &OperationRunner::GetMetadataOnUIThread,
426 runner_,
427 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived,
428 weak_ptr_factory_.GetWeakPtr(),
429 callback)));
432 void FileStreamReader::OnReadChunkReceived(
433 const net::CompletionCallback& callback,
434 int chunk_length,
435 bool has_more,
436 base::File::Error result) {
437 DCHECK_CURRENTLY_ON(BrowserThread::IO);
438 DCHECK_EQ(INITIALIZED, state_);
440 current_length_ += chunk_length;
442 // If this is the last chunk with a success, then finalize.
443 if (!has_more && result == base::File::FILE_OK) {
444 current_offset_ += current_length_;
445 callback.Run(current_length_);
446 return;
449 // In case of an error, abort.
450 if (result != base::File::FILE_OK) {
451 DCHECK(!has_more);
452 state_ = FAILED;
453 callback.Run(net::FileErrorToNetError(result));
454 return;
457 // More data is about to come, so do not call the callback yet.
458 DCHECK(has_more);
461 void FileStreamReader::OnGetMetadataForGetLengthReceived(
462 const net::Int64CompletionCallback& callback,
463 scoped_ptr<EntryMetadata> metadata,
464 base::File::Error result) {
465 DCHECK_CURRENTLY_ON(BrowserThread::IO);
466 DCHECK_EQ(INITIALIZED, state_);
468 // In case of an error, abort.
469 if (result != base::File::FILE_OK) {
470 state_ = FAILED;
471 callback.Run(net::FileErrorToNetError(result));
472 return;
475 // If the file modification time has changed, then abort. Note, that the file
476 // may be changed without affecting the modification time.
477 DCHECK(metadata.get());
478 if (!expected_modification_time_.is_null() &&
479 metadata->modification_time != expected_modification_time_) {
480 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
481 return;
484 DCHECK_EQ(base::File::FILE_OK, result);
485 callback.Run(metadata->size);
488 } // namespace file_system_provider
489 } // namespace chromeos