blob: 4fdc49b78895cf5ff41b2f73f05d12014e87e9ab [file] [log] [blame]
// Copyright 2019 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 "chromecast/browser/webview/platform_views_rpc_instance.h"
#include <deque>
#include <mutex>
#include <utility>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "chromecast/browser/webview/proto/webview.grpc.pb.h"
#include "chromecast/browser/webview/webview_window_manager.h"
#include "third_party/grpc/src/include/grpcpp/grpcpp.h"
#include "third_party/grpc/src/include/grpcpp/security/server_credentials.h"
#include "third_party/grpc/src/include/grpcpp/server_builder.h"
namespace chromecast {
PlatformViewsRpcInstance::PlatformViewsRpcInstance(
grpc::ServerCompletionQueue* cq,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
WebviewWindowManager* window_manager)
: cq_(cq),
io_(&ctx_),
window_manager_(window_manager),
task_runner_(task_runner) {
write_options_.clear_buffer_hint();
write_options_.clear_corked();
init_callback_ = base::BindRepeating(&PlatformViewsRpcInstance::InitComplete,
base::Unretained(this));
read_callback_ = base::BindRepeating(&PlatformViewsRpcInstance::ReadComplete,
base::Unretained(this));
write_callback_ = base::BindRepeating(
&PlatformViewsRpcInstance::WriteComplete, base::Unretained(this));
destroy_callback_ = base::BindRepeating(
&PlatformViewsRpcInstance::FinishComplete, base::Unretained(this));
}
PlatformViewsRpcInstance::~PlatformViewsRpcInstance() {
if (controller_) {
controller_.release()->Destroy();
}
window_manager_->RemoveObserver(this);
}
void PlatformViewsRpcInstance::FinishComplete(bool ok) {
// Bounce off of the webview thread to allow it to complete any pending work.
task_runner_->DeleteSoon(FROM_HERE, this);
}
void PlatformViewsRpcInstance::OnError(const std::string& error_message) {
std::unique_lock<std::mutex> l(send_lock_);
errored_ = true;
error_message_ = error_message;
}
void PlatformViewsRpcInstance::ProcessRequestOnControllerThread(
std::unique_ptr<webview::WebviewRequest> request) {
if (!errored_)
controller_->ProcessRequest(*request.get());
}
void PlatformViewsRpcInstance::InitComplete(bool ok) {
if (!ok) {
delete this;
return;
}
request_ = std::make_unique<webview::WebviewRequest>();
io_.Read(request_.get(), &read_callback_);
// Create a new instance to handle new requests.
// Instances of this class delete themselves.
CreateNewInstance();
}
void PlatformViewsRpcInstance::ReadComplete(bool ok) {
if (!ok) {
io_.Finish(grpc::Status(), &destroy_callback_);
} else if (controller_) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&PlatformViewsRpcInstance::ProcessRequestOnControllerThread,
base::Unretained(this), std::move(request_)));
request_ = std::make_unique<webview::WebviewRequest>();
std::unique_lock<std::mutex> l(send_lock_);
if (!errored_) {
io_.Read(request_.get(), &read_callback_);
} else if (!send_pending_) {
io_.Finish(grpc::Status(grpc::UNKNOWN, error_message_),
&destroy_callback_);
} else {
// There is a send in progress so let that completion send the Finish.
read_pending_ = false;
}
} else if (!Initialize()) {
io_.Finish(grpc::Status(grpc::FAILED_PRECONDITION, "Failed initialization"),
&destroy_callback_);
}
// In this case initialization is pending and the main webview thread will
// start the first read.
}
void PlatformViewsRpcInstance::WriteComplete(bool ok) {
std::unique_lock<std::mutex> l(send_lock_);
send_pending_ = false;
if (errored_) {
if (!read_pending_) {
io_.Finish(grpc::Status(grpc::UNKNOWN, error_message_),
&destroy_callback_);
}
} else if (!pending_messages_.empty()) {
send_pending_ = true;
io_.Write(*pending_messages_.front().get(), write_options_,
&write_callback_);
pending_messages_.pop_front();
}
}
void PlatformViewsRpcInstance::EnqueueSend(
std::unique_ptr<webview::WebviewResponse> response) {
std::unique_lock<std::mutex> l(send_lock_);
if (errored_)
return;
if (!send_pending_ && pending_messages_.empty()) {
send_pending_ = true;
io_.Write(*response.get(), write_options_, &write_callback_);
} else {
pending_messages_.emplace_back(std::move(response));
}
}
void PlatformViewsRpcInstance::OnNewWebviewContainerWindow(aura::Window* window,
int app_id) {
if (app_id != app_id_)
return;
controller_->AttachTo(window, window_id_);
// The Webview is attached! No reason to keep on listening for window property
// updates.
window_manager_->RemoveObserver(this);
}
} // namespace chromecast