Wire up copy and move events to chromeos::FileChangeService.
This is part two of a series of CLs that will expose file change events
to observers of a new browser context keyed service.
In this CL, the new service is notified of copy and move events
originating from Chrome OS specific file system operations which are
created by the Chrome OS file system backend.
This involved creation of a new chromeos::FileSystemOperation class
serving as a thin extension to the default file system operation impl.
This thin extension allows changes to storage// code to be minimal.
Additional file change events will be wired up in a follow up CL
following the same pattern.
Design: http://shortn/_xrD1GHuIf1
Bug: 1136173
Change-Id: I9b906b309a1fd6b3d3f5b189c683ae6ffdd65f38
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2551636
Commit-Queue: David Black <[email protected]>
Reviewed-by: Toni Baržić <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Marijn Kruisselbrink <[email protected]>
Reviewed-by: Sergei Datsenko <[email protected]>
Cr-Commit-Position: refs/heads/master@{#835046}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3b0478f..c780b7b 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3721,6 +3721,7 @@
content::BrowserContext::GetMountPoints(browser_context);
DCHECK(external_mount_points);
auto backend = std::make_unique<chromeos::FileSystemBackend>(
+ Profile::FromBrowserContext(browser_context),
std::make_unique<chromeos::file_system_provider::BackendDelegate>(),
std::make_unique<chromeos::MTPFileSystemBackendDelegate>(
storage_partition_path),
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 7945a81..e4a582a 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1405,6 +1405,7 @@
"fileapi/file_change_service.h",
"fileapi/file_change_service_factory.cc",
"fileapi/file_change_service_factory.h",
+ "fileapi/file_change_service_observer.h",
"fileapi/file_system_backend.cc",
"fileapi/file_system_backend.h",
"fileapi/file_system_backend_delegate.h",
@@ -1412,6 +1413,8 @@
"fileapi/mtp_file_system_backend_delegate.h",
"fileapi/mtp_watcher_manager.cc",
"fileapi/mtp_watcher_manager.h",
+ "fileapi/observable_file_system_operation_impl.cc",
+ "fileapi/observable_file_system_operation_impl.h",
"fileapi/recent_arc_media_source.cc",
"fileapi/recent_arc_media_source.h",
"fileapi/recent_disk_source.cc",
diff --git a/chrome/browser/chromeos/fileapi/file_change_service.cc b/chrome/browser/chromeos/fileapi/file_change_service.cc
index d3e82fa..39230b5 100644
--- a/chrome/browser/chromeos/fileapi/file_change_service.cc
+++ b/chrome/browser/chromeos/fileapi/file_change_service.cc
@@ -8,47 +8,26 @@
FileChangeService::FileChangeService() = default;
-FileChangeService::~FileChangeService() {
- DCHECK(!observer_list_.might_have_observers());
-}
+FileChangeService::~FileChangeService() = default;
-void FileChangeService::AddObserver(storage::FileChangeObserver* observer) {
+void FileChangeService::AddObserver(FileChangeServiceObserver* observer) {
observer_list_.AddObserver(observer);
}
-void FileChangeService::RemoveObserver(storage::FileChangeObserver* observer) {
+void FileChangeService::RemoveObserver(FileChangeServiceObserver* observer) {
observer_list_.RemoveObserver(observer);
}
-void FileChangeService::OnCreateFile(const storage::FileSystemURL& url) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnCreateFile(url);
+void FileChangeService::NotifyFileCopied(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ for (FileChangeServiceObserver& observer : observer_list_)
+ observer.OnFileCopied(src, dst);
}
-void FileChangeService::OnCreateFileFrom(const storage::FileSystemURL& url,
- const storage::FileSystemURL& src) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnCreateFileFrom(url, src);
-}
-
-void FileChangeService::OnRemoveFile(const storage::FileSystemURL& url) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnRemoveFile(url);
-}
-
-void FileChangeService::OnModifyFile(const storage::FileSystemURL& url) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnModifyFile(url);
-}
-
-void FileChangeService::OnCreateDirectory(const storage::FileSystemURL& url) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnCreateDirectory(url);
-}
-
-void FileChangeService::OnRemoveDirectory(const storage::FileSystemURL& url) {
- for (storage::FileChangeObserver& observer : observer_list_)
- observer.OnRemoveDirectory(url);
+void FileChangeService::NotifyFileMoved(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ for (FileChangeServiceObserver& observer : observer_list_)
+ observer.OnFileMoved(src, dst);
}
} // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/file_change_service.h b/chrome/browser/chromeos/fileapi/file_change_service.h
index 91d7147..4b6c6c5 100644
--- a/chrome/browser/chromeos/fileapi/file_change_service.h
+++ b/chrome/browser/chromeos/fileapi/file_change_service.h
@@ -6,37 +6,35 @@
#define CHROME_BROWSER_CHROMEOS_FILEAPI_FILE_CHANGE_SERVICE_H_
#include "base/observer_list.h"
+#include "chrome/browser/chromeos/fileapi/file_change_service_observer.h"
#include "components/keyed_service/core/keyed_service.h"
-#include "storage/browser/file_system/file_observers.h"
namespace chromeos {
// A service which notifies observers of file change events from external file
// systems. This serves as a bridge to allow for observation of file system
// changes across all file system contexts within a browser context.
-class FileChangeService : public KeyedService,
- public storage::FileChangeObserver {
+class FileChangeService : public KeyedService {
public:
FileChangeService();
FileChangeService(const FileChangeService& other) = delete;
FileChangeService& operator=(const FileChangeService& other) = delete;
~FileChangeService() override;
- // Adds/removes the specified `observer` for file change events.
- void AddObserver(storage::FileChangeObserver* observer);
- void RemoveObserver(storage::FileChangeObserver* observer);
+ // Adds/removes the specified `observer` for service events.
+ void AddObserver(FileChangeServiceObserver* observer);
+ void RemoveObserver(FileChangeServiceObserver* observer);
+
+ // Notifies the service that a file has been copied from `src` to `dst`.
+ void NotifyFileCopied(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst);
+
+ // Notifies the service that a file has been moved from `src` to `dst`.
+ void NotifyFileMoved(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst);
private:
- // storage::FileChangeObserver:
- void OnCreateFile(const storage::FileSystemURL& url) override;
- void OnCreateFileFrom(const storage::FileSystemURL& url,
- const storage::FileSystemURL& src) override;
- void OnRemoveFile(const storage::FileSystemURL& url) override;
- void OnModifyFile(const storage::FileSystemURL& url) override;
- void OnCreateDirectory(const storage::FileSystemURL& url) override;
- void OnRemoveDirectory(const storage::FileSystemURL& url) override;
-
- base::ObserverList<storage::FileChangeObserver>::Unchecked observer_list_;
+ base::ObserverList<FileChangeServiceObserver> observer_list_;
};
} // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/file_change_service_observer.h b/chrome/browser/chromeos/fileapi/file_change_service_observer.h
new file mode 100644
index 0000000..1582d82f
--- /dev/null
+++ b/chrome/browser/chromeos/fileapi/file_change_service_observer.h
@@ -0,0 +1,30 @@
+// Copyright 2020 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 CHROME_BROWSER_CHROMEOS_FILEAPI_FILE_CHANGE_SERVICE_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_FILEAPI_FILE_CHANGE_SERVICE_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+namespace storage {
+class FileSystemURL;
+} // namespace storage
+
+namespace chromeos {
+
+// An interface for an observer which receives `FileChangeService` events.
+class FileChangeServiceObserver : public base::CheckedObserver {
+ public:
+ // Invoked when a file has been copied from `src` to `dst`.
+ virtual void OnFileCopied(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {}
+
+ // Invoked when a file has been moved from `src` to `dst`.
+ virtual void OnFileMoved(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {}
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_FILE_CHANGE_SERVICE_OBSERVER_H_
diff --git a/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc b/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
index a37704a..cc86417d 100644
--- a/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_change_service_unittest.cc
@@ -4,14 +4,142 @@
#include "chrome/browser/chromeos/fileapi/file_change_service.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/scoped_observation.h"
+#include "base/unguessable_token.h"
+#include "chrome/browser/chromeos/file_manager/app_id.h"
+#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/fileapi/file_change_service_factory.h"
+#include "chrome/browser/chromeos/fileapi/file_change_service_observer.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile_manager.h"
+#include "storage/browser/file_system/external_mount_points.h"
+#include "storage/browser/file_system/file_system_operation_runner.h"
+#include "storage/browser/test/async_file_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
namespace chromeos {
namespace {
+// Helpers ---------------------------------------------------------------------
+
+// Returns the file system context associated with the specified `profile`.
+storage::FileSystemContext* GetFileSystemContext(Profile* profile) {
+ return file_manager::util::GetFileSystemContextForExtensionId(
+ profile, file_manager::kFileManagerAppId);
+}
+
+// Returns the file system operation runner associated with the specified
+// `profile`.
+storage::FileSystemOperationRunner* GetFileSystemOperationRunner(
+ Profile* profile) {
+ return GetFileSystemContext(profile)->operation_runner();
+}
+
+// MockFileChangeServiceObserver -----------------------------------------------
+
+class MockFileChangeServiceObserver : public FileChangeServiceObserver {
+ public:
+ // FileChangeServiceObserver:
+ MOCK_METHOD(void,
+ OnFileCopied,
+ (const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst),
+ (override));
+ MOCK_METHOD(void,
+ OnFileMoved,
+ (const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst),
+ (override));
+};
+
+// TempFileSystem --------------------------------------------------------------
+
+// A class which registers a temporary file system and provides convenient APIs
+// for interacting with that file system.
+class TempFileSystem {
+ public:
+ explicit TempFileSystem(Profile* profile)
+ : profile_(profile), name_(base::UnguessableToken::Create().ToString()) {}
+
+ TempFileSystem(const TempFileSystem&) = delete;
+ TempFileSystem& operator=(const TempFileSystem&) = delete;
+
+ ~TempFileSystem() {
+ storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(name_);
+ }
+
+ // Sets up and registers a temporary file system at `temp_dir_`.
+ void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ ASSERT_TRUE(
+ storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+ name_, storage::kFileSystemTypeNativeLocal,
+ storage::FileSystemMountOption(), temp_dir_.GetPath()));
+
+ GetFileSystemContext(profile_)
+ ->external_backend()
+ ->GrantFileAccessToExtension(file_manager::kFileManagerAppId,
+ base::FilePath(name_));
+ }
+
+ // Synchronously creates the file specified by `url`.
+ base::File::Error CreateFile(const storage::FileSystemURL& url) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::CreateFile(context, url);
+ }
+
+ // Returns a file system URL for the specified path relative to `temp_dir_`.
+ storage::FileSystemURL CreateFileSystemURL(const std::string& path) {
+ return GetFileSystemContext(profile_)->CreateCrackedFileSystemURL(
+ origin_, storage::kFileSystemTypeNativeLocal,
+ temp_dir_.GetPath().Append(base::FilePath::FromUTF8Unsafe(path)));
+ }
+
+ // Synchronously copies the file specified by `src` to `dst`.
+ base::File::Error CopyFile(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::Copy(context, src, dst);
+ }
+
+ // Synchronously copies the file specified by `src` to `dst` locally.
+ base::File::Error CopyFileLocal(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::CopyFileLocal(context, src, dst);
+ }
+
+ // Synchronously moves the file specified by `src` to `dst`.
+ base::File::Error MoveFile(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::Move(context, src, dst);
+ }
+
+ // Synchronously moves the file specified by `src` to `dst` locally.
+ base::File::Error MoveFileLocal(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::MoveFileLocal(context, src, dst);
+ }
+
+ // Synchronously removes the file specified by `url`.
+ base::File::Error RemoveFile(const storage::FileSystemURL& url,
+ bool recursive = false) {
+ storage::FileSystemContext* context = GetFileSystemContext(profile_);
+ return storage::AsyncFileTestHelper::Remove(context, url, recursive);
+ }
+
+ private:
+ Profile* const profile_;
+ const url::Origin origin_;
+ const std::string name_;
+ base::ScopedTempDir temp_dir_;
+};
+
// FileChangeServiceTest -------------------------------------------------------
class FileChangeServiceTest : public BrowserWithTestWindowTest {
@@ -58,4 +186,66 @@
ASSERT_NE(primary_profile_service, secondary_profile_service);
}
+// Verifies `OnFileCopied` events are propagated to observers.
+TEST_F(FileChangeServiceTest, PropagatesOnFileCopiedEvents) {
+ auto* profile = GetProfile();
+ auto* service = FileChangeServiceFactory::GetInstance()->GetService(profile);
+ ASSERT_TRUE(service);
+
+ testing::NiceMock<MockFileChangeServiceObserver> mock_observer;
+ base::ScopedObservation<FileChangeService, FileChangeServiceObserver>
+ scoped_observation{&mock_observer};
+ scoped_observation.Observe(service);
+
+ TempFileSystem temp_file_system(profile);
+ temp_file_system.SetUp();
+
+ storage::FileSystemURL src = temp_file_system.CreateFileSystemURL("src");
+ storage::FileSystemURL dst = temp_file_system.CreateFileSystemURL("dst");
+
+ ASSERT_EQ(temp_file_system.CreateFile(src), base::File::FILE_OK);
+
+ EXPECT_CALL(mock_observer, OnFileCopied)
+ .WillRepeatedly([&](const storage::FileSystemURL& propagated_src,
+ const storage::FileSystemURL& propagated_dst) {
+ EXPECT_EQ(src, propagated_src);
+ EXPECT_EQ(dst, propagated_dst);
+ });
+
+ ASSERT_EQ(temp_file_system.CopyFile(src, dst), base::File::FILE_OK);
+ ASSERT_EQ(temp_file_system.RemoveFile(dst), base::File::FILE_OK);
+ ASSERT_EQ(temp_file_system.CopyFileLocal(src, dst), base::File::FILE_OK);
+}
+
+// Verifies `OnFileMoved` events are propagated to observers.
+TEST_F(FileChangeServiceTest, PropagatesOnFileMovedEvents) {
+ auto* profile = GetProfile();
+ auto* service = FileChangeServiceFactory::GetInstance()->GetService(profile);
+ ASSERT_TRUE(service);
+
+ testing::NiceMock<MockFileChangeServiceObserver> mock_observer;
+ base::ScopedObservation<FileChangeService, FileChangeServiceObserver>
+ scoped_observation{&mock_observer};
+ scoped_observation.Observe(service);
+
+ TempFileSystem temp_file_system(profile);
+ temp_file_system.SetUp();
+
+ storage::FileSystemURL src = temp_file_system.CreateFileSystemURL("src");
+ storage::FileSystemURL dst = temp_file_system.CreateFileSystemURL("dst");
+
+ ASSERT_EQ(temp_file_system.CreateFile(src), base::File::FILE_OK);
+
+ EXPECT_CALL(mock_observer, OnFileMoved)
+ .WillRepeatedly([&](const storage::FileSystemURL& propagated_src,
+ const storage::FileSystemURL& propagated_dst) {
+ EXPECT_EQ(src, propagated_src);
+ EXPECT_EQ(dst, propagated_dst);
+ });
+
+ ASSERT_EQ(temp_file_system.MoveFile(src, dst), base::File::FILE_OK);
+ std::swap(dst, src);
+ ASSERT_EQ(temp_file_system.MoveFileLocal(src, dst), base::File::FILE_OK);
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index 04c0bdac..bccee2b 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -19,6 +19,8 @@
#include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
#include "chrome/browser/chromeos/fileapi/file_access_permissions.h"
#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
+#include "chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
#include "chrome/common/url_constants.h"
#include "chromeos/constants/chromeos_switches.h"
@@ -48,6 +50,13 @@
"klimoghijjogocdbaikffefjfcfheiel", // Retail Demo (OOBE),
};
+// Returns the `AccountId` associated with the specified `profile`.
+AccountId GetAccountId(Profile* profile) {
+ user_manager::User* user =
+ profile ? ProfileHelper::Get()->GetUserByProfile(profile) : nullptr;
+ return user ? user->GetAccountId() : AccountId();
+}
+
} // namespace
// static
@@ -65,6 +74,7 @@
}
FileSystemBackend::FileSystemBackend(
+ Profile* profile,
std::unique_ptr<FileSystemBackendDelegate> file_system_provider_delegate,
std::unique_ptr<FileSystemBackendDelegate> mtp_delegate,
std::unique_ptr<FileSystemBackendDelegate> arc_content_delegate,
@@ -73,7 +83,8 @@
std::unique_ptr<FileSystemBackendDelegate> smbfs_delegate,
scoped_refptr<storage::ExternalMountPoints> mount_points,
storage::ExternalMountPoints* system_mount_points)
- : file_access_permissions_(new FileAccessPermissions()),
+ : account_id_(GetAccountId(profile)),
+ file_access_permissions_(new FileAccessPermissions()),
local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()),
file_system_provider_delegate_(std::move(file_system_provider_delegate)),
mtp_delegate_(std::move(mtp_delegate)),
@@ -340,8 +351,8 @@
if (url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage) {
// MTP file operations run on MediaTaskRunner.
- return storage::FileSystemOperation::Create(
- url, context,
+ return new ObservableFileSystemOperationImpl(
+ account_id_, url, context,
std::make_unique<storage::FileSystemOperationContext>(
context, MediaFileSystemBackend::MediaTaskRunner().get()));
}
@@ -349,8 +360,8 @@
url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
url.type() == storage::kFileSystemTypeDriveFs ||
url.type() == storage::kFileSystemTypeSmbFs) {
- return storage::FileSystemOperation::Create(
- url, context,
+ return new ObservableFileSystemOperationImpl(
+ account_id_, url, context,
std::make_unique<storage::FileSystemOperationContext>(
context, base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE})
@@ -360,8 +371,8 @@
DCHECK(url.type() == storage::kFileSystemTypeProvided ||
url.type() == storage::kFileSystemTypeArcContent ||
url.type() == storage::kFileSystemTypeArcDocumentsProvider);
- return storage::FileSystemOperation::Create(
- url, context,
+ return new ObservableFileSystemOperationImpl(
+ account_id_, url, context,
std::make_unique<storage::FileSystemOperationContext>(context));
}
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.h b/chrome/browser/chromeos/fileapi/file_system_backend.h
index eae2bff..61327c0 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.h
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.h
@@ -15,10 +15,13 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "components/account_id/account_id.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
#include "storage/common/file_system/file_system_types.h"
+class Profile;
+
namespace storage {
class CopyOrMoveFileValidatorFactory;
class ExternalMountPoints;
@@ -73,6 +76,7 @@
// |system_mount_points| should outlive FileSystemBackend instance.
FileSystemBackend(
+ Profile* profile,
std::unique_ptr<FileSystemBackendDelegate> file_system_provider_delegate,
std::unique_ptr<FileSystemBackendDelegate> mtp_delegate,
std::unique_ptr<FileSystemBackendDelegate> arc_content_delegate,
@@ -146,6 +150,8 @@
const base::FilePath& entry_path) const override;
private:
+ const AccountId account_id_;
+
std::unique_ptr<FileAccessPermissions> file_access_permissions_;
std::unique_ptr<storage::AsyncFileUtil> local_file_util_;
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index bf4ea629..bd706020 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -40,6 +40,7 @@
scoped_refptr<storage::ExternalMountPoints> mount_points(
storage::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(
+ nullptr, // profile
nullptr, // file_system_provider_delegate
nullptr, // mtp_delegate
nullptr, // arc_content_delegate
@@ -69,6 +70,7 @@
storage::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(
+ nullptr, // profile
nullptr, // file_system_provider_delegate
nullptr, // mtp_delegate
nullptr, // arc_content_delegate
@@ -114,6 +116,7 @@
scoped_refptr<storage::ExternalMountPoints> system_mount_points(
storage::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(
+ nullptr, // profile
nullptr, // file_system_provider_delegate
nullptr, // mtp_delegate
nullptr, // arc_content_delegate
@@ -185,6 +188,7 @@
scoped_refptr<storage::ExternalMountPoints> system_mount_points(
storage::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(
+ nullptr, // profile
nullptr, // file_system_provider_delegate
nullptr, // mtp_delegate
nullptr, // arc_content_delegate
diff --git a/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.cc b/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.cc
new file mode 100644
index 0000000..03504bc
--- /dev/null
+++ b/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.cc
@@ -0,0 +1,147 @@
+// Copyright 2020 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 "chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.h"
+
+#include "chrome/browser/chromeos/fileapi/file_change_service.h"
+#include "chrome/browser/chromeos/fileapi/file_change_service_factory.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace chromeos {
+
+namespace {
+
+using StatusCallback = storage::FileSystemOperation::StatusCallback;
+
+// Helpers ---------------------------------------------------------------------
+
+// Returns the `FileChangeService` associated with the given `account_id`.
+FileChangeService* GetFileChangeService(const AccountId& account_id) {
+ Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
+ return profile ? FileChangeServiceFactory::GetInstance()->GetService(profile)
+ : nullptr;
+}
+
+// Notifies the `FileChangeService` associated with the given `account_id` of a
+// file being copied from `src` to `dst`. This method may only be called from
+// the browser UI thread.
+void NotifyFileCopiedOnUiThread(const AccountId& account_id,
+ const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ FileChangeService* service = GetFileChangeService(account_id);
+ if (service)
+ service->NotifyFileCopied(src, dst);
+}
+
+// Notifies the `FileChangeService` associated with the given `account_id` of a
+// file being moved form `src` to `dst`. This method may only be called from the
+// browser UI thread.
+void NotifyFileMovedOnUiThread(const AccountId& account_id,
+ const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ FileChangeService* service = GetFileChangeService(account_id);
+ if (service)
+ service->NotifyFileMoved(src, dst);
+}
+
+// Returns a `StatusCallback` which runs the specified callbacks in order.
+StatusCallback RunInOrderCallback(StatusCallback a, StatusCallback b) {
+ return base::BindOnce(
+ [](StatusCallback a, StatusCallback b, base::File::Error result) {
+ std::move(a).Run(result);
+ std::move(b).Run(result);
+ },
+ std::move(a), std::move(b));
+}
+
+// Returns a `StatusCallback` which runs the specified `closure` on the browser
+// UI thread if `result` indicates success.
+StatusCallback RunOnUiThreadOnSuccessCallback(base::OnceClosure closure) {
+ return base::BindOnce(
+ [](base::OnceClosure closure, base::File::Error result) {
+ if (result == base::File::FILE_OK) {
+ auto task_runner = content::GetUIThreadTaskRunner({});
+ task_runner->PostTask(FROM_HERE, std::move(closure));
+ }
+ },
+ std::move(closure));
+}
+
+} // namespace
+
+// ObservableFileSystemOperationImpl -------------------------------------------
+
+ObservableFileSystemOperationImpl::ObservableFileSystemOperationImpl(
+ const AccountId& account_id,
+ const storage::FileSystemURL& url,
+ storage::FileSystemContext* file_system_context,
+ std::unique_ptr<storage::FileSystemOperationContext> operation_context)
+ : storage::FileSystemOperationImpl(url,
+ file_system_context,
+ std::move(operation_context)),
+ account_id_(account_id) {}
+
+ObservableFileSystemOperationImpl::~ObservableFileSystemOperationImpl() =
+ default;
+
+void ObservableFileSystemOperationImpl::Copy(
+ const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ ErrorBehavior error_behavior,
+ const CopyProgressCallback& progress_callback,
+ StatusCallback callback) {
+ storage::FileSystemOperationImpl::Copy(
+ src, dst, option, error_behavior, progress_callback,
+ RunInOrderCallback(
+ RunOnUiThreadOnSuccessCallback(base::BindOnce(
+ &NotifyFileCopiedOnUiThread, account_id_, src, dst)),
+ std::move(callback)));
+}
+
+void ObservableFileSystemOperationImpl::CopyFileLocal(
+ const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ const CopyFileProgressCallback& progress_callback,
+ StatusCallback callback) {
+ storage::FileSystemOperationImpl::CopyFileLocal(
+ src, dst, option, progress_callback,
+ RunInOrderCallback(
+ RunOnUiThreadOnSuccessCallback(base::BindOnce(
+ &NotifyFileCopiedOnUiThread, account_id_, src, dst)),
+ std::move(callback)));
+}
+
+void ObservableFileSystemOperationImpl::Move(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ StatusCallback callback) {
+ storage::FileSystemOperationImpl::Move(
+ src, dst, option,
+ RunInOrderCallback(
+ RunOnUiThreadOnSuccessCallback(base::BindOnce(
+ &NotifyFileMovedOnUiThread, account_id_, src, dst)),
+ std::move(callback)));
+}
+
+void ObservableFileSystemOperationImpl::MoveFileLocal(
+ const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ StatusCallback callback) {
+ storage::FileSystemOperationImpl::MoveFileLocal(
+ src, dst, option,
+ RunInOrderCallback(
+ RunOnUiThreadOnSuccessCallback(base::BindOnce(
+ &NotifyFileMovedOnUiThread, account_id_, src, dst)),
+ std::move(callback)));
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.h b/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.h
new file mode 100644
index 0000000..698d9c8
--- /dev/null
+++ b/chrome/browser/chromeos/fileapi/observable_file_system_operation_impl.h
@@ -0,0 +1,59 @@
+// Copyright 2020 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 CHROME_BROWSER_CHROMEOS_FILEAPI_OBSERVABLE_FILE_SYSTEM_OPERATION_IMPL_H_
+#define CHROME_BROWSER_CHROMEOS_FILEAPI_OBSERVABLE_FILE_SYSTEM_OPERATION_IMPL_H_
+
+#include <memory>
+
+#include "components/account_id/account_id.h"
+#include "storage/browser/file_system/file_system_operation_impl.h"
+
+namespace chromeos {
+
+// A thin extension to the default `storage::FileSystemOperationImpl` which
+// notifies the `FileChangeService` of file change events.
+class ObservableFileSystemOperationImpl
+ : public storage::FileSystemOperationImpl {
+ public:
+ ObservableFileSystemOperationImpl(
+ const AccountId& account_id,
+ const storage::FileSystemURL& url,
+ storage::FileSystemContext* file_system_context,
+ std::unique_ptr<storage::FileSystemOperationContext> operation_context);
+ ObservableFileSystemOperationImpl(const ObservableFileSystemOperationImpl&) =
+ delete;
+ ObservableFileSystemOperationImpl& operator=(
+ const ObservableFileSystemOperationImpl&) = delete;
+ ~ObservableFileSystemOperationImpl() override;
+
+ private:
+ // storage::FileSystemOperationImpl:
+ void Copy(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ ErrorBehavior error_behavior,
+ const CopyProgressCallback& progress_callback,
+ StatusCallback callback) override;
+ void CopyFileLocal(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ const CopyFileProgressCallback& progress_callback,
+ StatusCallback callback) override;
+ void Move(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ StatusCallback callback) override;
+ void MoveFileLocal(const storage::FileSystemURL& src,
+ const storage::FileSystemURL& dst,
+ CopyOrMoveOption option,
+ StatusCallback callback) override;
+
+ const AccountId account_id_;
+ base::WeakPtrFactory<ObservableFileSystemOperationImpl> weak_factory_{this};
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_OBSERVABLE_FILE_SYSTEM_OPERATION_IMPL_H_
diff --git a/chrome/browser/platform_util_unittest.cc b/chrome/browser/platform_util_unittest.cc
index 6b81370..b093629 100644
--- a/chrome/browser/platform_util_unittest.cc
+++ b/chrome/browser/platform_util_unittest.cc
@@ -59,7 +59,13 @@
// New FileSystemBackend that uses our MockSpecialStoragePolicy.
additional_backends->push_back(
std::make_unique<chromeos::FileSystemBackend>(
- nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, // profile
+ nullptr, // file_system_provider_delegate
+ nullptr, // mtp_delegate
+ nullptr, // arc_content_delegate
+ nullptr, // arc_documents_provider_delegate
+ nullptr, // drivefs_delegate
+ nullptr, // smbfs_delegate
external_mount_points,
storage::ExternalMountPoints::GetSystemInstance()));
}