blob: 27e1cfd2934072ceceb15413cc394ef24d35da7a [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 "fuchsia/runners/cast/api_bindings_client.h"
#include <utility>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/string_piece.h"
namespace {
uint64_t kBindingsIdStart = 0xFF0000;
} // namespace
ApiBindingsClient::ApiBindingsClient(
fidl::InterfaceHandle<chromium::cast::ApiBindings> bindings_service,
base::OnceClosure on_bindings_received_callback)
: bindings_service_(bindings_service.Bind()),
on_bindings_received_callback_(std::move(on_bindings_received_callback)) {
DCHECK(bindings_service_);
DCHECK(on_bindings_received_callback_);
bindings_service_->GetAll(
fit::bind_member(this, &ApiBindingsClient::OnBindingsReceived));
bindings_service_.set_error_handler([this](zx_status_t status) mutable {
ZX_LOG_IF(ERROR, status != ZX_OK, status)
<< "ApiBindings disconnected before bindings were read.";
if (!bindings_) {
// The Agent disconnected before sending a bindings response,
// so it's possible that the Agent doesn't yet implement the ApiBindings
// service. Populate the bindings with an empty set so initialization may
// continue.
// TODO(crbug.com/953958): Remove this fallback once the Agent implements
// ApiBindings.
LOG(WARNING)
<< "Couldn't receive bindings from Agent, proceeding anyway.";
OnBindingsReceived({});
}
});
}
void ApiBindingsClient::OnBindingsReceived(
std::vector<chromium::cast::ApiBinding> bindings) {
DCHECK(on_bindings_received_callback_);
bindings_ = std::move(bindings);
std::move(on_bindings_received_callback_).Run();
}
void ApiBindingsClient::AttachToFrame(fuchsia::web::Frame* frame,
NamedMessagePortConnector* connector,
base::OnceClosure on_error_callback) {
DCHECK(!frame_) << "AttachToFrame() was called twice.";
DCHECK(frame);
DCHECK(connector);
DCHECK(bindings_)
<< "AttachToFrame() was called before bindings were received.";
connector_ = connector;
frame_ = frame;
bindings_service_.set_error_handler([on_error_callback =
std::move(on_error_callback)](
zx_status_t status) mutable {
ZX_LOG_IF(ERROR, status != ZX_OK, status) << "ApiBindings disconnected.";
std::move(on_error_callback).Run();
});
connector_->RegisterDefaultHandler(base::BindRepeating(
&ApiBindingsClient::OnPortConnected, base::Unretained(this)));
// Enumerate and inject all scripts in |bindings|.
uint64_t bindings_id = kBindingsIdStart;
for (chromium::cast::ApiBinding& entry : *bindings_) {
frame_->AddBeforeLoadJavaScript(
bindings_id++, {"*"}, std::move(*entry.mutable_before_load_script()),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
CHECK(result.is_response()) << "JavaScript injection error: "
<< static_cast<uint32_t>(result.err());
});
}
}
ApiBindingsClient::~ApiBindingsClient() {
if (connector_ && frame_) {
connector_->RegisterDefaultHandler({});
// Remove all injected scripts using their automatically enumerated IDs.
for (uint64_t i = 0; i < bindings_->size(); ++i)
frame_->RemoveBeforeLoadJavaScript(kBindingsIdStart + i);
}
}
void ApiBindingsClient::OnPortConnected(
base::StringPiece port_name,
fidl::InterfaceHandle<fuchsia::web::MessagePort> port) {
if (bindings_service_)
bindings_service_->Connect(port_name.as_string(), std::move(port));
}
bool ApiBindingsClient::HasBindings() const {
return bindings_.has_value();
}