910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/file_transfer/session_file_operations_handler.h"

#include <memory>
#include <optional>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "remoting/host/mojom/desktop_session.mojom.h"
#include "remoting/protocol/file_transfer_helpers.h"

namespace remoting {

namespace {

using BeginFileReadCallback =
    SessionFileOperationsHandler::BeginFileReadCallback;
using BeginFileWriteCallback =
    SessionFileOperationsHandler::BeginFileWriteCallback;

using Reader = FileOperations::Reader;
using Writer = FileOperations::Writer;

class MojoFileReader : public mojom::FileReader {
 public:
  explicit MojoFileReader(std::unique_ptr<Reader> file_reader);

  MojoFileReader(const MojoFileReader&) = delete;
  MojoFileReader& operator=(const MojoFileReader&) = delete;

  ~MojoFileReader() override = default;

  base::WeakPtr<MojoFileReader> GetWeakPtr();

  // Requests |file_reader_| to open an existing file for reading.
  void Open(Reader::OpenCallback callback);
  // Receives the result from Open() and returns it over the mojo channel.
  void OnFileOpened(BeginFileReadCallback callback,
                    mojo::PendingAssociatedRemote<mojom::FileReader> remote,
                    Reader::OpenResult result);

  // mojom::FileReader implementation.
  void ReadChunk(uint64_t bytes_to_read, ReadChunkCallback callback) override;

  const base::FilePath& filename() const { return file_reader_->filename(); }

  uint64_t size() const { return file_reader_->size(); }

 private:
  const std::unique_ptr<Reader> file_reader_;
  base::WeakPtrFactory<MojoFileReader> weak_ptr_factory{this};
};

class MojoFileWriter : public mojom::FileWriter {
 public:
  explicit MojoFileWriter(std::unique_ptr<Writer> file_writer);

  MojoFileWriter(const MojoFileWriter&) = delete;
  MojoFileWriter& operator=(const MojoFileWriter&) = delete;

  ~MojoFileWriter() override = default;

  // Requests |file_writer_| to open a new file for writing.
  void Open(const base::FilePath& filename, Writer::Callback callback);
  // Receives the result from Open() and returns it over the mojo channel.
  void OnFileOpened(BeginFileWriteCallback callback,
                    mojo::PendingAssociatedRemote<mojom::FileWriter> remote,
                    Writer::Result result);

  // mojom::FileWriter implementation.
  void WriteChunk(const std::vector<std::uint8_t>& data,
                  WriteChunkCallback callback) override;
  void CloseFile(CloseFileCallback callback) override;

  base::WeakPtr<MojoFileWriter> GetWeakPtr();

 private:
  const std::unique_ptr<Writer> file_writer_;
  base::WeakPtrFactory<MojoFileWriter> weak_ptr_factory{this};
};

MojoFileReader::MojoFileReader(std::unique_ptr<Reader> file_reader)
    : file_reader_(std::move(file_reader)) {}

base::WeakPtr<MojoFileReader> MojoFileReader::GetWeakPtr() {
  return weak_ptr_factory.GetWeakPtr();
}

void MojoFileReader::Open(Reader::OpenCallback callback) {
  file_reader_->Open(std::move(callback));
}

void MojoFileReader::OnFileOpened(
    BeginFileReadCallback callback,
    mojo::PendingAssociatedRemote<mojom::FileReader> remote,
    Reader::OpenResult result) {
  if (result.is_error()) {
    std::move(callback).Run(
        mojom::BeginFileReadResult::NewError(result.error()));
    // This will asynchronously trigger the destruction of this object.
    remote.reset();
    return;
  }

  std::move(callback).Run(mojom::BeginFileReadResult::NewSuccess(
      mojom::BeginFileReadSuccess::New(std::move(remote), filename(), size())));
}

// The Mojo-generated ReadChunkCallback has a const& param whereas the
// FileReader::ReadCallback does not so this function wraps the Mojo callback
// so it is compatible with the FileReader interface.
void ReadChunkCallbackWrapper(
    mojom::FileReader::ReadChunkCallback callback,
    protocol::FileTransferResult<std::vector<std::uint8_t>> result) {
  std::move(callback).Run(result);
}

void MojoFileReader::ReadChunk(uint64_t bytes_to_read,
                               ReadChunkCallback callback) {
  file_reader_->ReadChunk(
      bytes_to_read,
      base::BindOnce(&ReadChunkCallbackWrapper, std::move(callback)));
}

MojoFileWriter::MojoFileWriter(std::unique_ptr<Writer> file_writer)
    : file_writer_(std::move(file_writer)) {}

void MojoFileWriter::Open(const base::FilePath& filename,
                          Writer::Callback callback) {
  file_writer_->Open(filename, std::move(callback));
}

void MojoFileWriter::OnFileOpened(
    BeginFileWriteCallback callback,
    mojo::PendingAssociatedRemote<mojom::FileWriter> remote,
    Writer::Result result) {
  if (result.is_error()) {
    std::move(callback).Run(
        mojom::BeginFileWriteResult::NewError(result.error()));
    // This will asynchronously trigger the destruction of this object.
    remote.reset();
    return;
  }

  std::move(callback).Run(mojom::BeginFileWriteResult::NewSuccess(
      mojom::BeginFileWriteSuccess::New(std::move(remote))));
}

base::WeakPtr<MojoFileWriter> MojoFileWriter::GetWeakPtr() {
  return weak_ptr_factory.GetWeakPtr();
}

// Wraps a Mojo-generated callback to provide compatibility with the FileWriter
// methods which expect a Writer::Callback.
template <class T>
void FileWriterCallbackWrapper(T callback, remoting::Writer::Result result) {
  std::optional<protocol::FileTransfer_Error> error;
  if (result.is_error()) {
    error = std::move(result.error());
  }
  std::move(callback).Run(std::move(error));
}

void MojoFileWriter::WriteChunk(const std::vector<std::uint8_t>& data,
                                WriteChunkCallback callback) {
  file_writer_->WriteChunk(
      std::move(data),
      base::BindOnce(&FileWriterCallbackWrapper<WriteChunkCallback>,
                     std::move(callback)));
}

void MojoFileWriter::CloseFile(CloseFileCallback callback) {
  file_writer_->Close(base::BindOnce(
      &FileWriterCallbackWrapper<CloseFileCallback>, std::move(callback)));
}

}  // namespace

SessionFileOperationsHandler::SessionFileOperationsHandler(
    std::unique_ptr<FileOperations> file_operations)
    : file_operations_(std::move(file_operations)) {}

SessionFileOperationsHandler::~SessionFileOperationsHandler() = default;

void SessionFileOperationsHandler::BeginFileRead(
    BeginFileReadCallback callback) {
  mojo::AssociatedRemote<mojom::FileReader> remote;
  auto mojo_file_reader =
      std::make_unique<MojoFileReader>(file_operations_->CreateReader());
  MojoFileReader* ptr = mojo_file_reader.get();

  // |file_readers_| now manages the lifetime of |mojo_file_reader| and will
  // destroy the instance when |remote| is reset.
  file_readers_.Add(std::move(mojo_file_reader),
                    remote.BindNewEndpointAndPassReceiver());

  // We Unbind() |remote| so we can return it to the other end of the IPC
  // channel, this will not cause |mojo_file_reader| to be destroyed.
  ptr->Open(base::BindOnce(&MojoFileReader::OnFileOpened, ptr->GetWeakPtr(),
                           std::move(callback), remote.Unbind()));
}

void SessionFileOperationsHandler::BeginFileWrite(
    const base::FilePath& file_path,
    BeginFileWriteCallback callback) {
  mojo::AssociatedRemote<mojom::FileWriter> remote;
  auto mojo_file_writer =
      std::make_unique<MojoFileWriter>(file_operations_->CreateWriter());
  MojoFileWriter* ptr = mojo_file_writer.get();

  // |file_writers_| now manages the lifetime of |mojo_file_writer| and will
  // destroy the instance when |remote| is reset.
  file_writers_.Add(std::move(mojo_file_writer),
                    remote.BindNewEndpointAndPassReceiver());

  // We Unbind() |remote| so we can return it to the other end of the IPC
  // channel, this will not cause |mojo_file_writer| to be destroyed.
  ptr->Open(file_path,
            base::BindOnce(&MojoFileWriter::OnFileOpened, ptr->GetWeakPtr(),
                           std::move(callback), remote.Unbind()));
}

}  // namespace remoting