- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / file_system_operation_impl.cc
1 // Copyright 2013 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 "webkit/browser/fileapi/file_system_operation_impl.h"
6
7 #include "base/bind.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "net/base/escape.h"
12 #include "net/url_request/url_request.h"
13 #include "webkit/browser/fileapi/async_file_util.h"
14 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
15 #include "webkit/browser/fileapi/file_observers.h"
16 #include "webkit/browser/fileapi/file_system_backend.h"
17 #include "webkit/browser/fileapi/file_system_context.h"
18 #include "webkit/browser/fileapi/file_system_file_util.h"
19 #include "webkit/browser/fileapi/file_system_operation_context.h"
20 #include "webkit/browser/fileapi/file_system_url.h"
21 #include "webkit/browser/fileapi/file_writer_delegate.h"
22 #include "webkit/browser/fileapi/remove_operation_delegate.h"
23 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
24 #include "webkit/browser/quota/quota_manager.h"
25 #include "webkit/common/blob/shareable_file_reference.h"
26 #include "webkit/common/fileapi/file_system_types.h"
27 #include "webkit/common/fileapi/file_system_util.h"
28 #include "webkit/common/quota/quota_types.h"
29
30 using webkit_blob::ScopedFile;
31
32 namespace fileapi {
33
34 FileSystemOperation* FileSystemOperation::Create(
35     const FileSystemURL& url,
36     FileSystemContext* file_system_context,
37     scoped_ptr<FileSystemOperationContext> operation_context) {
38   return new FileSystemOperationImpl(url, file_system_context,
39                                      operation_context.Pass());
40 }
41
42 FileSystemOperationImpl::~FileSystemOperationImpl() {
43 }
44
45 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
46                                          bool exclusive,
47                                          const StatusCallback& callback) {
48   DCHECK(SetPendingOperationType(kOperationCreateFile));
49   GetUsageAndQuotaThenRunTask(
50       url,
51       base::Bind(&FileSystemOperationImpl::DoCreateFile,
52                  weak_factory_.GetWeakPtr(), url, callback, exclusive),
53       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
54 }
55
56 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
57                                               bool exclusive,
58                                               bool recursive,
59                                               const StatusCallback& callback) {
60   DCHECK(SetPendingOperationType(kOperationCreateDirectory));
61   GetUsageAndQuotaThenRunTask(
62       url,
63       base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
64                  weak_factory_.GetWeakPtr(), url, callback,
65                  exclusive, recursive),
66       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
67 }
68
69 void FileSystemOperationImpl::Copy(
70     const FileSystemURL& src_url,
71     const FileSystemURL& dest_url,
72     CopyOrMoveOption option,
73     const CopyProgressCallback& progress_callback,
74     const StatusCallback& callback) {
75   DCHECK(SetPendingOperationType(kOperationCopy));
76   DCHECK(!recursive_operation_delegate_);
77
78   // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
79   recursive_operation_delegate_.reset(
80       new CopyOrMoveOperationDelegate(
81           file_system_context(),
82           src_url, dest_url,
83           CopyOrMoveOperationDelegate::OPERATION_COPY,
84           option,
85           progress_callback,
86           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
87                      weak_factory_.GetWeakPtr(), callback)));
88   recursive_operation_delegate_->RunRecursively();
89 }
90
91 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
92                                    const FileSystemURL& dest_url,
93                                    CopyOrMoveOption option,
94                                    const StatusCallback& callback) {
95   DCHECK(SetPendingOperationType(kOperationMove));
96   DCHECK(!recursive_operation_delegate_);
97   recursive_operation_delegate_.reset(
98       new CopyOrMoveOperationDelegate(
99           file_system_context(),
100           src_url, dest_url,
101           CopyOrMoveOperationDelegate::OPERATION_MOVE,
102           option,
103           FileSystemOperation::CopyProgressCallback(),
104           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
105                      weak_factory_.GetWeakPtr(), callback)));
106   recursive_operation_delegate_->RunRecursively();
107 }
108
109 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
110                                               const StatusCallback& callback) {
111   DCHECK(SetPendingOperationType(kOperationDirectoryExists));
112   async_file_util_->GetFileInfo(
113       operation_context_.Pass(), url,
114       base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
115                  weak_factory_.GetWeakPtr(), callback));
116 }
117
118 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
119                                          const StatusCallback& callback) {
120   DCHECK(SetPendingOperationType(kOperationFileExists));
121   async_file_util_->GetFileInfo(
122       operation_context_.Pass(), url,
123       base::Bind(&FileSystemOperationImpl::DidFileExists,
124                  weak_factory_.GetWeakPtr(), callback));
125 }
126
127 void FileSystemOperationImpl::GetMetadata(
128     const FileSystemURL& url, const GetMetadataCallback& callback) {
129   DCHECK(SetPendingOperationType(kOperationGetMetadata));
130   async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
131 }
132
133 void FileSystemOperationImpl::ReadDirectory(
134     const FileSystemURL& url, const ReadDirectoryCallback& callback) {
135   DCHECK(SetPendingOperationType(kOperationReadDirectory));
136   async_file_util_->ReadDirectory(
137       operation_context_.Pass(), url, callback);
138 }
139
140 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
141                                      bool recursive,
142                                      const StatusCallback& callback) {
143   DCHECK(SetPendingOperationType(kOperationRemove));
144   DCHECK(!recursive_operation_delegate_);
145
146   if (recursive) {
147     // For recursive removal, try to delegate the operation to AsyncFileUtil
148     // first. If not supported, it is delegated to RemoveOperationDelegate
149     // in DidDeleteRecursively.
150     async_file_util_->DeleteRecursively(
151         operation_context_.Pass(), url,
152         base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
153                    weak_factory_.GetWeakPtr(), url, callback));
154     return;
155   }
156
157   recursive_operation_delegate_.reset(
158       new RemoveOperationDelegate(
159           file_system_context(), url,
160           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
161                      weak_factory_.GetWeakPtr(), callback)));
162   recursive_operation_delegate_->Run();
163 }
164
165 void FileSystemOperationImpl::Write(
166     const FileSystemURL& url,
167     scoped_ptr<FileWriterDelegate> writer_delegate,
168     scoped_ptr<net::URLRequest> blob_request,
169     const WriteCallback& callback) {
170   DCHECK(SetPendingOperationType(kOperationWrite));
171   file_writer_delegate_ = writer_delegate.Pass();
172   file_writer_delegate_->Start(
173       blob_request.Pass(),
174       base::Bind(&FileSystemOperationImpl::DidWrite,
175                  weak_factory_.GetWeakPtr(), url, callback));
176 }
177
178 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
179                                        const StatusCallback& callback) {
180   DCHECK(SetPendingOperationType(kOperationTruncate));
181   GetUsageAndQuotaThenRunTask(
182       url,
183       base::Bind(&FileSystemOperationImpl::DoTruncate,
184                  weak_factory_.GetWeakPtr(), url, callback, length),
185       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
186 }
187
188 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
189                                         const base::Time& last_access_time,
190                                         const base::Time& last_modified_time,
191                                         const StatusCallback& callback) {
192   DCHECK(SetPendingOperationType(kOperationTouchFile));
193   async_file_util_->Touch(
194       operation_context_.Pass(), url,
195       last_access_time, last_modified_time,
196       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
197                  weak_factory_.GetWeakPtr(), callback));
198 }
199
200 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
201                                        int file_flags,
202                                        base::ProcessHandle peer_handle,
203                                        const OpenFileCallback& callback) {
204   DCHECK(SetPendingOperationType(kOperationOpenFile));
205   peer_handle_ = peer_handle;
206
207   if (file_flags &
208       (base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN)) {
209     callback.Run(base::PLATFORM_FILE_ERROR_FAILED,
210                  base::kInvalidPlatformFileValue,
211                  base::Closure(),
212                  base::kNullProcessHandle);
213     return;
214   }
215   GetUsageAndQuotaThenRunTask(
216       url,
217       base::Bind(&FileSystemOperationImpl::DoOpenFile,
218                  weak_factory_.GetWeakPtr(),
219                  url, callback, file_flags),
220       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
221                  base::kInvalidPlatformFileValue,
222                  base::Closure(),
223                  base::kNullProcessHandle));
224 }
225
226 // We can only get here on a write or truncate that's not yet completed.
227 // We don't support cancelling any other operation at this time.
228 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
229   DCHECK(cancel_callback_.is_null());
230   cancel_callback_ = cancel_callback;
231
232   if (file_writer_delegate_.get()) {
233     DCHECK_EQ(kOperationWrite, pending_operation_);
234     // This will call DidWrite() with ABORT status code.
235     file_writer_delegate_->Cancel();
236   } else if (recursive_operation_delegate_) {
237     // This will call DidFinishOperation() with ABORT status code.
238     recursive_operation_delegate_->Cancel();
239   } else {
240     // For truncate we have no way to cancel the inflight operation (for now).
241     // Let it just run and dispatch cancel callback later.
242     DCHECK_EQ(kOperationTruncate, pending_operation_);
243   }
244 }
245
246 void FileSystemOperationImpl::CreateSnapshotFile(
247     const FileSystemURL& url,
248     const SnapshotFileCallback& callback) {
249   DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
250   async_file_util_->CreateSnapshotFile(
251       operation_context_.Pass(), url, callback);
252 }
253
254 void FileSystemOperationImpl::CopyInForeignFile(
255     const base::FilePath& src_local_disk_file_path,
256     const FileSystemURL& dest_url,
257     const StatusCallback& callback) {
258   DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
259   GetUsageAndQuotaThenRunTask(
260       dest_url,
261       base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
262                  weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
263                  callback),
264       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
265 }
266
267 void FileSystemOperationImpl::RemoveFile(
268     const FileSystemURL& url,
269     const StatusCallback& callback) {
270   DCHECK(SetPendingOperationType(kOperationRemove));
271   async_file_util_->DeleteFile(
272       operation_context_.Pass(), url,
273       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
274                  weak_factory_.GetWeakPtr(), callback));
275 }
276
277 void FileSystemOperationImpl::RemoveDirectory(
278     const FileSystemURL& url,
279     const StatusCallback& callback) {
280   DCHECK(SetPendingOperationType(kOperationRemove));
281   async_file_util_->DeleteDirectory(
282       operation_context_.Pass(), url,
283       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
284                  weak_factory_.GetWeakPtr(), callback));
285 }
286
287 void FileSystemOperationImpl::CopyFileLocal(
288     const FileSystemURL& src_url,
289     const FileSystemURL& dest_url,
290     CopyOrMoveOption option,
291     const CopyFileProgressCallback& progress_callback,
292     const StatusCallback& callback) {
293   DCHECK(SetPendingOperationType(kOperationCopy));
294   DCHECK(src_url.IsInSameFileSystem(dest_url));
295
296   GetUsageAndQuotaThenRunTask(
297       dest_url,
298       base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
299                  weak_factory_.GetWeakPtr(), src_url, dest_url, option,
300                  progress_callback, callback),
301       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
302 }
303
304 void FileSystemOperationImpl::MoveFileLocal(
305     const FileSystemURL& src_url,
306     const FileSystemURL& dest_url,
307     CopyOrMoveOption option,
308     const StatusCallback& callback) {
309   DCHECK(SetPendingOperationType(kOperationMove));
310   DCHECK(src_url.IsInSameFileSystem(dest_url));
311   GetUsageAndQuotaThenRunTask(
312       dest_url,
313       base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
314                  weak_factory_.GetWeakPtr(),
315                  src_url, dest_url, option, callback),
316       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
317 }
318
319 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
320     const FileSystemURL& url,
321     base::FilePath* platform_path) {
322   DCHECK(SetPendingOperationType(kOperationGetLocalPath));
323   if (!file_system_context()->IsSandboxFileSystem(url.type()))
324     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
325   FileSystemFileUtil* file_util =
326       file_system_context()->sandbox_delegate()->sync_file_util();
327   file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
328   return base::PLATFORM_FILE_OK;
329 }
330
331 FileSystemOperationImpl::FileSystemOperationImpl(
332     const FileSystemURL& url,
333     FileSystemContext* file_system_context,
334     scoped_ptr<FileSystemOperationContext> operation_context)
335     : file_system_context_(file_system_context),
336       operation_context_(operation_context.Pass()),
337       async_file_util_(NULL),
338       peer_handle_(base::kNullProcessHandle),
339       pending_operation_(kOperationNone),
340       weak_factory_(this) {
341   DCHECK(operation_context_.get());
342   operation_context_->DetachUserDataThread();
343   async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
344   DCHECK(async_file_util_);
345 }
346
347 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
348     const FileSystemURL& url,
349     const base::Closure& task,
350     const base::Closure& error_callback) {
351   quota::QuotaManagerProxy* quota_manager_proxy =
352       file_system_context()->quota_manager_proxy();
353   if (!quota_manager_proxy ||
354       !file_system_context()->GetQuotaUtil(url.type())) {
355     // If we don't have the quota manager or the requested filesystem type
356     // does not support quota, we should be able to let it go.
357     operation_context_->set_allowed_bytes_growth(kint64max);
358     task.Run();
359     return;
360   }
361
362   DCHECK(quota_manager_proxy);
363   DCHECK(quota_manager_proxy->quota_manager());
364   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
365       url.origin(),
366       FileSystemTypeToQuotaStorageType(url.type()),
367       base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
368                  weak_factory_.GetWeakPtr(), task, error_callback));
369 }
370
371 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
372     const base::Closure& task,
373     const base::Closure& error_callback,
374     quota::QuotaStatusCode status,
375     int64 usage, int64 quota) {
376   if (status != quota::kQuotaStatusOk) {
377     LOG(WARNING) << "Got unexpected quota error : " << status;
378     error_callback.Run();
379     return;
380   }
381
382   operation_context_->set_allowed_bytes_growth(quota - usage);
383   task.Run();
384 }
385
386 void FileSystemOperationImpl::DoCreateFile(
387     const FileSystemURL& url,
388     const StatusCallback& callback,
389     bool exclusive) {
390   async_file_util_->EnsureFileExists(
391       operation_context_.Pass(), url,
392       base::Bind(
393           exclusive ?
394               &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
395               &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
396           weak_factory_.GetWeakPtr(), callback));
397 }
398
399 void FileSystemOperationImpl::DoCreateDirectory(
400     const FileSystemURL& url,
401     const StatusCallback& callback,
402     bool exclusive, bool recursive) {
403   async_file_util_->CreateDirectory(
404       operation_context_.Pass(),
405       url, exclusive, recursive,
406       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
407                  weak_factory_.GetWeakPtr(), callback));
408 }
409
410 void FileSystemOperationImpl::DoCopyFileLocal(
411     const FileSystemURL& src_url,
412     const FileSystemURL& dest_url,
413     CopyOrMoveOption option,
414     const CopyFileProgressCallback& progress_callback,
415     const StatusCallback& callback) {
416   async_file_util_->CopyFileLocal(
417       operation_context_.Pass(), src_url, dest_url, option, progress_callback,
418       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
419                  weak_factory_.GetWeakPtr(), callback));
420 }
421
422 void FileSystemOperationImpl::DoMoveFileLocal(
423     const FileSystemURL& src_url,
424     const FileSystemURL& dest_url,
425     CopyOrMoveOption option,
426     const StatusCallback& callback) {
427   async_file_util_->MoveFileLocal(
428       operation_context_.Pass(), src_url, dest_url, option,
429       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
430                  weak_factory_.GetWeakPtr(), callback));
431 }
432
433 void FileSystemOperationImpl::DoCopyInForeignFile(
434     const base::FilePath& src_local_disk_file_path,
435     const FileSystemURL& dest_url,
436     const StatusCallback& callback) {
437   async_file_util_->CopyInForeignFile(
438       operation_context_.Pass(),
439       src_local_disk_file_path, dest_url,
440       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
441                  weak_factory_.GetWeakPtr(), callback));
442 }
443
444 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
445                                          const StatusCallback& callback,
446                                          int64 length) {
447   async_file_util_->Truncate(
448       operation_context_.Pass(), url, length,
449       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
450                  weak_factory_.GetWeakPtr(), callback));
451 }
452
453 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
454                                          const OpenFileCallback& callback,
455                                          int file_flags) {
456   async_file_util_->CreateOrOpen(
457       operation_context_.Pass(), url, file_flags,
458       base::Bind(&FileSystemOperationImpl::DidOpenFile,
459                  weak_factory_.GetWeakPtr(), callback));
460 }
461
462 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
463     const StatusCallback& callback,
464     base::PlatformFileError rv, bool created) {
465   if (rv == base::PLATFORM_FILE_OK && !created) {
466     callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
467   } else {
468     DidFinishOperation(callback, rv);
469   }
470 }
471
472 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
473     const StatusCallback& callback,
474     base::PlatformFileError rv, bool /* created */) {
475   DidFinishOperation(callback, rv);
476 }
477
478 void FileSystemOperationImpl::DidFinishOperation(
479     const StatusCallback& callback,
480     base::PlatformFileError rv) {
481   if (!cancel_callback_.is_null()) {
482     StatusCallback cancel_callback = cancel_callback_;
483     callback.Run(rv);
484
485     // Return OK only if we succeeded to stop the operation.
486     cancel_callback.Run(rv == base::PLATFORM_FILE_ERROR_ABORT ?
487                         base::PLATFORM_FILE_OK :
488                         base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
489   } else {
490     callback.Run(rv);
491   }
492 }
493
494 void FileSystemOperationImpl::DidDirectoryExists(
495     const StatusCallback& callback,
496     base::PlatformFileError rv,
497     const base::PlatformFileInfo& file_info) {
498   if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory)
499     rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
500   callback.Run(rv);
501 }
502
503 void FileSystemOperationImpl::DidFileExists(
504     const StatusCallback& callback,
505     base::PlatformFileError rv,
506     const base::PlatformFileInfo& file_info) {
507   if (rv == base::PLATFORM_FILE_OK && file_info.is_directory)
508     rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
509   callback.Run(rv);
510 }
511
512 void FileSystemOperationImpl::DidDeleteRecursively(
513     const FileSystemURL& url,
514     const StatusCallback& callback,
515     base::PlatformFileError rv) {
516   if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) {
517     // Recursive removal is not supported on this platform.
518     DCHECK(!recursive_operation_delegate_);
519     recursive_operation_delegate_.reset(
520         new RemoveOperationDelegate(
521             file_system_context(), url,
522             base::Bind(&FileSystemOperationImpl::DidFinishOperation,
523                        weak_factory_.GetWeakPtr(), callback)));
524     recursive_operation_delegate_->RunRecursively();
525     return;
526   }
527
528   callback.Run(rv);
529 }
530
531 void FileSystemOperationImpl::DidWrite(
532     const FileSystemURL& url,
533     const WriteCallback& write_callback,
534     base::PlatformFileError rv,
535     int64 bytes,
536     FileWriterDelegate::WriteProgressStatus write_status) {
537   const bool complete = (
538       write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
539   if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
540     DCHECK(operation_context_);
541     operation_context_->change_observers()->Notify(
542         &FileChangeObserver::OnModifyFile, MakeTuple(url));
543   }
544
545   StatusCallback cancel_callback = cancel_callback_;
546   write_callback.Run(rv, bytes, complete);
547   if (!cancel_callback.is_null())
548     cancel_callback.Run(base::PLATFORM_FILE_OK);
549 }
550
551 void FileSystemOperationImpl::DidOpenFile(
552     const OpenFileCallback& callback,
553     base::PlatformFileError rv,
554     base::PassPlatformFile file,
555     const base::Closure& on_close_callback) {
556   if (rv == base::PLATFORM_FILE_OK)
557     CHECK_NE(base::kNullProcessHandle, peer_handle_);
558   callback.Run(rv, file.ReleaseValue(), on_close_callback, peer_handle_);
559 }
560
561 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
562   if (pending_operation_ != kOperationNone)
563     return false;
564   pending_operation_ = type;
565   return true;
566 }
567
568 }  // namespace fileapi