chromium/chrome/browser/ash/fileapi/observable_file_system_operation_impl.cc

// 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