Changes for browser-side implementation for file api.

BUG=32277

TEST=Separate CL for unit test.

Review URL: http://codereview.chromium.org/3212002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58312 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: 61da1dbbe85912d3b21310b5e934ddcc273e5cb1
diff --git a/base/file_util_proxy.cc b/base/file_util_proxy.cc
index de16b10..e7c0263 100644
--- a/base/file_util_proxy.cc
+++ b/base/file_util_proxy.cc
@@ -4,7 +4,6 @@
 
 #include "base/file_util_proxy.h"
 
-#include "base/file_util.h"
 #include "base/message_loop_proxy.h"
 
 // TODO(jianli): Move the code from anonymous namespace to base namespace so
@@ -16,7 +15,7 @@
  public:
   MessageLoopRelay()
       : origin_message_loop_proxy_(
-          base::MessageLoopProxy::CreateForCurrentThread()),
+            base::MessageLoopProxy::CreateForCurrentThread()),
         error_code_(base::PLATFORM_FILE_OK) {
   }
 
@@ -200,8 +199,17 @@
 
  protected:
   virtual void RunWork() {
-    if (!file_util::Delete(file_path_, recursive_))
+    if (!file_util::PathExists(file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
+    if (!file_util::Delete(file_path_, recursive_)) {
+      if (!recursive_ && !file_util::IsDirectoryEmpty(file_path_)) {
+        set_error_code(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
+        return;
+      }
       set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+    }
   }
 
  private:
@@ -209,6 +217,185 @@
   bool recursive_;
 };
 
+class RelayCopy : public RelayWithStatusCallback {
+ public:
+  RelayCopy(const FilePath& src_file_path,
+            const FilePath& dest_file_path,
+            base::FileUtilProxy::StatusCallback* callback)
+      : RelayWithStatusCallback(callback),
+        src_file_path_(src_file_path),
+        dest_file_path_(dest_file_path) {
+  }
+
+ protected:
+  virtual void RunWork() {
+    bool dest_path_exists = file_util::PathExists(dest_file_path_);
+    if (!dest_path_exists &&
+        !file_util::DirectoryExists(dest_file_path_.DirName())) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
+    // |src_file_path| exists and is a directory.
+    // |dest_file_path| exists and is a file.
+    if (file_util::DirectoryExists(src_file_path_) &&
+        dest_path_exists && !file_util::DirectoryExists(dest_file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY);
+      return;
+    }
+    if (file_util::ContainsPath(src_file_path_, dest_file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+      return;
+    }
+    if (!file_util::CopyDirectory(src_file_path_, dest_file_path_,
+        true /* recursive */)) {
+      if (!file_util::PathExists(src_file_path_)) {
+        set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+        return;
+      }
+      if (src_file_path_.value() == dest_file_path_.value()) {
+        set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
+        return;
+      }
+      // Something else went wrong.
+      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+    }
+  }
+
+ private:
+  FilePath src_file_path_;
+  FilePath dest_file_path_;
+};
+
+class RelayMove : public RelayWithStatusCallback {
+ public:
+  RelayMove(const FilePath& src_file_path,
+            const FilePath& dest_file_path,
+            base::FileUtilProxy::StatusCallback* callback)
+      : RelayWithStatusCallback(callback),
+        src_file_path_(src_file_path),
+        dest_file_path_(dest_file_path) {
+  }
+
+ protected:
+  virtual void RunWork() {
+    bool dest_path_exists = file_util::PathExists(dest_file_path_);
+    if (!dest_path_exists &&
+        !file_util::DirectoryExists(dest_file_path_.DirName())) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
+    // |src_file_path| exists and is a directory.
+    // |dest_file_path| exists and is a file.
+    if (file_util::DirectoryExists(src_file_path_) &&
+          dest_path_exists &&
+          !file_util::DirectoryExists(dest_file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
+      return;
+    }
+    if (file_util::ContainsPath(src_file_path_, dest_file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
+      return;
+    }
+    if (!file_util::Move(src_file_path_, dest_file_path_)) {
+      if (!file_util::PathExists(src_file_path_)) {
+        set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+        return;
+      }
+      if (src_file_path_.value() == dest_file_path_.value()) {
+        set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
+        return;
+      }
+      // Something else went wrong.
+      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+    }
+  }
+
+ private:
+  FilePath src_file_path_;
+  FilePath dest_file_path_;
+};
+
+class RelayCreateDirectory : public RelayWithStatusCallback {
+ public:
+  RelayCreateDirectory(
+      const FilePath& file_path,
+      bool exclusive,
+      base::FileUtilProxy::StatusCallback* callback)
+      : RelayWithStatusCallback(callback),
+        file_path_(file_path),
+        exclusive_(exclusive) {
+  }
+
+ protected:
+  virtual void RunWork() {
+    bool path_exists = file_util::PathExists(file_path_);
+    // If parent dir of file doesn't exist.
+    if (!file_util::PathExists(file_path_.DirName())) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
+    if (exclusive_ && path_exists) {
+      set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
+      return;
+    }
+    // If file exists at the path.
+    if (path_exists && !file_util::DirectoryExists(file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
+      return;
+    }
+    if (!file_util::CreateDirectory(file_path_))
+      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
+  }
+
+ private:
+  FilePath file_path_;
+  bool exclusive_;
+};
+
+class RelayReadDirectory : public MessageLoopRelay {
+ public:
+  RelayReadDirectory(const FilePath& file_path,
+      base::FileUtilProxy::ReadDirectoryCallback* callback)
+      : callback_(callback), file_path_(file_path) {
+    DCHECK(callback);
+  }
+
+ protected:
+  virtual void RunWork() {
+    // TODO(kkanetkar): Implement directory read in multiple chunks.
+    if (!file_util::DirectoryExists(file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
+
+    file_util::FileEnumerator file_enum(
+        file_path_, false, static_cast<file_util::FileEnumerator::FILE_TYPE>(
+        file_util::FileEnumerator::FILES |
+        file_util::FileEnumerator::DIRECTORIES));
+    FilePath current;
+    while (!(current = file_enum.Next()).empty()) {
+      base::file_util_proxy::Entry entry;
+      file_util::FileEnumerator::FindInfo info;
+      file_enum.GetFindInfo(&info);
+      entry.isDirectory = file_enum.IsDirectory(info);
+      // This will just give the entry's name instead of entire path
+      // if we use current.value().
+      entry.name = file_util::FileEnumerator::GetFilename(info).value();
+      entries_.push_back(entry);
+    }
+  }
+
+  virtual void RunCallback() {
+    callback_->Run(error_code(), entries_);
+    delete callback_;
+  }
+
+ private:
+  base::FileUtilProxy::ReadDirectoryCallback* callback_;
+  FilePath file_path_;
+  std::vector<base::file_util_proxy::Entry> entries_;
+};
+
 class RelayGetFileInfo : public MessageLoopRelay {
  public:
   RelayGetFileInfo(const FilePath& file_path,
@@ -220,6 +407,10 @@
 
  protected:
   virtual void RunWork() {
+    if (!file_util::PathExists(file_path_)) {
+      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
+      return;
+    }
     if (!file_util::GetFileInfo(file_path_, &file_info_))
       set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
   }
@@ -263,6 +454,16 @@
 }
 
 // static
+bool FileUtilProxy::CreateDirectory(
+    scoped_refptr<MessageLoopProxy> message_loop_proxy,
+    const FilePath& file_path,
+    bool exclusive,
+    StatusCallback* callback) {
+  return Start(FROM_HERE, message_loop_proxy, new RelayCreateDirectory(
+      file_path, exclusive, callback));
+}
+
+// static
 bool FileUtilProxy::Close(scoped_refptr<MessageLoopProxy> message_loop_proxy,
                           base::PlatformFile file_handle,
                           StatusCallback* callback) {
@@ -279,6 +480,43 @@
 }
 
 // static
+bool FileUtilProxy::Copy(scoped_refptr<MessageLoopProxy> message_loop_proxy,
+                         const FilePath& src_file_path,
+                         const FilePath& dest_file_path,
+                         StatusCallback* callback) {
+  return Start(FROM_HERE, message_loop_proxy,
+               new RelayCopy(src_file_path, dest_file_path, callback));
+}
+
+// static
+bool FileUtilProxy::Move(scoped_refptr<MessageLoopProxy> message_loop_proxy,
+                         const FilePath& src_file_path,
+                         const FilePath& dest_file_path,
+                         StatusCallback* callback) {
+  return Start(FROM_HERE, message_loop_proxy,
+               new RelayMove(src_file_path, dest_file_path, callback));
+}
+
+// static
+bool FileUtilProxy::ReadDirectory(
+    scoped_refptr<MessageLoopProxy> message_loop_proxy,
+    const FilePath& file_path,
+    ReadDirectoryCallback* callback) {
+  return Start(FROM_HERE, message_loop_proxy, new RelayReadDirectory(
+               file_path, callback));
+}
+
+// Retrieves the information about a file. It is invalid to pass NULL for the
+// callback.
+bool FileUtilProxy::GetFileInfo(
+    scoped_refptr<MessageLoopProxy> message_loop_proxy,
+    const FilePath& file_path,
+    GetFileInfoCallback* callback) {
+  return Start(FROM_HERE, message_loop_proxy, new RelayGetFileInfo(
+               file_path, callback));
+}
+
+// static
 bool FileUtilProxy::RecursiveDelete(
     scoped_refptr<MessageLoopProxy> message_loop_proxy,
     const FilePath& file_path,
@@ -287,13 +525,4 @@
                new RelayDelete(file_path, true, callback));
 }
 
-// static
-bool FileUtilProxy::GetFileInfo(
-    scoped_refptr<MessageLoopProxy> message_loop_proxy,
-    const FilePath& file_path,
-    GetFileInfoCallback* callback) {
-  return Start(FROM_HERE, message_loop_proxy,
-               new RelayGetFileInfo(file_path, callback));
-}
-
-} // namespace base
+}  // namespace base
diff --git a/base/file_util_proxy.h b/base/file_util_proxy.h
index 9899c77..dec06e3 100644
--- a/base/file_util_proxy.h
+++ b/base/file_util_proxy.h
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef BASE_FILE_SYSTEM_PROXY_H_
-#define BASE_FILE_SYSTEM_PROXY_H_
+#ifndef BASE_FILE_UTIL_PROXY_H_
+#define BASE_FILE_UTIL_PROXY_H_
+
+#include <vector>
 
 #include "base/callback.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
 #include "base/platform_file.h"
 #include "base/ref_counted.h"
 #include "base/tracked_objects.h"
@@ -16,6 +20,14 @@
 
 namespace base {
 
+namespace file_util_proxy {
+  // Holds metadata for file or directory entry.
+struct Entry {
+  FilePath::StringType name;
+  bool isDirectory;
+};
+}  // namespace file_util_proxy
+
 class MessageLoopProxy;
 
 // This class provides asynchronous access to common file routines.
@@ -51,17 +63,6 @@
                     base::PlatformFile,
                     StatusCallback* callback);
 
-  // Deletes a file or empty directory.
-  static bool Delete(scoped_refptr<MessageLoopProxy> message_loop_proxy,
-                     const FilePath& file_path,
-                     StatusCallback* callback);
-
-  // Deletes a directory and all of its contents.
-  static bool RecursiveDelete(
-      scoped_refptr<MessageLoopProxy> message_loop_proxy,
-      const FilePath& file_path,
-      StatusCallback* callback);
-
   // Retrieves the information about a file. It is invalid to pass NULL for the
   // callback.
   typedef Callback2<base::PlatformFileError /* error code */,
@@ -72,10 +73,56 @@
       const FilePath& file_path,
       GetFileInfoCallback* callback);
 
+  typedef Callback2<base::PlatformFileError /* error code */,
+      const std::vector<base::file_util_proxy::Entry>&
+       >::Type ReadDirectoryCallback;
+  static bool ReadDirectory(scoped_refptr<MessageLoopProxy> message_loop_proxy,
+                            const FilePath& file_path,
+                            ReadDirectoryCallback* callback);
+
+  // Copies a file or a directory from |src_file_path| to |dest_file_path|
+  // Error cases:
+  // If destination file doesn't exist or destination's parent
+  // doesn't exists.
+  // If source dir exists but destination path is an existing file.
+  // If source is a parent of destination.
+  // If source doesn't exists.
+  static bool Copy(scoped_refptr<MessageLoopProxy> message_loop_proxy,
+                   const FilePath& src_file_path,
+                   const FilePath& dest_file_path,
+                   StatusCallback* callback);
+
+  // Creates directory at given path. It's an error to create
+  // if |exclusive| is true and dir already exists.
+  static bool CreateDirectory(
+      scoped_refptr<MessageLoopProxy> message_loop_proxy,
+      const FilePath& file_path,
+      bool exclusive,
+      StatusCallback* callback);
+
+  // Deletes a file or empty directory.
+  static bool Delete(scoped_refptr<MessageLoopProxy> message_loop_proxy,
+                     const FilePath& file_path,
+                     StatusCallback* callback);
+
+  // Moves a file or a directory from src_file_path to dest_file_path.
+  // Error cases are similar to Copy method's error cases.
+  static bool Move(
+      scoped_refptr<MessageLoopProxy> message_loop_proxy,
+      const FilePath& src_file_path,
+      const FilePath& dest_file_path,
+      StatusCallback* callback);
+
+  // Deletes a directory and all of its contents.
+  static bool RecursiveDelete(
+      scoped_refptr<MessageLoopProxy> message_loop_proxy,
+      const FilePath& file_path,
+      StatusCallback* callback);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtilProxy);
 };
 
-} // namespace base
+}  // namespace base
 
-#endif  // BASE_FILE_SYSTEM_PROXY_H_
+#endif  // BASE_FILE_UTIL_PROXY_H_
diff --git a/base/platform_file.h b/base/platform_file.h
index 70ed0f1..0e4d05c 100644
--- a/base/platform_file.h
+++ b/base/platform_file.h
@@ -50,8 +50,9 @@
   PLATFORM_FILE_ERROR_ACCESS_DENIED = -5,
   PLATFORM_FILE_ERROR_TOO_MANY_OPENED = -6,
   PLATFORM_FILE_ERROR_NO_MEMORY = -7,
-  PLATFORM_FILE_ERROR_NO_SPACE = -7,
-  PLATFORM_FILE_ERROR_NOT_A_DIRECTORY = -9
+  PLATFORM_FILE_ERROR_NO_SPACE = -8,
+  PLATFORM_FILE_ERROR_NOT_A_DIRECTORY = -9,
+  PLATFORM_FILE_ERROR_INVALID_OPERATION = -10
 };
 
 // Creates or opens the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and