blob: aadfaaac60f45f1e9558f97ffa380ace6c15bdc9 [file] [log] [blame]
// Copyright 2018 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 "remoting/host/file_transfer/ipc_file_operations.h"
#include <cstdint>
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "remoting/protocol/file_transfer_helpers.h"
namespace remoting {
using ResultCallback =
base::OnceCallback<void(base::Optional<protocol::FileTransfer_Error>)>;
class IpcFileOperations::IpcWriter : public FileOperations::Writer {
public:
IpcWriter(std::uint64_t file_id, base::WeakPtr<SharedState> shared_state);
~IpcWriter() override;
// FileOperations::Writer implementation.
void WriteChunk(std::string data, Callback callback) override;
void Close(Callback callback) override;
void Cancel() override;
State state() override;
private:
void OnWriteChunkResult(Callback callback,
base::Optional<protocol::FileTransfer_Error> error);
void OnCloseResult(Callback callback,
base::Optional<protocol::FileTransfer_Error> error);
State state_ = kReady;
std::uint64_t file_id_;
base::WeakPtr<SharedState> shared_state_;
DISALLOW_COPY_AND_ASSIGN(IpcWriter);
};
IpcFileOperations::~IpcFileOperations() = default;
void IpcFileOperations::WriteFile(const base::FilePath& filename,
WriteFileCallback callback) {
if (!shared_state_) {
return;
}
std::uint64_t file_id = shared_state_->next_file_id++;
auto writer = std::make_unique<IpcWriter>(file_id, shared_state_);
shared_state_->result_callbacks.emplace(
file_id, base::BindOnce(OnWriteFileResult, std::move(writer),
std::move(callback)));
shared_state_->request_handler->WriteFile(file_id, filename);
}
void IpcFileOperations::ReadFile(ReadFileCallback) {
NOTIMPLEMENTED();
}
IpcFileOperations::SharedState::SharedState(RequestHandler* request_handler)
: request_handler(request_handler), weak_ptr_factory(this) {}
IpcFileOperations::SharedState::~SharedState() = default;
IpcFileOperations::IpcFileOperations(base::WeakPtr<SharedState> shared_state)
: shared_state_(std::move(shared_state)) {}
void IpcFileOperations::OnWriteFileResult(
std::unique_ptr<IpcWriter> writer,
WriteFileCallback callback,
base::Optional<protocol::FileTransfer_Error> error) {
if (error) {
// If there's an error, writer should be null.
writer.reset();
}
std::move(callback).Run(std::move(error), std::move(writer));
}
IpcFileOperationsFactory::IpcFileOperationsFactory(
IpcFileOperations::RequestHandler* request_handler)
: shared_state_(request_handler) {}
IpcFileOperationsFactory::~IpcFileOperationsFactory() = default;
std::unique_ptr<FileOperations>
IpcFileOperationsFactory::CreateFileOperations() {
return base::WrapUnique(
new IpcFileOperations(shared_state_.weak_ptr_factory.GetWeakPtr()));
}
void IpcFileOperationsFactory::OnResult(
uint64_t file_id,
base::Optional<protocol::FileTransfer_Error> error) {
auto callback_iter = shared_state_.result_callbacks.find(file_id);
if (callback_iter != shared_state_.result_callbacks.end()) {
ResultCallback callback = std::move(callback_iter->second);
shared_state_.result_callbacks.erase(callback_iter);
std::move(callback).Run(error);
}
}
IpcFileOperations::IpcWriter::IpcWriter(std::uint64_t file_id,
base::WeakPtr<SharedState> shared_state)
: file_id_(file_id), shared_state_(std::move(shared_state)) {}
IpcFileOperations::IpcWriter::~IpcWriter() {
if (!shared_state_) {
return;
}
Cancel();
// Destroy any pending callbacks.
auto callback_iter = shared_state_->result_callbacks.find(file_id_);
if (callback_iter != shared_state_->result_callbacks.end()) {
shared_state_->result_callbacks.erase(callback_iter);
}
}
void IpcFileOperations::IpcWriter::WriteChunk(std::string data,
Callback callback) {
DCHECK_EQ(kReady, state_);
if (!shared_state_) {
return;
}
state_ = kBusy;
shared_state_->request_handler->WriteChunk(file_id_, data);
// Unretained is sound because IpcWriter will destroy any outstanding callback
// in its destructor.
shared_state_->result_callbacks.emplace(
file_id_, base::BindOnce(&IpcWriter::OnWriteChunkResult,
base::Unretained(this), std::move(callback)));
}
void IpcFileOperations::IpcWriter::Close(Callback callback) {
DCHECK_EQ(kReady, state_);
if (!shared_state_) {
return;
}
state_ = kBusy;
shared_state_->request_handler->Close(file_id_);
// Unretained is sound because IpcWriter will destroy any outstanding callback
// in its destructor.
shared_state_->result_callbacks.emplace(
file_id_, base::BindOnce(&IpcWriter::OnCloseResult,
base::Unretained(this), std::move(callback)));
}
void IpcFileOperations::IpcWriter::Cancel() {
if (!shared_state_ || state_ == kClosed || state_ == kFailed) {
return;
}
shared_state_->request_handler->Cancel(file_id_);
}
FileOperations::State IpcFileOperations::IpcWriter::state() {
return state_;
}
void IpcFileOperations::IpcWriter::OnWriteChunkResult(
Callback callback,
base::Optional<protocol::FileTransfer_Error> error) {
if (error) {
state_ = kFailed;
} else {
state_ = kReady;
}
std::move(callback).Run(error);
}
void IpcFileOperations::IpcWriter::OnCloseResult(
Callback callback,
base::Optional<protocol::FileTransfer_Error> error) {
if (error) {
state_ = kFailed;
} else {
state_ = kClosed;
}
std::move(callback).Run(error);
}
} // namespace remoting