blob: a0fd8f7cd8157fb7bb31215fe95073a765a25075 [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/js_channel_service.h"
#include <algorithm>
#include "base/lazy_instance.h"
#include "base/observer_list.h"
#include "content/public/browser/render_process_host.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
namespace chromecast {
namespace {
class JsChannelImpl : public mojom::JsChannel {
public:
JsChannelImpl(const std::string& channel, JsChannelCallback callback);
~JsChannelImpl() override;
private:
void PostMessage(const std::string& message) override;
std::string channel_;
JsChannelCallback callback_;
DISALLOW_COPY_AND_ASSIGN(JsChannelImpl);
};
// The web contents and channel implementations don't know about each other
// so keep some global state to allow access.
struct JsChannelsGlobalState {
static JsChannelsGlobalState& Get();
struct Instance {
int process_id;
int routing_id;
JsClientInstance* instance;
};
std::vector<Instance> instance_list;
base::ObserverList<JsClientInstance::Observer> observer_list;
};
static base::LazyInstance<JsChannelsGlobalState>::DestructorAtExit
g_global_state = LAZY_INSTANCE_INITIALIZER;
// static
JsChannelsGlobalState& JsChannelsGlobalState::Get() {
return g_global_state.Get();
}
} // namespace
JsChannelService::JsChannelService(int process_id) : process_id_(process_id) {}
JsChannelService::~JsChannelService() = default;
// static
void JsChannelService::Create(
content::RenderProcessHost* render_process_host,
mojo::PendingReceiver<mojom::JsChannelBindingProvider> receiver,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
mojo::MakeSelfOwnedReceiver(
std::make_unique<JsChannelService>(render_process_host->GetID()),
std::move(receiver), std::move(task_runner));
}
void JsChannelService::Register(
int routing_id,
mojo::PendingRemote<mojom::JsChannelClient> client) {
if (JsClientInstance::Find(process_id_, routing_id)) {
LOG(ERROR) << "Duplicate process/routing ID pair!";
} else {
// ClientInstance's lifetime is tied to the JsChannelClient mojo
// channel. It will delete itself when that channel closes.
new JsClientInstance(process_id_, routing_id, std::move(client));
}
}
JsClientInstance::JsClientInstance(
int process_id,
int routing_id,
mojo::PendingRemote<mojom::JsChannelClient> client)
: client_(std::move(client)) {
client_.set_disconnect_with_reason_handler(
base::BindRepeating([](JsClientInstance* self, uint32_t err,
const std::string& str) { delete self; },
base::Unretained(this)));
auto& state = JsChannelsGlobalState::Get();
state.instance_list.push_back({process_id, routing_id, this});
for (auto& o : state.observer_list)
o.OnJsClientInstanceRegistered(process_id, routing_id, this);
}
JsClientInstance::~JsClientInstance() {
auto& list = JsChannelsGlobalState::Get().instance_list;
list.erase(std::find_if(list.begin(), list.end(), [this](const auto& e) {
return e.instance == this;
}));
}
// static
void JsClientInstance::AddObserver(Observer* observer) {
JsChannelsGlobalState::Get().observer_list.AddObserver(observer);
}
// static
void JsClientInstance::RemoveObserver(Observer* observer) {
JsChannelsGlobalState::Get().observer_list.RemoveObserver(observer);
}
// static
JsClientInstance* JsClientInstance::Find(int process_id, int routing_id) {
for (auto& e : JsChannelsGlobalState::Get().instance_list) {
if (e.process_id == process_id && e.routing_id == routing_id)
return e.instance;
}
return nullptr;
}
void JsClientInstance::AddChannel(const std::string& channel,
JsChannelCallback callback) {
mojo::PendingRemote<mojom::JsChannel> channel_remote;
auto receiver = channel_remote.InitWithNewPipeAndPassReceiver();
client_->CreateChannel(channel, std::move(channel_remote));
mojo::MakeSelfOwnedReceiver(
std::make_unique<JsChannelImpl>(channel, std::move(callback)),
std::move(receiver));
}
void JsClientInstance::RemoveChannel(const std::string& channel) {
client_->RemoveChannel(channel);
}
JsChannelImpl::JsChannelImpl(const std::string& channel,
JsChannelCallback callback)
: channel_(channel), callback_(std::move(callback)) {}
JsChannelImpl::~JsChannelImpl() = default;
void JsChannelImpl::PostMessage(const std::string& message) {
callback_.Run(channel_, message);
}
} // namespace chromecast