1// Copyright (c) 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/async_file_util_adapter.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/sequenced_task_runner.h"
11#include "base/task_runner_util.h"
12#include "webkit/browser/fileapi/file_system_context.h"
13#include "webkit/browser/fileapi/file_system_file_util.h"
14#include "webkit/browser/fileapi/file_system_operation_context.h"
15#include "webkit/browser/fileapi/file_system_url.h"
16#include "webkit/common/blob/shareable_file_reference.h"
17#include "webkit/common/fileapi/file_system_util.h"
18
19using base::Bind;
20using base::Callback;
21using base::Owned;
22using base::PlatformFileError;
23using base::Unretained;
24using webkit_blob::ShareableFileReference;
25
26namespace fileapi {
27
28namespace {
29
30class EnsureFileExistsHelper {
31 public:
32  EnsureFileExistsHelper() : error_(base::PLATFORM_FILE_OK), created_(false) {}
33
34  void RunWork(FileSystemFileUtil* file_util,
35               FileSystemOperationContext* context,
36               const FileSystemURL& url) {
37    error_ = file_util->EnsureFileExists(context, url, &created_);
38  }
39
40  void Reply(const AsyncFileUtil::EnsureFileExistsCallback& callback) {
41    callback.Run(error_, created_);
42  }
43
44 private:
45  base::PlatformFileError error_;
46  bool created_;
47  DISALLOW_COPY_AND_ASSIGN(EnsureFileExistsHelper);
48};
49
50class GetFileInfoHelper {
51 public:
52  GetFileInfoHelper()
53      : error_(base::PLATFORM_FILE_OK) {}
54
55  void GetFileInfo(FileSystemFileUtil* file_util,
56                   FileSystemOperationContext* context,
57                   const FileSystemURL& url) {
58    error_ = file_util->GetFileInfo(context, url, &file_info_, &platform_path_);
59  }
60
61  void CreateSnapshotFile(FileSystemFileUtil* file_util,
62                          FileSystemOperationContext* context,
63                          const FileSystemURL& url) {
64    scoped_file_ = file_util->CreateSnapshotFile(
65        context, url, &error_, &file_info_, &platform_path_);
66  }
67
68  void ReplyFileInfo(const AsyncFileUtil::GetFileInfoCallback& callback) {
69    callback.Run(error_, file_info_);
70  }
71
72  void ReplySnapshotFile(
73      const AsyncFileUtil::CreateSnapshotFileCallback& callback) {
74    callback.Run(error_, file_info_, platform_path_,
75                  ShareableFileReference::GetOrCreate(scoped_file_.Pass()));
76  }
77
78 private:
79  base::PlatformFileError error_;
80  base::PlatformFileInfo file_info_;
81  base::FilePath platform_path_;
82  webkit_blob::ScopedFile scoped_file_;
83  DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper);
84};
85
86class ReadDirectoryHelper {
87 public:
88  ReadDirectoryHelper() : error_(base::PLATFORM_FILE_OK) {}
89
90  void RunWork(FileSystemFileUtil* file_util,
91               FileSystemOperationContext* context,
92               const FileSystemURL& url) {
93    base::PlatformFileInfo file_info;
94    base::FilePath platform_path;
95    PlatformFileError error = file_util->GetFileInfo(
96        context, url, &file_info, &platform_path);
97    if (error != base::PLATFORM_FILE_OK) {
98      error_ = error;
99      return;
100    }
101    if (!file_info.is_directory) {
102      error_ = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
103      return;
104    }
105
106    scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum(
107        file_util->CreateFileEnumerator(context, url));
108
109    base::FilePath current;
110    while (!(current = file_enum->Next()).empty()) {
111      DirectoryEntry entry;
112      entry.is_directory = file_enum->IsDirectory();
113      entry.name = VirtualPath::BaseName(current).value();
114      entry.size = file_enum->Size();
115      entry.last_modified_time = file_enum->LastModifiedTime();
116      entries_.push_back(entry);
117    }
118    error_ = base::PLATFORM_FILE_OK;
119  }
120
121  void Reply(const AsyncFileUtil::ReadDirectoryCallback& callback) {
122    callback.Run(error_, entries_, false /* has_more */);
123  }
124
125 private:
126  base::PlatformFileError error_;
127  std::vector<DirectoryEntry> entries_;
128  DISALLOW_COPY_AND_ASSIGN(ReadDirectoryHelper);
129};
130
131void RunCreateOrOpenCallback(
132    const AsyncFileUtil::CreateOrOpenCallback& callback,
133    base::PlatformFileError result,
134    base::PassPlatformFile file,
135    bool created) {
136  callback.Run(result, file, base::Closure());
137}
138
139}  // namespace
140
141AsyncFileUtilAdapter::AsyncFileUtilAdapter(
142    FileSystemFileUtil* sync_file_util)
143    : sync_file_util_(sync_file_util) {
144  DCHECK(sync_file_util_.get());
145}
146
147AsyncFileUtilAdapter::~AsyncFileUtilAdapter() {
148}
149
150void AsyncFileUtilAdapter::CreateOrOpen(
151    scoped_ptr<FileSystemOperationContext> context,
152    const FileSystemURL& url,
153    int file_flags,
154    const CreateOrOpenCallback& callback) {
155  FileSystemOperationContext* context_ptr = context.release();
156  const bool success = base::FileUtilProxy::RelayCreateOrOpen(
157      context_ptr->task_runner(),
158      Bind(&FileSystemFileUtil::CreateOrOpen, Unretained(sync_file_util_.get()),
159           context_ptr, url, file_flags),
160      Bind(&FileSystemFileUtil::Close, Unretained(sync_file_util_.get()),
161           base::Owned(context_ptr)),
162      Bind(&RunCreateOrOpenCallback, callback));
163  DCHECK(success);
164}
165
166void AsyncFileUtilAdapter::EnsureFileExists(
167    scoped_ptr<FileSystemOperationContext> context,
168    const FileSystemURL& url,
169    const EnsureFileExistsCallback& callback) {
170  EnsureFileExistsHelper* helper = new EnsureFileExistsHelper;
171  FileSystemOperationContext* context_ptr = context.release();
172  const bool success = context_ptr->task_runner()->PostTaskAndReply(
173      FROM_HERE,
174      Bind(&EnsureFileExistsHelper::RunWork, Unretained(helper),
175           sync_file_util_.get(), base::Owned(context_ptr), url),
176      Bind(&EnsureFileExistsHelper::Reply, Owned(helper), callback));
177  DCHECK(success);
178}
179
180void AsyncFileUtilAdapter::CreateDirectory(
181    scoped_ptr<FileSystemOperationContext> context,
182    const FileSystemURL& url,
183    bool exclusive,
184    bool recursive,
185    const StatusCallback& callback) {
186  FileSystemOperationContext* context_ptr = context.release();
187  const bool success = base::PostTaskAndReplyWithResult(
188      context_ptr->task_runner(), FROM_HERE,
189      Bind(&FileSystemFileUtil::CreateDirectory,
190           Unretained(sync_file_util_.get()),
191           base::Owned(context_ptr), url, exclusive, recursive),
192      callback);
193  DCHECK(success);
194}
195
196void AsyncFileUtilAdapter::GetFileInfo(
197    scoped_ptr<FileSystemOperationContext> context,
198    const FileSystemURL& url,
199    const GetFileInfoCallback& callback) {
200  FileSystemOperationContext* context_ptr = context.release();
201  GetFileInfoHelper* helper = new GetFileInfoHelper;
202  const bool success = context_ptr->task_runner()->PostTaskAndReply(
203      FROM_HERE,
204      Bind(&GetFileInfoHelper::GetFileInfo, Unretained(helper),
205           sync_file_util_.get(), base::Owned(context_ptr), url),
206      Bind(&GetFileInfoHelper::ReplyFileInfo, Owned(helper), callback));
207  DCHECK(success);
208}
209
210void AsyncFileUtilAdapter::ReadDirectory(
211    scoped_ptr<FileSystemOperationContext> context,
212    const FileSystemURL& url,
213    const ReadDirectoryCallback& callback) {
214  FileSystemOperationContext* context_ptr = context.release();
215  ReadDirectoryHelper* helper = new ReadDirectoryHelper;
216  const bool success = context_ptr->task_runner()->PostTaskAndReply(
217      FROM_HERE,
218      Bind(&ReadDirectoryHelper::RunWork, Unretained(helper),
219           sync_file_util_.get(), base::Owned(context_ptr), url),
220      Bind(&ReadDirectoryHelper::Reply, Owned(helper), callback));
221  DCHECK(success);
222}
223
224void AsyncFileUtilAdapter::Touch(
225    scoped_ptr<FileSystemOperationContext> context,
226    const FileSystemURL& url,
227    const base::Time& last_access_time,
228    const base::Time& last_modified_time,
229    const StatusCallback& callback) {
230  FileSystemOperationContext* context_ptr = context.release();
231  const bool success = base::PostTaskAndReplyWithResult(
232      context_ptr->task_runner(), FROM_HERE,
233      Bind(&FileSystemFileUtil::Touch, Unretained(sync_file_util_.get()),
234           base::Owned(context_ptr), url,
235           last_access_time, last_modified_time),
236      callback);
237  DCHECK(success);
238}
239
240void AsyncFileUtilAdapter::Truncate(
241    scoped_ptr<FileSystemOperationContext> context,
242    const FileSystemURL& url,
243    int64 length,
244    const StatusCallback& callback) {
245  FileSystemOperationContext* context_ptr = context.release();
246  const bool success = base::PostTaskAndReplyWithResult(
247      context_ptr->task_runner(), FROM_HERE,
248      Bind(&FileSystemFileUtil::Truncate, Unretained(sync_file_util_.get()),
249           base::Owned(context_ptr), url, length),
250      callback);
251  DCHECK(success);
252}
253
254void AsyncFileUtilAdapter::CopyFileLocal(
255    scoped_ptr<FileSystemOperationContext> context,
256    const FileSystemURL& src_url,
257    const FileSystemURL& dest_url,
258    CopyOrMoveOption option,
259    const CopyFileProgressCallback& progress_callback,
260    const StatusCallback& callback) {
261  // TODO(hidehiko): Support progress_callback.
262  FileSystemOperationContext* context_ptr = context.release();
263  const bool success = base::PostTaskAndReplyWithResult(
264      context_ptr->task_runner(), FROM_HERE,
265      Bind(&FileSystemFileUtil::CopyOrMoveFile,
266           Unretained(sync_file_util_.get()), base::Owned(context_ptr),
267           src_url, dest_url, option, true /* copy */),
268      callback);
269  DCHECK(success);
270}
271
272void AsyncFileUtilAdapter::MoveFileLocal(
273    scoped_ptr<FileSystemOperationContext> context,
274    const FileSystemURL& src_url,
275    const FileSystemURL& dest_url,
276    CopyOrMoveOption option,
277    const StatusCallback& callback) {
278  FileSystemOperationContext* context_ptr = context.release();
279  const bool success = base::PostTaskAndReplyWithResult(
280      context_ptr->task_runner(), FROM_HERE,
281      Bind(&FileSystemFileUtil::CopyOrMoveFile,
282           Unretained(sync_file_util_.get()), base::Owned(context_ptr),
283           src_url, dest_url, option, false /* copy */),
284      callback);
285  DCHECK(success);
286}
287
288void AsyncFileUtilAdapter::CopyInForeignFile(
289      scoped_ptr<FileSystemOperationContext> context,
290      const base::FilePath& src_file_path,
291      const FileSystemURL& dest_url,
292      const StatusCallback& callback) {
293  FileSystemOperationContext* context_ptr = context.release();
294  const bool success = base::PostTaskAndReplyWithResult(
295      context_ptr->task_runner(), FROM_HERE,
296      Bind(&FileSystemFileUtil::CopyInForeignFile,
297           Unretained(sync_file_util_.get()),
298           base::Owned(context_ptr), src_file_path, dest_url),
299      callback);
300  DCHECK(success);
301}
302
303void AsyncFileUtilAdapter::DeleteFile(
304    scoped_ptr<FileSystemOperationContext> context,
305    const FileSystemURL& url,
306    const StatusCallback& callback) {
307  FileSystemOperationContext* context_ptr = context.release();
308  const bool success = base::PostTaskAndReplyWithResult(
309      context_ptr->task_runner(), FROM_HERE,
310      Bind(&FileSystemFileUtil::DeleteFile,
311           Unretained(sync_file_util_.get()),
312           base::Owned(context_ptr), url),
313      callback);
314  DCHECK(success);
315}
316
317void AsyncFileUtilAdapter::DeleteDirectory(
318    scoped_ptr<FileSystemOperationContext> context,
319    const FileSystemURL& url,
320    const StatusCallback& callback) {
321  FileSystemOperationContext* context_ptr = context.release();
322  const bool success = base::PostTaskAndReplyWithResult(
323      context_ptr->task_runner(), FROM_HERE,
324      Bind(&FileSystemFileUtil::DeleteDirectory,
325           Unretained(sync_file_util_.get()),
326           base::Owned(context_ptr), url),
327      callback);
328  DCHECK(success);
329}
330
331void AsyncFileUtilAdapter::DeleteRecursively(
332    scoped_ptr<FileSystemOperationContext> context,
333    const FileSystemURL& url,
334    const StatusCallback& callback) {
335  callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
336}
337
338void AsyncFileUtilAdapter::CreateSnapshotFile(
339    scoped_ptr<FileSystemOperationContext> context,
340    const FileSystemURL& url,
341    const CreateSnapshotFileCallback& callback) {
342  FileSystemOperationContext* context_ptr = context.release();
343  GetFileInfoHelper* helper = new GetFileInfoHelper;
344  const bool success = context_ptr->task_runner()->PostTaskAndReply(
345      FROM_HERE,
346      Bind(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper),
347           sync_file_util_.get(), base::Owned(context_ptr), url),
348      Bind(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper), callback));
349  DCHECK(success);
350}
351
352}  // namespace fileapi
353