Add validation framework to Copy or Move operations in fileapi.

BUG=176566


Review URL: https://chromiumcodereview.appspot.com/12886042

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192246 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/webkit/chromeos/fileapi/cros_mount_point_provider.cc b/webkit/chromeos/fileapi/cros_mount_point_provider.cc
index 731b10ce..550bb61 100644
--- a/webkit/chromeos/fileapi/cros_mount_point_provider.cc
+++ b/webkit/chromeos/fileapi/cros_mount_point_provider.cc
@@ -20,6 +20,7 @@
 #include "webkit/chromeos/fileapi/remote_file_stream_writer.h"
 #include "webkit/chromeos/fileapi/remote_file_system_operation.h"
 #include "webkit/fileapi/async_file_util_adapter.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/external_mount_points.h"
 #include "webkit/fileapi/file_system_file_stream_reader.h"
 #include "webkit/fileapi/file_system_operation_context.h"
@@ -214,6 +215,20 @@
   return local_file_util_.get();
 }
 
+fileapi::CopyOrMoveFileValidatorFactory*
+CrosMountPointProvider::GetCopyOrMoveFileValidatorFactory(
+    fileapi::FileSystemType type, base::PlatformFileError* error_code) {
+  DCHECK(error_code);
+  *error_code = base::PLATFORM_FILE_OK;
+  return NULL;
+}
+
+void CrosMountPointProvider::InitializeCopyOrMoveFileValidatorFactory(
+    fileapi::FileSystemType type,
+    scoped_ptr<fileapi::CopyOrMoveFileValidatorFactory> factory) {
+  DCHECK(!factory);
+}
+
 fileapi::FilePermissionPolicy CrosMountPointProvider::GetPermissionPolicy(
     const fileapi::FileSystemURL& url, int permissions) const {
   if (url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal &&
diff --git a/webkit/chromeos/fileapi/cros_mount_point_provider.h b/webkit/chromeos/fileapi/cros_mount_point_provider.h
index 21cb7cc..bf27785 100644
--- a/webkit/chromeos/fileapi/cros_mount_point_provider.h
+++ b/webkit/chromeos/fileapi/cros_mount_point_provider.h
@@ -19,6 +19,7 @@
 
 namespace fileapi {
 class AsyncFileUtilAdapter;
+class CopyOrMoveFileValidatorFactory;
 class ExternalMountPoints;
 class FileSystemFileUtil;
 class FileSystemURL;
@@ -63,6 +64,13 @@
       fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::AsyncFileUtil* GetAsyncFileUtil(
       fileapi::FileSystemType type) OVERRIDE;
+  virtual fileapi::CopyOrMoveFileValidatorFactory*
+      GetCopyOrMoveFileValidatorFactory(
+          fileapi::FileSystemType type,
+          base::PlatformFileError* error_code) OVERRIDE;
+  virtual void InitializeCopyOrMoveFileValidatorFactory(
+      fileapi::FileSystemType type,
+      scoped_ptr<fileapi::CopyOrMoveFileValidatorFactory> factory) OVERRIDE;
   virtual fileapi::FilePermissionPolicy GetPermissionPolicy(
       const fileapi::FileSystemURL& url,
       int permissions) const OVERRIDE;
diff --git a/webkit/fileapi/copy_or_move_file_validator.h b/webkit/fileapi/copy_or_move_file_validator.h
new file mode 100644
index 0000000..507a2766
--- /dev/null
+++ b/webkit/fileapi/copy_or_move_file_validator.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_FILEAPI_COPY_OR_MOVE_FILE_VALIDATOR_H_
+#define WEBKIT_FILEAPI_COPY_OR_MOVE_FILE_VALIDATOR_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/platform_file.h"
+
+namespace fileapi {
+
+class CopyOrMoveFileValidator {
+ public:
+  // Callback that is invoked when validation completes. A result of
+  // base::PLATFORM_FILE_OK means the file validated.
+  typedef base::Callback<void(base::PlatformFileError result)> ResultCallback;
+
+  virtual ~CopyOrMoveFileValidator() {}
+
+  virtual void StartValidation(const ResultCallback& result_callback) = 0;
+};
+
+class CopyOrMoveFileValidatorFactory {
+ public:
+  virtual ~CopyOrMoveFileValidatorFactory() {}
+
+  // This method must always return a non-NULL validator.
+  virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+      const base::FilePath& platform_path) = 0;
+};
+
+}  // namespace fileapi
+
+#endif  // WEBKIT_FILEAPI_COPY_OR_MOVE_FILE_VALIDATOR_H_
diff --git a/webkit/fileapi/copy_or_move_file_validator_unittest.cc b/webkit/fileapi/copy_or_move_file_validator_unittest.cc
new file mode 100644
index 0000000..7abc628
--- /dev/null
+++ b/webkit/fileapi/copy_or_move_file_validator_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/async_file_test_helper.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
+#include "webkit/fileapi/external_mount_points.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/file_system_mount_point_provider.h"
+#include "webkit/fileapi/file_system_task_runners.h"
+#include "webkit/fileapi/file_system_url.h"
+#include "webkit/fileapi/file_system_util.h"
+#include "webkit/fileapi/isolated_context.h"
+#include "webkit/fileapi/mock_file_system_options.h"
+#include "webkit/quota/mock_special_storage_policy.h"
+
+namespace fileapi {
+
+class CopyOrMoveFileValidatorTestHelper {
+ public:
+  CopyOrMoveFileValidatorTestHelper(
+      const GURL& origin,
+      FileSystemType src_type,
+      FileSystemType dest_type)
+      : origin_(origin),
+        src_type_(src_type),
+        dest_type_(dest_type) {}
+
+  ~CopyOrMoveFileValidatorTestHelper() {
+    file_system_context_ = NULL;
+    MessageLoop::current()->RunUntilIdle();
+  }
+
+  void SetUp() {
+    ASSERT_TRUE(base_.CreateUniqueTempDir());
+    base::FilePath base_dir = base_.path();
+    file_system_context_ = new FileSystemContext(
+        FileSystemTaskRunners::CreateMockTaskRunners(),
+        ExternalMountPoints::CreateRefCounted().get(),
+        make_scoped_refptr(new quota::MockSpecialStoragePolicy),
+        NULL,
+        base_dir,
+        CreateAllowFileAccessOptions());
+
+    // Prepare the origin's root directory.
+    if (src_type_ == kFileSystemTypeNativeMedia) {
+      base::FilePath src_path = base_dir.Append(FILE_PATH_LITERAL("src_media"));
+      file_util::CreateDirectory(src_path);
+      src_fsid_ = IsolatedContext::GetInstance()->RegisterFileSystemForPath(
+          kFileSystemTypeNativeMedia, src_path, NULL);
+    } else {
+      FileSystemMountPointProvider* mount_point_provider =
+          file_system_context_->GetMountPointProvider(src_type_);
+      mount_point_provider->GetFileSystemRootPathOnFileThread(
+          SourceURL(""),
+          true /* create */);
+    }
+    DCHECK_EQ(kFileSystemTypeNativeMedia, dest_type_);
+    base::FilePath dest_path = base_dir.Append(FILE_PATH_LITERAL("dest_media"));
+    file_util::CreateDirectory(dest_path);
+    dest_fsid_ = IsolatedContext::GetInstance()->RegisterFileSystemForPath(
+        kFileSystemTypeNativeMedia, dest_path, NULL);
+
+    copy_src_ = SourceURL("copy_src.jpg");
+    move_src_ = SourceURL("move_src.jpg");
+    copy_dest_ = DestURL("copy_dest.jpg");
+    move_dest_ = DestURL("move_dest.jpg");
+
+    ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(copy_src_, 10));
+    ASSERT_EQ(base::PLATFORM_FILE_OK, CreateFile(move_src_, 10));
+
+    ASSERT_TRUE(FileExists(copy_src_, 10));
+    ASSERT_TRUE(FileExists(move_src_, 10));
+    ASSERT_FALSE(FileExists(copy_dest_, 10));
+    ASSERT_FALSE(FileExists(move_dest_, 10));
+  }
+
+  void SetMediaCopyOrMoveFileValidatorFactory(
+      scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+    FileSystemMountPointProvider* mount_point_provider =
+        file_system_context_->GetMountPointProvider(kFileSystemTypeNativeMedia);
+    mount_point_provider->InitializeCopyOrMoveFileValidatorFactory(
+        kFileSystemTypeNativeMedia, factory.Pass());
+  }
+
+  void CopyTest(base::PlatformFileError expected) {
+    ASSERT_TRUE(FileExists(copy_src_, 10));
+    ASSERT_FALSE(FileExists(copy_dest_, 10));
+
+    EXPECT_EQ(expected,
+              AsyncFileTestHelper::Copy(file_system_context_, copy_src_,
+                                        copy_dest_));
+
+    EXPECT_TRUE(FileExists(copy_src_, 10));
+    if (expected == base::PLATFORM_FILE_OK)
+      EXPECT_TRUE(FileExists(copy_dest_, 10));
+    else
+      EXPECT_FALSE(FileExists(copy_dest_, 10));
+  };
+
+  void MoveTest(base::PlatformFileError expected) {
+    ASSERT_TRUE(FileExists(move_src_, 10));
+    ASSERT_FALSE(FileExists(move_dest_, 10));
+
+    EXPECT_EQ(expected,
+              AsyncFileTestHelper::Move(file_system_context_, move_src_,
+                                        move_dest_));
+
+    if (expected == base::PLATFORM_FILE_OK) {
+      EXPECT_FALSE(FileExists(move_src_, 10));
+      EXPECT_TRUE(FileExists(move_dest_, 10));
+    } else {
+      EXPECT_TRUE(FileExists(move_src_, 10));
+      EXPECT_FALSE(FileExists(move_dest_, 10));
+    }
+  };
+
+ private:
+  FileSystemURL SourceURL(const std::string& path) {
+    if (src_type_ == kFileSystemTypeNativeMedia) {
+      std::string root_fs_url = GetIsolatedFileSystemRootURIString(
+          origin_, src_fsid_, "src_media/");
+      return file_system_context_->CrackURL(GURL(root_fs_url + path));
+    }
+    return file_system_context_->CreateCrackedFileSystemURL(
+        origin_, src_type_, base::FilePath::FromUTF8Unsafe(path));
+  }
+
+  FileSystemURL DestURL(const std::string& path) {
+    std::string root_fs_url = GetIsolatedFileSystemRootURIString(
+        origin_, dest_fsid_, "dest_media/");
+    return file_system_context_->CrackURL(GURL(root_fs_url + path));
+  }
+
+  base::PlatformFileError CreateFile(const FileSystemURL& url, size_t size) {
+    base::PlatformFileError result =
+        AsyncFileTestHelper::CreateFile(file_system_context_, url);
+    if (result != base::PLATFORM_FILE_OK)
+      return result;
+    return AsyncFileTestHelper::TruncateFile(file_system_context_, url, size);
+  }
+
+  bool FileExists(const FileSystemURL& url, int64 expected_size) {
+    return AsyncFileTestHelper::FileExists(
+        file_system_context_, url, expected_size);
+  }
+
+  base::ScopedTempDir base_;
+
+  const GURL origin_;
+
+  const FileSystemType src_type_;
+  const FileSystemType dest_type_;
+  std::string src_fsid_;
+  std::string dest_fsid_;
+
+  MessageLoop message_loop_;
+  scoped_refptr<FileSystemContext> file_system_context_;
+
+  FileSystemURL copy_src_;
+  FileSystemURL copy_dest_;
+  FileSystemURL move_src_;
+  FileSystemURL move_dest_;
+
+  DISALLOW_COPY_AND_ASSIGN(CopyOrMoveFileValidatorTestHelper);
+};
+
+class TestCopyOrMoveFileValidatorFactory
+    : public CopyOrMoveFileValidatorFactory {
+ public:
+  // A factory that creates validators that accept everything or nothing.
+  TestCopyOrMoveFileValidatorFactory(bool all_valid) : all_valid_(all_valid) {}
+  virtual ~TestCopyOrMoveFileValidatorFactory() {}
+
+  virtual CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
+      const base::FilePath& /*platform_path*/) {
+    return new TestCopyOrMoveFileValidator(all_valid_);
+  }
+
+ private:
+  class TestCopyOrMoveFileValidator : public CopyOrMoveFileValidator {
+   public:
+    TestCopyOrMoveFileValidator(bool all_valid)
+        : result_(all_valid ? base::PLATFORM_FILE_OK
+                            : base::PLATFORM_FILE_ERROR_SECURITY) {
+    }
+    virtual ~TestCopyOrMoveFileValidator() {}
+
+    virtual void StartValidation(const ResultCallback& result_callback) {
+      // Post the result since a real validator must do work asynchronously.
+      MessageLoop::current()->PostTask(FROM_HERE, base::Bind(result_callback,
+                                                             result_));
+    }
+
+   private:
+    base::PlatformFileError result_;
+
+    DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidator);
+  };
+
+  bool all_valid_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestCopyOrMoveFileValidatorFactory);
+};
+
+TEST(CopyOrMoveFileValidatorTest, NoValidatorWithin6ameFSType) {
+  // Within a file system type, validation is not expected, so it should
+  // work for kFileSystemTypeNativeMedia without a validator set.
+  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+                                           kFileSystemTypeNativeMedia,
+                                           kFileSystemTypeNativeMedia);
+  helper.SetUp();
+  helper.CopyTest(base::PLATFORM_FILE_OK);
+  helper.MoveTest(base::PLATFORM_FILE_OK);
+}
+
+TEST(CopyOrMoveFileValidatorTest, MissingValidator) {
+  // Copying or moving into a kFileSystemTypeNativeMedia requires a file
+  // validator.  An error is expect if copy is attempted without a validator.
+  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+                                           kFileSystemTypeTemporary,
+                                           kFileSystemTypeNativeMedia);
+  helper.SetUp();
+  helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+  helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+TEST(CopyOrMoveFileValidatorTest, AcceptAll) {
+  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+                                           kFileSystemTypeTemporary,
+                                           kFileSystemTypeNativeMedia);
+  helper.SetUp();
+  scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+      new TestCopyOrMoveFileValidatorFactory(true /*accept_all*/));
+  helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
+
+  helper.CopyTest(base::PLATFORM_FILE_OK);
+  helper.MoveTest(base::PLATFORM_FILE_OK);
+}
+
+TEST(CopyOrMoveFileValidatorTest, AcceptNone) {
+  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+                                           kFileSystemTypeTemporary,
+                                           kFileSystemTypeNativeMedia);
+  helper.SetUp();
+  scoped_ptr<CopyOrMoveFileValidatorFactory> factory(
+      new TestCopyOrMoveFileValidatorFactory(false /*accept_all*/));
+  helper.SetMediaCopyOrMoveFileValidatorFactory(factory.Pass());
+
+  helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+  helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+TEST(CopyOrMoveFileValidatorTest, OverrideValidator) {
+  // Once set, you can not override the validator.
+  CopyOrMoveFileValidatorTestHelper helper(GURL("http://foo"),
+                                           kFileSystemTypeTemporary,
+                                           kFileSystemTypeNativeMedia);
+  helper.SetUp();
+  scoped_ptr<CopyOrMoveFileValidatorFactory> reject_factory(
+      new TestCopyOrMoveFileValidatorFactory(false /*accept_all*/));
+  helper.SetMediaCopyOrMoveFileValidatorFactory(reject_factory.Pass());
+
+  scoped_ptr<CopyOrMoveFileValidatorFactory> accept_factory(
+      new TestCopyOrMoveFileValidatorFactory(true /*accept_all*/));
+  helper.SetMediaCopyOrMoveFileValidatorFactory(accept_factory.Pass());
+
+  helper.CopyTest(base::PLATFORM_FILE_ERROR_SECURITY);
+  helper.MoveTest(base::PLATFORM_FILE_ERROR_SECURITY);
+}
+
+}  // namespace fileapi
diff --git a/webkit/fileapi/cross_operation_delegate.cc b/webkit/fileapi/cross_operation_delegate.cc
index b7e391a..3ec57ec 100644
--- a/webkit/fileapi/cross_operation_delegate.cc
+++ b/webkit/fileapi/cross_operation_delegate.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "webkit/blob/shareable_file_reference.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/file_system_context.h"
 #include "webkit/fileapi/file_system_operation_context.h"
 #include "webkit/fileapi/file_system_util.h"
@@ -154,6 +155,35 @@
   // TODO(kinuko): Otherwise create a FileStreamReader to perform a copy/move.
   DCHECK(!platform_path.empty());
 
+  CopyOrMoveFileValidatorFactory* factory =
+      file_system_context()->GetCopyOrMoveFileValidatorFactory(
+          dest_root_.type(), &error);
+  if (error != base::PLATFORM_FILE_OK) {
+    callback.Run(error);
+    return;
+  }
+  if (!factory) {
+    DidValidateFile(dest, callback, file_info, platform_path, error);
+    return;
+  }
+
+  validator_.reset(factory->CreateCopyOrMoveFileValidator(platform_path));
+  validator_->StartValidation(
+      base::Bind(&CrossOperationDelegate::DidValidateFile, AsWeakPtr(),
+                 dest, callback, file_info, platform_path));
+}
+
+void CrossOperationDelegate::DidValidateFile(
+    const FileSystemURL& dest,
+    const StatusCallback& callback,
+    const base::PlatformFileInfo& file_info,
+    const base::FilePath& platform_path,
+    base::PlatformFileError error) {
+  if (error != base::PLATFORM_FILE_OK) {
+    callback.Run(error);
+    return;
+  }
+
   NewDestOperation()->CopyInForeignFile(platform_path, dest, callback);
 }
 
diff --git a/webkit/fileapi/cross_operation_delegate.h b/webkit/fileapi/cross_operation_delegate.h
index 0aeabd3..77c15f5 100644
--- a/webkit/fileapi/cross_operation_delegate.h
+++ b/webkit/fileapi/cross_operation_delegate.h
@@ -17,6 +17,8 @@
 
 namespace fileapi {
 
+class CopyOrMoveFileValidator;
+
 // A delegate class for recursive copy or move operations.
 class CrossOperationDelegate
     : public RecursiveOperationDelegate,
@@ -61,6 +63,12 @@
       const base::PlatformFileInfo& file_info,
       const base::FilePath& platform_path,
       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref);
+  void DidValidateFile(
+      const FileSystemURL& dest,
+      const StatusCallback& callback,
+      const base::PlatformFileInfo& file_info,
+      const base::FilePath& platform_path,
+      base::PlatformFileError error);
   void DidFinishCopy(
       const FileSystemURL& src,
       const StatusCallback& callback,
@@ -97,6 +105,8 @@
 
   scoped_refptr<webkit_blob::ShareableFileReference> current_file_ref_;
 
+  scoped_ptr<CopyOrMoveFileValidator> validator_;
+
   DISALLOW_COPY_AND_ASSIGN(CrossOperationDelegate);
 };
 
diff --git a/webkit/fileapi/file_system_context.cc b/webkit/fileapi/file_system_context.cc
index 33825f6..f487f54 100644
--- a/webkit/fileapi/file_system_context.cc
+++ b/webkit/fileapi/file_system_context.cc
@@ -8,6 +8,7 @@
 #include "base/stl_util.h"
 #include "base/single_thread_task_runner.h"
 #include "googleurl/src/gurl.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/external_mount_points.h"
 #include "webkit/fileapi/file_system_file_util.h"
 #include "webkit/fileapi/file_system_operation.h"
@@ -133,6 +134,19 @@
   return mount_point_provider->GetAsyncFileUtil(type);
 }
 
+CopyOrMoveFileValidatorFactory*
+FileSystemContext::GetCopyOrMoveFileValidatorFactory(
+    FileSystemType type, base::PlatformFileError* error_code) const {
+  DCHECK(error_code);
+  *error_code = base::PLATFORM_FILE_OK;
+  FileSystemMountPointProvider* mount_point_provider =
+      GetMountPointProvider(type);
+  if (!mount_point_provider)
+    return NULL;
+  return mount_point_provider->GetCopyOrMoveFileValidatorFactory(
+      type, error_code);
+}
+
 FileSystemMountPointProvider* FileSystemContext::GetMountPointProvider(
     FileSystemType type) const {
   switch (type) {
diff --git a/webkit/fileapi/file_system_context.h b/webkit/fileapi/file_system_context.h
index 1a938e2..f0daf11 100644
--- a/webkit/fileapi/file_system_context.h
+++ b/webkit/fileapi/file_system_context.h
@@ -41,6 +41,7 @@
 namespace fileapi {
 
 class AsyncFileUtil;
+class CopyOrMoveFileValidatorFactory;
 class ExternalFileSystemMountPointProvider;
 class ExternalMountPoints;
 class FileSystemFileUtil;
@@ -97,6 +98,12 @@
   // Returns the appropriate AsyncFileUtil instance for the given |type|.
   AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) const;
 
+  // Returns the appropriate CopyOrMoveFileValidatorFactory for the given
+  // |type|.  If |error_code| is PLATFORM_FILE_OK and the result is NULL,
+  // then no validator is required.
+  CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
+      FileSystemType type, base::PlatformFileError* error_code) const;
+
   // Returns the mount point provider instance for the given |type|.
   // This may return NULL if it is given an invalid or unsupported filesystem
   // type.
diff --git a/webkit/fileapi/file_system_mount_point_provider.h b/webkit/fileapi/file_system_mount_point_provider.h
index 976d4605..066c2ab 100644
--- a/webkit/fileapi/file_system_mount_point_provider.h
+++ b/webkit/fileapi/file_system_mount_point_provider.h
@@ -10,6 +10,7 @@
 
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/platform_file.h"
 #include "webkit/fileapi/file_permission_policy.h"
 #include "webkit/fileapi/file_system_types.h"
@@ -22,6 +23,7 @@
 namespace fileapi {
 
 class AsyncFileUtil;
+class CopyOrMoveFileValidatorFactory;
 class FileSystemURL;
 class FileStreamWriter;
 class FileSystemContext;
@@ -68,6 +70,18 @@
   // Returns the specialized AsyncFileUtil for this mount point.
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) = 0;
 
+  // Returns the specialized CopyOrMoveFileValidatorFactory for this mount
+  // point and |type|.  If |error_code| is PLATFORM_FILE_OK and the result
+  // is NULL, then no validator is required.
+  virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
+      FileSystemType type, base::PlatformFileError* error_code) = 0;
+
+  // Initialize the CopyOrMoveFileValidatorFactory. Invalid to call more than
+  // once.
+  virtual void InitializeCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      scoped_ptr<CopyOrMoveFileValidatorFactory> factory) = 0;
+
   // Returns file permission policy we should apply for the given |url|.
   virtual FilePermissionPolicy GetPermissionPolicy(
       const FileSystemURL& url,
diff --git a/webkit/fileapi/isolated_mount_point_provider.cc b/webkit/fileapi/isolated_mount_point_provider.cc
index c3e3d3c..78ed482 100644
--- a/webkit/fileapi/isolated_mount_point_provider.cc
+++ b/webkit/fileapi/isolated_mount_point_provider.cc
@@ -14,6 +14,7 @@
 #include "base/sequenced_task_runner.h"
 #include "webkit/blob/local_file_stream_reader.h"
 #include "webkit/fileapi/async_file_util_adapter.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/file_system_callback_dispatcher.h"
 #include "webkit/fileapi/file_system_context.h"
 #include "webkit/fileapi/file_system_file_stream_reader.h"
@@ -122,6 +123,46 @@
   return NULL;
 }
 
+CopyOrMoveFileValidatorFactory*
+IsolatedMountPointProvider::GetCopyOrMoveFileValidatorFactory(
+    FileSystemType type, base::PlatformFileError* error_code) {
+  DCHECK(error_code);
+  *error_code = base::PLATFORM_FILE_OK;
+  switch (type) {
+    case kFileSystemTypeNativeLocal:
+    case kFileSystemTypeDragged:
+      return NULL;
+    case kFileSystemTypeNativeMedia:
+    case kFileSystemTypeDeviceMedia:
+      if (!media_copy_or_move_file_validator_factory_) {
+        *error_code = base::PLATFORM_FILE_ERROR_SECURITY;
+        return NULL;
+      }
+      return media_copy_or_move_file_validator_factory_.get();
+    default:
+      NOTREACHED();
+  }
+  return NULL;
+}
+
+void IsolatedMountPointProvider::InitializeCopyOrMoveFileValidatorFactory(
+    FileSystemType type,
+    scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+  switch (type) {
+    case kFileSystemTypeNativeLocal:
+    case kFileSystemTypeDragged:
+      DCHECK(factory == NULL);
+      break;
+    case kFileSystemTypeNativeMedia:
+    case kFileSystemTypeDeviceMedia:
+      if (!media_copy_or_move_file_validator_factory_)
+        media_copy_or_move_file_validator_factory_.reset(factory.release());
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
 FilePermissionPolicy IsolatedMountPointProvider::GetPermissionPolicy(
     const FileSystemURL& url, int permissions) const {
   if (url.type() == kFileSystemTypeDragged && url.path().empty()) {
diff --git a/webkit/fileapi/isolated_mount_point_provider.h b/webkit/fileapi/isolated_mount_point_provider.h
index f0713aa..09e5d52 100644
--- a/webkit/fileapi/isolated_mount_point_provider.h
+++ b/webkit/fileapi/isolated_mount_point_provider.h
@@ -35,6 +35,12 @@
       bool create) OVERRIDE;
   virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
+  virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      base::PlatformFileError* error_code) OVERRIDE;
+  virtual void InitializeCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      scoped_ptr<CopyOrMoveFileValidatorFactory> factory) OVERRIDE;
   virtual FilePermissionPolicy GetPermissionPolicy(
       const FileSystemURL& url,
       int permissions) const OVERRIDE;
@@ -63,6 +69,8 @@
   const base::FilePath profile_path_;
 
   scoped_ptr<MediaPathFilter> media_path_filter_;
+  scoped_ptr<CopyOrMoveFileValidatorFactory>
+      media_copy_or_move_file_validator_factory_;
 
   scoped_ptr<AsyncFileUtilAdapter> isolated_file_util_;
   scoped_ptr<AsyncFileUtilAdapter> dragged_file_util_;
diff --git a/webkit/fileapi/sandbox_mount_point_provider.cc b/webkit/fileapi/sandbox_mount_point_provider.cc
index 3cb3bd55..da1ea76 100644
--- a/webkit/fileapi/sandbox_mount_point_provider.cc
+++ b/webkit/fileapi/sandbox_mount_point_provider.cc
@@ -13,6 +13,7 @@
 #include "googleurl/src/gurl.h"
 #include "net/base/net_util.h"
 #include "webkit/fileapi/async_file_util_adapter.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/file_system_context.h"
 #include "webkit/fileapi/file_system_file_stream_reader.h"
 #include "webkit/fileapi/file_system_operation_context.h"
@@ -262,6 +263,21 @@
   return sandbox_file_util_.get();
 }
 
+CopyOrMoveFileValidatorFactory*
+SandboxMountPointProvider::GetCopyOrMoveFileValidatorFactory(
+    FileSystemType type,
+    base::PlatformFileError* error_code) {
+  DCHECK(error_code);
+  *error_code = base::PLATFORM_FILE_OK;
+  return NULL;
+}
+
+void SandboxMountPointProvider::InitializeCopyOrMoveFileValidatorFactory(
+    FileSystemType type,
+    scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+  DCHECK(!factory);
+}
+
 FilePermissionPolicy SandboxMountPointProvider::GetPermissionPolicy(
     const FileSystemURL& url, int permissions) const {
   if (!CanHandleType(url.type()) || !IsAllowedScheme(url.origin()))
diff --git a/webkit/fileapi/sandbox_mount_point_provider.h b/webkit/fileapi/sandbox_mount_point_provider.h
index 1752b8a..e8fe024 100644
--- a/webkit/fileapi/sandbox_mount_point_provider.h
+++ b/webkit/fileapi/sandbox_mount_point_provider.h
@@ -89,6 +89,12 @@
       bool create) OVERRIDE;
   virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
+  virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      base::PlatformFileError* error_code) OVERRIDE;
+  virtual void InitializeCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      scoped_ptr<CopyOrMoveFileValidatorFactory> factory) OVERRIDE;
   virtual FilePermissionPolicy GetPermissionPolicy(
       const FileSystemURL& url,
       int permissions) const OVERRIDE;
diff --git a/webkit/fileapi/test_mount_point_provider.cc b/webkit/fileapi/test_mount_point_provider.cc
index 58cc910..5599c983 100644
--- a/webkit/fileapi/test_mount_point_provider.cc
+++ b/webkit/fileapi/test_mount_point_provider.cc
@@ -10,6 +10,7 @@
 
 #include "base/file_util.h"
 #include "base/sequenced_task_runner.h"
+#include "webkit/fileapi/copy_or_move_file_validator.h"
 #include "webkit/fileapi/file_observers.h"
 #include "webkit/fileapi/file_system_file_stream_reader.h"
 #include "webkit/fileapi/file_system_operation_context.h"
@@ -111,6 +112,19 @@
   return local_file_util_.get();
 }
 
+CopyOrMoveFileValidatorFactory*
+TestMountPointProvider::GetCopyOrMoveFileValidatorFactory(
+    FileSystemType type, base::PlatformFileError* error_code) {
+  DCHECK(error_code);
+  *error_code = base::PLATFORM_FILE_OK;
+  return NULL;
+}
+
+void TestMountPointProvider::InitializeCopyOrMoveFileValidatorFactory(
+    FileSystemType type, scoped_ptr<CopyOrMoveFileValidatorFactory> factory) {
+  DCHECK(!factory);
+}
+
 FilePermissionPolicy TestMountPointProvider::GetPermissionPolicy(
     const FileSystemURL& url, int permissions) const {
   return FILE_PERMISSION_ALWAYS_DENY;
diff --git a/webkit/fileapi/test_mount_point_provider.h b/webkit/fileapi/test_mount_point_provider.h
index c6d510be..8ad2308 100644
--- a/webkit/fileapi/test_mount_point_provider.h
+++ b/webkit/fileapi/test_mount_point_provider.h
@@ -44,6 +44,12 @@
       bool create) OVERRIDE;
   virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
+  virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      base::PlatformFileError* error_code) OVERRIDE;
+  virtual void InitializeCopyOrMoveFileValidatorFactory(
+      FileSystemType type,
+      scoped_ptr<CopyOrMoveFileValidatorFactory> factory) OVERRIDE;
   virtual FilePermissionPolicy GetPermissionPolicy(
       const FileSystemURL& url,
       int permissions) const OVERRIDE;
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index 5ce925e5..b6b9ba0 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -8,6 +8,7 @@
       '../fileapi/async_file_util.h',
       '../fileapi/async_file_util_adapter.cc',
       '../fileapi/async_file_util_adapter.h',
+      '../fileapi/copy_or_move_file_validator.h',
       '../fileapi/cross_operation_delegate.cc',
       '../fileapi/cross_operation_delegate.h',
       '../fileapi/external_mount_points.cc',