// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/fileapi/observable_file_system_operation_impl.h"
#include "chrome/browser/ash/fileapi/file_change_service.h"
#include "chrome/browser/ash/fileapi/file_change_service_factory.h"
#include "chrome/browser/ash/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"
#include "storage/browser/file_system/copy_or_move_hook_delegate.h"
namespace ash {
namespace {
using StatusCallback = storage::FileSystemOperation::StatusCallback;
using WriteCallback = storage::FileSystemOperation::WriteCallback;
// 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 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);
}
// Notifies the `FileChangeService` associated with the given `account_id` of a
// file under `url` getting modified. This method may only be called from the
// browser UI thread.
void NotifyFileModifiedOnUiThread(const AccountId& account_id,
const storage::FileSystemURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
FileChangeService* service = GetFileChangeService(account_id);
if (service)
service->NotifyFileModified(url);
}
// Returns a `WriteCallback` which runs the specified callbacks in order.
WriteCallback RunInOrderCallback(WriteCallback a, WriteCallback b) {
return base::BindRepeating(
[](WriteCallback a, WriteCallback b, base::File::Error result,
int64_t bytes, bool complete) {
std::move(a).Run(result, bytes, complete);
std::move(b).Run(result, bytes, complete);
},
std::move(a), std::move(b));
}
// 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));
}
// Returns a `WriteCallback` which runs the specified `closure` on the browser
// UI thread if `complete` is set.
WriteCallback RunOnUiThreadOnCompleteCallback(
const base::RepeatingClosure& closure) {
return base::BindRepeating(
[](const base::RepeatingClosure& closure, base::File::Error result,
int64_t bytes, bool complete) {
if (complete && 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,
storage::OperationType type,
const storage::FileSystemURL& url,
storage::FileSystemContext* file_system_context,
std::unique_ptr<storage::FileSystemOperationContext> operation_context)
: storage::FileSystemOperationImpl(
type,
url,
file_system_context,
std::move(operation_context),
storage::FileSystemOperation::CreatePassKey()),
account_id_(account_id) {}
ObservableFileSystemOperationImpl::~ObservableFileSystemOperationImpl() =
default;
void ObservableFileSystemOperationImpl::Move(
const storage::FileSystemURL& src,
const storage::FileSystemURL& dst,
CopyOrMoveOptionSet options,
ErrorBehavior error_behavior,
std::unique_ptr<storage::CopyOrMoveHookDelegate> copy_or_move_hook_delegate,
StatusCallback callback) {
storage::FileSystemOperationImpl::Move(
src, dst, options, error_behavior, std::move(copy_or_move_hook_delegate),
RunInOrderCallback(
RunOnUiThreadOnSuccessCallback(base::BindOnce(
&NotifyFileMovedOnUiThread, account_id_, src, dst)),
std::move(callback)));
}
void ObservableFileSystemOperationImpl::MoveFileLocal(
const storage::FileSystemURL& src,
const storage::FileSystemURL& dst,
CopyOrMoveOptionSet options,
StatusCallback callback) {
storage::FileSystemOperationImpl::MoveFileLocal(
src, dst, options,
RunInOrderCallback(
RunOnUiThreadOnSuccessCallback(base::BindOnce(
&NotifyFileMovedOnUiThread, account_id_, src, dst)),
std::move(callback)));
}
void ObservableFileSystemOperationImpl::WriteBlob(
const storage::FileSystemURL& url,
std::unique_ptr<storage::FileWriterDelegate> writer_delegate,
std::unique_ptr<storage::BlobReader> blob_reader,
const WriteCallback& callback) {
storage::FileSystemOperationImpl::WriteBlob(
url, std::move(writer_delegate), std::move(blob_reader),
RunInOrderCallback(RunOnUiThreadOnCompleteCallback(base::BindRepeating(
&NotifyFileModifiedOnUiThread, account_id_, url)),
std::move(callback)));
}
void ObservableFileSystemOperationImpl::Write(
const storage::FileSystemURL& url,
std::unique_ptr<storage::FileWriterDelegate> writer_delegate,
mojo::ScopedDataPipeConsumerHandle data_pipe,
const WriteCallback& callback) {
storage::FileSystemOperationImpl::Write(
url, std::move(writer_delegate), std::move(data_pipe),
RunInOrderCallback(RunOnUiThreadOnCompleteCallback(base::BindRepeating(
&NotifyFileModifiedOnUiThread, account_id_, url)),
std::move(callback)));
}
void ObservableFileSystemOperationImpl::Truncate(
const storage::FileSystemURL& url,
int64_t length,
StatusCallback callback) {
storage::FileSystemOperationImpl::Truncate(
url, length,
RunInOrderCallback(RunOnUiThreadOnSuccessCallback(base::BindOnce(
&NotifyFileModifiedOnUiThread, account_id_, url)),
std::move(callback)));
}
} // namespace ash