blob: ba5a5d85e147673c2be75f9908004ede1698f2c0 [file] [log] [blame]
// Copyright 2016 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 "content/browser/broadcast_channel/broadcast_channel_provider.h"
#include "base/bind.h"
#include "base/stl_util.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace content {
// There is a one-to-one mapping of BroadcastChannel instances in the renderer
// and Connection instances in the browser. The Connection is owned by a
// BroadcastChannelProvider.
class BroadcastChannelProvider::Connection
: public blink::mojom::BroadcastChannelClient {
public:
Connection(const url::Origin& origin,
const std::string& name,
blink::mojom::BroadcastChannelClientAssociatedPtrInfo client,
blink::mojom::BroadcastChannelClientAssociatedRequest connection,
BroadcastChannelProvider* service);
void OnMessage(blink::CloneableMessage message) override;
void MessageToClient(const blink::CloneableMessage& message) const {
client_->OnMessage(message.ShallowClone());
}
const url::Origin& origin() const { return origin_; }
const std::string& name() const { return name_; }
void set_connection_error_handler(const base::Closure& error_handler) {
binding_.set_connection_error_handler(error_handler);
client_.set_connection_error_handler(error_handler);
}
private:
mojo::AssociatedBinding<blink::mojom::BroadcastChannelClient> binding_;
blink::mojom::BroadcastChannelClientAssociatedPtr client_;
BroadcastChannelProvider* service_;
url::Origin origin_;
std::string name_;
};
BroadcastChannelProvider::Connection::Connection(
const url::Origin& origin,
const std::string& name,
blink::mojom::BroadcastChannelClientAssociatedPtrInfo client,
blink::mojom::BroadcastChannelClientAssociatedRequest connection,
BroadcastChannelProvider* service)
: binding_(this, std::move(connection)),
service_(service),
origin_(origin),
name_(name) {
client_.Bind(std::move(client));
}
void BroadcastChannelProvider::Connection::OnMessage(
blink::CloneableMessage message) {
service_->ReceivedMessageOnConnection(this, message);
}
BroadcastChannelProvider::BroadcastChannelProvider() {}
mojo::BindingId BroadcastChannelProvider::Connect(
RenderProcessHostId render_process_host_id,
blink::mojom::BroadcastChannelProviderRequest request) {
return bindings_.AddBinding(this, std::move(request), render_process_host_id);
}
void BroadcastChannelProvider::ConnectToChannel(
const url::Origin& origin,
const std::string& name,
blink::mojom::BroadcastChannelClientAssociatedPtrInfo client,
blink::mojom::BroadcastChannelClientAssociatedRequest connection) {
RenderProcessHostId process_id = bindings_.dispatch_context();
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
if (!policy->CanAccessDataForOrigin(process_id, origin)) {
mojo::ReportBadMessage("BROADCAST_CHANNEL_INVALID_ORIGIN");
return;
}
std::unique_ptr<Connection> c(new Connection(origin, name, std::move(client),
std::move(connection), this));
c->set_connection_error_handler(
base::Bind(&BroadcastChannelProvider::UnregisterConnection,
base::Unretained(this), c.get()));
connections_[origin].insert(std::make_pair(name, std::move(c)));
}
BroadcastChannelProvider::~BroadcastChannelProvider() {}
void BroadcastChannelProvider::UnregisterConnection(Connection* c) {
url::Origin origin = c->origin();
auto& connections = connections_[origin];
for (auto it = connections.lower_bound(c->name()),
end = connections.upper_bound(c->name());
it != end; ++it) {
if (it->second.get() == c) {
connections.erase(it);
break;
}
}
if (connections.empty())
connections_.erase(origin);
}
void BroadcastChannelProvider::ReceivedMessageOnConnection(
Connection* c,
const blink::CloneableMessage& message) {
auto& connections = connections_[c->origin()];
for (auto it = connections.lower_bound(c->name()),
end = connections.upper_bound(c->name());
it != end; ++it) {
if (it->second.get() != c)
it->second->MessageToClient(message);
}
}
} // namespace content