blob: f0a1b82a2e40506a5e97493cb4e22e69557a0c1d [file] [log] [blame]
// Copyright 2020 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/renderer_host/policy_container_host.h"
#include "base/callback_helpers.h"
#include "base/lazy_instance.h"
#include "content/public/browser/browser_thread.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
namespace content {
namespace {
// KeepAliveHandle is simply a class referencing a PolicyContainerHost through a
// scoped_refptr, hence maintaining it alive.
class KeepAliveHandle
: public scoped_refptr<PolicyContainerHost>,
public blink::mojom::PolicyContainerHostKeepAliveHandle {
public:
explicit KeepAliveHandle(PolicyContainerHost* policy_container_host) {
wrapped_pointer = policy_container_host;
}
private:
scoped_refptr<PolicyContainerHost> wrapped_pointer;
};
using TokenPolicyContainerMap =
std::unordered_map<blink::LocalFrameToken,
PolicyContainerHost*,
blink::LocalFrameToken::Hasher>;
base::LazyInstance<TokenPolicyContainerMap>::Leaky
g_token_policy_container_map = LAZY_INSTANCE_INITIALIZER;
} // namespace
bool operator==(const PolicyContainerPolicies& lhs,
const PolicyContainerPolicies& rhs) {
return lhs.referrer_policy == rhs.referrer_policy &&
lhs.ip_address_space == rhs.ip_address_space &&
lhs.is_web_secure_context == rhs.is_web_secure_context &&
std::equal(lhs.content_security_policies.begin(),
lhs.content_security_policies.end(),
rhs.content_security_policies.begin(),
rhs.content_security_policies.end());
}
bool operator!=(const PolicyContainerPolicies& lhs,
const PolicyContainerPolicies& rhs) {
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& out,
const PolicyContainerPolicies& policies) {
out << "{ referrer_policy: " << policies.referrer_policy
<< ", ip_address_space: " << policies.ip_address_space
<< ", is_web_secure_context: " << policies.is_web_secure_context
<< ", content_security_policies: ";
if (policies.content_security_policies.empty())
return out << "[] }";
out << "[ ";
auto it = policies.content_security_policies.begin();
for (; it + 1 != policies.content_security_policies.end(); ++it) {
out << (*it)->header->header_value << ", ";
}
return out << (*it)->header->header_value << " ] }";
}
PolicyContainerPolicies::PolicyContainerPolicies() = default;
PolicyContainerPolicies::PolicyContainerPolicies(
network::mojom::ReferrerPolicy referrer_policy,
network::mojom::IPAddressSpace ip_address_space,
bool is_web_secure_context,
std::vector<network::mojom::ContentSecurityPolicyPtr>
content_security_policies)
: referrer_policy(referrer_policy),
ip_address_space(ip_address_space),
is_web_secure_context(is_web_secure_context),
content_security_policies(std::move(content_security_policies)) {}
PolicyContainerPolicies::~PolicyContainerPolicies() = default;
std::unique_ptr<PolicyContainerPolicies> PolicyContainerPolicies::Clone()
const {
return std::make_unique<PolicyContainerPolicies>(
referrer_policy, ip_address_space, is_web_secure_context,
mojo::Clone(content_security_policies));
}
void PolicyContainerPolicies::AddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr> policies) {
content_security_policies.insert(content_security_policies.end(),
std::make_move_iterator(policies.begin()),
std::make_move_iterator(policies.end()));
}
PolicyContainerHost::PolicyContainerHost()
: PolicyContainerHost(std::make_unique<PolicyContainerPolicies>()) {}
PolicyContainerHost::PolicyContainerHost(
std::unique_ptr<PolicyContainerPolicies> policies)
: policies_(std::move(policies)) {
DCHECK(policies_);
}
PolicyContainerHost::~PolicyContainerHost() {
// The PolicyContainerHost associated with |frame_token_| might have
// changed. In that case, we must not remove the map entry.
if (frame_token_ && FromFrameToken(frame_token_.value()) == this)
g_token_policy_container_map.Get().erase(frame_token_.value());
}
void PolicyContainerHost::AssociateWithFrameToken(
const blink::LocalFrameToken& frame_token) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!frame_token_);
frame_token_ = frame_token;
g_token_policy_container_map.Get().erase(frame_token);
g_token_policy_container_map.Get().emplace(frame_token, this);
}
PolicyContainerHost* PolicyContainerHost::FromFrameToken(
const blink::LocalFrameToken& frame_token) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto it = g_token_policy_container_map.Get().find(frame_token);
if (it == g_token_policy_container_map.Get().end())
return nullptr;
return it->second;
}
void PolicyContainerHost::SetReferrerPolicy(
network::mojom::ReferrerPolicy referrer_policy) {
policies_->referrer_policy = referrer_policy;
}
void PolicyContainerHost::AddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr>
content_security_policies) {
policies_->AddContentSecurityPolicies(std::move(content_security_policies));
}
blink::mojom::PolicyContainerPtr
PolicyContainerHost::CreatePolicyContainerForBlink() {
// This function might be called several times, for example if we need to
// recreate the RenderFrame after the renderer process died. We gracefully
// handle this by resetting the receiver and creating a new one. It would be
// good to find a way to check that the previous remote has been deleted or is
// not needed anymore. Unfortunately, this cannot be done with a disconnect
// handler, since the mojo disconnect notification is not guaranteed to be
// received before we try to create a new remote.
policy_container_host_receiver_.reset();
mojo::PendingAssociatedRemote<blink::mojom::PolicyContainerHost> remote;
Bind(blink::mojom::PolicyContainerBindParams::New(
remote.InitWithNewEndpointAndPassReceiver()));
return blink::mojom::PolicyContainer::New(
blink::mojom::PolicyContainerPolicies::New(
policies_->referrer_policy, policies_->ip_address_space,
mojo::Clone(policies_->content_security_policies)),
std::move(remote));
}
scoped_refptr<PolicyContainerHost> PolicyContainerHost::Clone() const {
return base::MakeRefCounted<PolicyContainerHost>(policies_->Clone());
}
void PolicyContainerHost::Bind(
blink::mojom::PolicyContainerBindParamsPtr bind_params) {
policy_container_host_receiver_.Bind(std::move(bind_params->receiver));
// Keep the PolicyContainerHost alive, as long as its PolicyContainer (owning
// the mojo remote) in the renderer process alive.
scoped_refptr<PolicyContainerHost> copy = this;
policy_container_host_receiver_.set_disconnect_handler(base::BindOnce(
base::DoNothing::Once<scoped_refptr<PolicyContainerHost>>(),
std::move(copy)));
}
void PolicyContainerHost::IssueKeepAliveHandle(
mojo::PendingReceiver<blink::mojom::PolicyContainerHostKeepAliveHandle>
receiver) {
keep_alive_handles_receiver_set_.Add(std::make_unique<KeepAliveHandle>(this),
std::move(receiver));
}
} // namespace content