blob: 85295141d0eba430392568d3f7f1872e2ed59874 [file] [log] [blame]
// Copyright (c) 2021 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/browsing_context_state.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/content_navigation_policy.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
namespace features {
const base::Feature kNewBrowsingContextStateOnBrowsingContextGroupSwap{
"NewBrowsingContextStateOnBrowsingContextGroupSwap",
base::FEATURE_DISABLED_BY_DEFAULT};
BrowsingContextStateImplementationType GetBrowsingContextMode() {
if (base::FeatureList::IsEnabled(
kNewBrowsingContextStateOnBrowsingContextGroupSwap)) {
return BrowsingContextStateImplementationType::
kSwapForCrossBrowsingInstanceNavigations;
}
return BrowsingContextStateImplementationType::
kLegacyOneToOneWithFrameTreeNode;
}
} // namespace features
namespace content {
BrowsingContextState::BrowsingContextState(
blink::mojom::FrameReplicationStatePtr replication_state,
raw_ptr<RenderFrameHostImpl> parent)
: replication_state_(std::move(replication_state)), parent_(parent) {}
BrowsingContextState::~BrowsingContextState() = default;
RenderFrameProxyHost* BrowsingContextState::GetRenderFrameProxyHost(
SiteInstanceGroup* site_instance_group) const {
auto it = proxy_hosts_.find(site_instance_group->GetId());
if (it != proxy_hosts_.end())
return it->second.get();
return nullptr;
}
size_t BrowsingContextState::GetProxyCount() {
return proxy_hosts_.size();
}
bool BrowsingContextState::UpdateFramePolicyHeaders(
network::mojom::WebSandboxFlags sandbox_flags,
const blink::ParsedPermissionsPolicy& parsed_header) {
bool changed = false;
if (replication_state_->permissions_policy_header != parsed_header) {
replication_state_->permissions_policy_header = parsed_header;
changed = true;
}
// TODO(iclelland): Kill the renderer if sandbox flags is not a subset of the
// currently effective sandbox flags from the frame. https://crbug.com/740556
network::mojom::WebSandboxFlags updated_flags =
sandbox_flags | replication_state_->frame_policy.sandbox_flags;
if (replication_state_->active_sandbox_flags != updated_flags) {
replication_state_->active_sandbox_flags = updated_flags;
changed = true;
}
// Notify any proxies if the policies have been changed.
if (changed) {
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->DidSetFramePolicyHeaders(
replication_state_->active_sandbox_flags,
replication_state_->permissions_policy_header);
}
}
return changed;
}
bool BrowsingContextState::CommitFramePolicy(
const blink::FramePolicy& new_frame_policy) {
// Documents create iframes, iframes host new documents. Both are associated
// with sandbox flags. They are required to be stricter or equal to their
// owner when they change, as we go down.
// TODO(https://crbug.com/1262061). Enforce the invariant mentioned above,
// once the interactions with fenced frame has been tested and clarified.
bool did_change_flags = new_frame_policy.sandbox_flags !=
replication_state_->frame_policy.sandbox_flags;
bool did_change_container_policy =
new_frame_policy.container_policy !=
replication_state_->frame_policy.container_policy;
bool did_change_required_document_policy =
new_frame_policy.required_document_policy !=
replication_state_->frame_policy.required_document_policy;
DCHECK_EQ(new_frame_policy.is_fenced,
replication_state_->frame_policy.is_fenced);
if (did_change_flags) {
replication_state_->frame_policy.sandbox_flags =
new_frame_policy.sandbox_flags;
}
if (did_change_container_policy) {
replication_state_->frame_policy.container_policy =
new_frame_policy.container_policy;
}
if (did_change_required_document_policy) {
replication_state_->frame_policy.required_document_policy =
new_frame_policy.required_document_policy;
}
UpdateFramePolicyHeaders(new_frame_policy.sandbox_flags,
replication_state_->permissions_policy_header);
return did_change_flags || did_change_container_policy ||
did_change_required_document_policy;
}
void BrowsingContextState::SetFrameName(const std::string& name,
const std::string& unique_name) {
if (name == replication_state_->name) {
// |unique_name| shouldn't change unless |name| changes.
DCHECK_EQ(unique_name, replication_state_->unique_name);
return;
}
if (parent_) {
// Non-main frames should have a non-empty unique name.
DCHECK(!unique_name.empty());
} else {
// Unique name of main frames should always stay empty.
DCHECK(unique_name.empty());
}
// Note the unique name should only be able to change before the first real
// load is committed, but that's not strongly enforced here.
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->SetReplicatedName(name,
unique_name);
}
replication_state_->unique_name = unique_name;
replication_state_->name = name;
}
void BrowsingContextState::SetCurrentOrigin(
const url::Origin& origin,
bool is_potentially_trustworthy_unique_origin) {
if (origin.IsSameOriginWith(replication_state_->origin) &&
replication_state_->has_potentially_trustworthy_unique_origin ==
is_potentially_trustworthy_unique_origin) {
return;
}
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->SetReplicatedOrigin(
origin, is_potentially_trustworthy_unique_origin);
}
replication_state_->origin = origin;
replication_state_->has_potentially_trustworthy_unique_origin =
is_potentially_trustworthy_unique_origin;
}
void BrowsingContextState::SetInsecureRequestPolicy(
blink::mojom::InsecureRequestPolicy policy) {
if (policy == replication_state_->insecure_request_policy)
return;
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->EnforceInsecureRequestPolicy(
policy);
}
replication_state_->insecure_request_policy = policy;
}
void BrowsingContextState::SetInsecureNavigationsSet(
const std::vector<uint32_t>& insecure_navigations_set) {
DCHECK(std::is_sorted(insecure_navigations_set.begin(),
insecure_navigations_set.end()));
if (insecure_navigations_set == replication_state_->insecure_navigations_set)
return;
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->EnforceInsecureNavigationsSet(
insecure_navigations_set);
}
replication_state_->insecure_navigations_set = insecure_navigations_set;
}
void BrowsingContextState::OnSetHadStickyUserActivationBeforeNavigation(
bool value) {
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()
->SetHadStickyUserActivationBeforeNavigation(value);
}
replication_state_->has_received_user_gesture_before_nav = value;
}
void BrowsingContextState::SetIsAdSubframe(bool is_ad_subframe) {
if (is_ad_subframe == replication_state_->is_ad_subframe)
return;
replication_state_->is_ad_subframe = is_ad_subframe;
for (const auto& pair : proxy_hosts_) {
pair.second->GetAssociatedRemoteFrame()->SetReplicatedIsAdSubframe(
is_ad_subframe);
}
}
void BrowsingContextState::ActiveFrameCountIsZero(
SiteInstanceGroup* site_instance_group) {
// |site_instance_group| no longer contains any active RenderFrameHosts, so we
// don't need to maintain a proxy there anymore.
RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(site_instance_group);
CHECK(proxy);
DeleteRenderFrameProxyHost(site_instance_group);
}
void BrowsingContextState::RenderProcessGone(
SiteInstanceGroup* site_instance_group,
const ChildProcessTerminationInfo& info) {
GetRenderFrameProxyHost(site_instance_group)
->SetRenderFrameProxyCreated(false);
}
void BrowsingContextState::DeleteRenderFrameProxyHost(
SiteInstanceGroup* site_instance_group) {
site_instance_group->RemoveObserver(this);
proxy_hosts_.erase(site_instance_group->GetId());
}
void BrowsingContextState::SendFramePolicyUpdatesToProxies(
SiteInstance* parent_site_instance,
const blink::FramePolicy& frame_policy) {
// Notify all of the frame's proxies about updated policies, excluding
// the parent process since it already knows the latest state.
for (const auto& pair : proxy_hosts_) {
if (pair.second->GetSiteInstance() != parent_site_instance) {
pair.second->GetAssociatedRemoteFrame()->DidUpdateFramePolicy(
frame_policy);
}
}
}
RenderFrameProxyHost* BrowsingContextState::CreateRenderFrameProxyHost(
SiteInstance* site_instance,
const scoped_refptr<RenderViewHostImpl>& rvh,
FrameTreeNode* frame_tree_node) {
if (features::GetBrowsingContextMode() ==
features::BrowsingContextStateImplementationType::
kLegacyOneToOneWithFrameTreeNode) {
DCHECK_EQ(this,
frame_tree_node->current_frame_host()->browsing_context_state());
}
auto site_instance_group_id =
static_cast<SiteInstanceImpl*>(site_instance)->group()->GetId();
CHECK(proxy_hosts_.find(site_instance_group_id) == proxy_hosts_.end())
<< "A proxy already existed for this SiteInstanceGroup.";
RenderFrameProxyHost* proxy_host =
new RenderFrameProxyHost(site_instance, std::move(rvh), frame_tree_node);
proxy_hosts_[site_instance_group_id] = base::WrapUnique(proxy_host);
static_cast<SiteInstanceImpl*>(site_instance)->group()->AddObserver(this);
TRACE_EVENT_INSTANT(
"navigation", "BrowsingContextState::CreateRenderFrameProxyHost",
perfetto::protos::pbzero::ChromeTrackEvent::kRenderFrameProxyHost,
*proxy_host);
return proxy_host;
}
} // namespace content