blob: 87c448c04f8f164f7b2dca6f21a8ea9cc26db163 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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/render_view_host_impl.h"
#include <algorithm>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/hash/hash.h"
#include "base/i18n/rtl.h"
#include "base/json/json_reader.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/supports_user_data.h"
#include "base/system/sys_info.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
#include "base/values.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/input/native_web_keyboard_event.h"
#include "components/input/timeout_monitor.h"
#include "components/viz/common/features.h"
#include "content/browser/bad_message.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/fenced_frame/fenced_frame.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/preloading/prerender/prerender_host.h"
#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/page_delegate.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/scoped_active_url.h"
#include "content/common/agent_scheduling_group.mojom.h"
#include "content/common/content_switches_internal.h"
#include "content/common/features.h"
#include "content/common/render_message_filter.mojom.h"
#include "content/common/renderer.mojom.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "ipc/constants.mojom.h"
#include "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "net/base/url_util.h"
#include "services/network/public/cpp/features.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/mojom/page/prerender_page_param.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/device_form_factor.h"
#include "ui/base/pointer/pointer_device.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/display/display.h"
#include "ui/display/display_switches.h"
#include "ui/display/screen.h"
#include "ui/events/blink/blink_features.h"
#include "ui/gfx/animation/animation.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/native_theme/features/native_theme_features.h"
#include "url/url_constants.h"
#if BUILDFLAG(IS_WIN)
#include "ui/display/win/screen_win.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/system_fonts_win.h"
#endif
#if !BUILDFLAG(IS_ANDROID)
#include "content/browser/host_zoom_map_impl.h"
#endif
using blink::WebInputEvent;
namespace content {
namespace {
using perfetto::protos::pbzero::ChromeTrackEvent;
// <process id, routing id>
using RenderViewHostID = std::pair<int32_t, int32_t>;
using RoutingIDViewMap =
absl::flat_hash_map<RenderViewHostID, RenderViewHostImpl*>;
RoutingIDViewMap& GetRoutingIDViewMap() {
static base::NoDestructor<RoutingIDViewMap> routing_id_view_map;
return *routing_id_view_map;
}
#if BUILDFLAG(IS_WIN)
// Fetches the name and font size of a particular Windows system font.
void GetFontInfo(gfx::win::SystemFont system_font,
std::u16string* name,
int32_t* size) {
const gfx::Font& font = gfx::win::GetSystemFont(system_font);
*name = base::UTF8ToUTF16(font.GetFontName());
*size = font.GetFontSize();
}
#endif // BUILDFLAG(IS_WIN)
// Set of RenderViewHostImpl* that can be attached as UserData to a
// RenderProcessHost. Used to keep track of whether any RenderViewHostImpl
// instances are in the bfcache.
class PerProcessRenderViewHostSet : public base::SupportsUserData::Data {
public:
static PerProcessRenderViewHostSet* GetOrCreateForProcess(
RenderProcessHost* process) {
DCHECK(process);
auto* set = static_cast<PerProcessRenderViewHostSet*>(
process->GetUserData(UserDataKey()));
if (!set) {
auto new_set = std::make_unique<PerProcessRenderViewHostSet>();
set = new_set.get();
process->SetUserData(UserDataKey(), std::move(new_set));
}
return set;
}
void Insert(const RenderViewHostImpl* rvh) {
render_view_host_instances_.insert(rvh);
}
void Erase(const RenderViewHostImpl* rvh) {
auto it = render_view_host_instances_.find(rvh);
CHECK(it != render_view_host_instances_.end());
render_view_host_instances_.erase(it);
}
bool HasNonBackForwardCachedInstances() const {
return !std::ranges::all_of(render_view_host_instances_,
&RenderViewHostImpl::is_in_back_forward_cache);
}
private:
static const void* UserDataKey() { return &kUserDataKey; }
static const int kUserDataKey = 0;
std::unordered_set<raw_ptr<const RenderViewHostImpl, CtnExperimental>>
render_view_host_instances_;
};
const int PerProcessRenderViewHostSet::kUserDataKey;
// Finds all viz::SurfaceIds within `node_range` and adds them to `out_ids`.
void CollectSurfaceIdsForEvictionForFrameTreeNodeRange(
FrameTree::NodeRange& node_range,
std::vector<viz::SurfaceId>& out_ids) {
for (FrameTreeNode* node : node_range) {
if (!node->current_frame_host()->is_local_root()) {
continue;
}
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
node->current_frame_host()->GetView());
if (!view) {
continue;
}
viz::SurfaceId id = view->GetCurrentSurfaceId();
if (id.is_valid()) {
out_ids.push_back(id);
}
view->set_is_evicted();
}
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
// RenderViewHost, public:
// static
RenderViewHost* RenderViewHost::FromID(int render_process_id,
int render_view_id) {
return RenderViewHostImpl::FromID(render_process_id, render_view_id);
}
// static
RenderViewHost* RenderViewHost::From(RenderWidgetHost* rwh) {
return RenderViewHostImpl::From(rwh);
}
///////////////////////////////////////////////////////////////////////////////
// RenderViewHostImpl, public:
// static
RenderViewHostImpl* RenderViewHostImpl::FromID(int process_id, int routing_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RoutingIDViewMap& views = GetRoutingIDViewMap();
auto it = views.find(RenderViewHostID(process_id, routing_id));
return it == views.end() ? nullptr : it->second;
}
// static
RenderViewHostImpl* RenderViewHostImpl::From(RenderWidgetHost* rwh) {
DCHECK(rwh);
RenderWidgetHostOwnerDelegate* owner_delegate =
RenderWidgetHostImpl::From(rwh)->owner_delegate();
if (!owner_delegate)
return nullptr;
RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(owner_delegate);
DCHECK_EQ(rwh, rvh->GetWidget());
return rvh;
}
// static
void RenderViewHostImpl::GetPlatformSpecificPrefs(
blink::RendererPreferences* prefs) {
#if BUILDFLAG(IS_WIN)
// Note that what is called "height" in this struct is actually the font size;
// font "height" typically includes ascender, descender, and padding and is
// often a third or so larger than the given font size.
GetFontInfo(gfx::win::SystemFont::kCaption, &prefs->caption_font_family_name,
&prefs->caption_font_height);
GetFontInfo(gfx::win::SystemFont::kSmallCaption,
&prefs->small_caption_font_family_name,
&prefs->small_caption_font_height);
GetFontInfo(gfx::win::SystemFont::kMenu, &prefs->menu_font_family_name,
&prefs->menu_font_height);
GetFontInfo(gfx::win::SystemFont::kMessage, &prefs->message_font_family_name,
&prefs->message_font_height);
GetFontInfo(gfx::win::SystemFont::kStatus, &prefs->status_font_family_name,
&prefs->status_font_height);
prefs->vertical_scroll_bar_width_in_dips =
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CXVSCROLL);
prefs->horizontal_scroll_bar_height_in_dips =
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CYHSCROLL);
prefs->arrow_bitmap_height_vertical_scroll_bar_in_dips =
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CYVSCROLL);
prefs->arrow_bitmap_width_horizontal_scroll_bar_in_dips =
display::win::GetScreenWin()->GetSystemMetricsInDIP(SM_CXHSCROLL);
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kSystemFontFamily)) {
prefs->system_font_family_name =
command_line->GetSwitchValueASCII(switches::kSystemFontFamily);
} else {
prefs->system_font_family_name = gfx::Font().GetFontName();
}
#elif BUILDFLAG(IS_FUCHSIA)
// Make Blink's "focus ring" invisible. The focus ring is a hairline border
// that's rendered around clickable targets.
// TODO(crbug.com/40124608): Consider exposing this as a FIDL parameter.
prefs->focus_ring_color = SK_AlphaTRANSPARENT;
#endif
#if BUILDFLAG(IS_OZONE)
prefs->selection_clipboard_buffer_available =
ui::Clipboard::IsSupportedClipboardBuffer(
ui::ClipboardBuffer::kSelection);
#endif
}
// static
bool RenderViewHostImpl::HasNonBackForwardCachedInstancesForProcess(
RenderProcessHost* process) {
return PerProcessRenderViewHostSet::GetOrCreateForProcess(process)
->HasNonBackForwardCachedInstances();
}
RenderViewHostImpl::RenderViewHostImpl(
FrameTree* frame_tree,
SiteInstanceGroup* group,
const StoragePartitionConfig& storage_partition_config,
std::unique_ptr<RenderWidgetHostImpl> widget,
RenderViewHostDelegate* delegate,
int32_t routing_id,
int32_t main_frame_routing_id,
bool has_initialized_audio_host,
scoped_refptr<BrowsingContextState> main_browsing_context_state,
CreateRenderViewHostCase create_case)
: render_widget_host_(std::move(widget)),
delegate_(delegate),
render_view_host_map_id_(frame_tree->GetRenderViewHostMapId(group)),
site_instance_group_(group->GetWeakPtrToAllowDangling()),
storage_partition_config_(storage_partition_config),
routing_id_(routing_id),
main_frame_routing_id_(main_frame_routing_id),
frame_tree_(frame_tree),
main_browsing_context_state_(
main_browsing_context_state
? std::make_optional(main_browsing_context_state->GetSafeRef())
: std::nullopt),
is_speculative_(create_case == CreateRenderViewHostCase::kSpeculative) {
base::ScopedUmaHistogramTimer histogram_timer(
"Navigation.RenderViewHostConstructor");
TRACE_EVENT("navigation", "RenderViewHostImpl::RenderViewHostImpl",
ChromeTrackEvent::kRenderViewHost, *this);
TRACE_EVENT_BEGIN("navigation.debug", "RenderViewHost",
perfetto::Track::FromPointer(this),
"render_view_host_when_created", this);
DCHECK(delegate_);
DCHECK_NE(GetRoutingID(), render_widget_host_->GetRoutingID());
PerProcessRenderViewHostSet::GetOrCreateForProcess(GetProcess())
->Insert(this);
std::pair<RoutingIDViewMap::iterator, bool> result =
GetRoutingIDViewMap().emplace(
RenderViewHostID(GetProcess()->GetDeprecatedID(), routing_id_), this);
CHECK(result.second) << "Inserting a duplicate item!";
GetAgentSchedulingGroup().AddRoute(routing_id_, this);
GetProcess()->AddObserver(this);
// New views may be created during RenderProcessHost::ProcessDied(), within a
// brief window where the internal ChannelProxy is null. This ensures that the
// ChannelProxy is re-initialized in such cases so that subsequent messages
// make their way to the new renderer once its restarted.
// TODO(crbug.com/40142495): Should this go via AgentSchedulingGroupHost? Is
// it even needed after the migration?
GetProcess()->EnableSendQueue();
if (!is_active())
GetWidget()->UpdatePriority();
bool initially_hidden = frame_tree_->delegate()->IsHidden();
page_lifecycle_state_manager_ = std::make_unique<PageLifecycleStateManager>(
this, initially_hidden ? blink::mojom::PageVisibilityState::kHidden
: blink::mojom::PageVisibilityState::kVisible);
GetWidget()->set_owner_delegate(this);
}
RenderViewHostImpl::~RenderViewHostImpl() {
TRACE_EVENT_INSTANT("navigation", "~RenderViewHostImpl()",
ChromeTrackEvent::kRenderViewHost, *this);
base::ScopedUmaHistogramTimer histogram_timer(
"Navigation.RenderViewHostDestructor");
PerProcessRenderViewHostSet::GetOrCreateForProcess(GetProcess())->Erase(this);
// Destroy the RenderWidgetHost.
GetWidget()->ShutdownAndDestroyWidget(false);
// Detach the routing ID as the object is going away.
GetAgentSchedulingGroup().RemoveRoute(GetRoutingID());
GetRoutingIDViewMap().erase(
RenderViewHostID(GetProcess()->GetDeprecatedID(), GetRoutingID()));
delegate_->RenderViewDeleted(this);
GetProcess()->RemoveObserver(this);
// We may have already unregistered the RenderViewHost when marking this not
// available for reuse.
if (registered_with_frame_tree_)
frame_tree_->UnregisterRenderViewHost(render_view_host_map_id_, this);
// Corresponds to the TRACE_EVENT_BEGIN in RenderViewHostImpl's constructor.
TRACE_EVENT_END("navigation.debug", perfetto::Track::FromPointer(this));
}
RenderViewHostDelegate* RenderViewHostImpl::GetDelegate() {
return delegate_;
}
base::WeakPtr<RenderViewHostImpl> RenderViewHostImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void RenderViewHostImpl::DisallowReuse() {
if (registered_with_frame_tree_) {
frame_tree_->UnregisterRenderViewHost(render_view_host_map_id_, this);
registered_with_frame_tree_ = false;
}
}
bool RenderViewHostImpl::CreateRenderView(
const std::optional<blink::FrameToken>& opener_frame_token,
int proxy_route_id,
bool window_was_opened_by_another_window,
const std::optional<base::UnguessableToken>& navigation_metrics_token) {
TRACE_EVENT0("renderer_host,navigation",
"RenderViewHostImpl::CreateRenderView");
DCHECK(!IsRenderViewLive()) << "Creating view twice";
// The process may (if we're sharing a process with another host that already
// initialized it) or may not (we have our own process or the old process
// crashed) have been initialized. Calling Init() multiple times will be
// ignored, so this is safe.
if (!GetAgentSchedulingGroup().Init())
return false;
DCHECK(GetProcess()->IsInitializedAndNotDead());
DCHECK(GetProcess()->GetBrowserContext());
// Exactly one of main_frame_routing_id_ or proxy_route_id should be set.
CHECK(!(main_frame_routing_id_ != IPC::mojom::kRoutingIdNone &&
proxy_route_id != IPC::mojom::kRoutingIdNone));
CHECK(!(main_frame_routing_id_ == IPC::mojom::kRoutingIdNone &&
proxy_route_id == IPC::mojom::kRoutingIdNone));
RenderFrameHostImpl* main_rfh = nullptr;
RenderFrameProxyHost* main_rfph = nullptr;
if (main_frame_routing_id_ != IPC::mojom::kRoutingIdNone) {
main_rfh = RenderFrameHostImpl::FromID(GetProcess()->GetDeprecatedID(),
main_frame_routing_id_);
DCHECK(main_rfh);
} else {
main_rfph = RenderFrameProxyHost::FromID(GetProcess()->GetDeprecatedID(),
proxy_route_id);
DCHECK(main_rfph);
}
FrameTreeNode* const frame_tree_node =
main_rfh ? main_rfh->frame_tree_node() : main_rfph->frame_tree_node();
mojom::CreateViewParamsPtr params = mojom::CreateViewParams::New();
params->renderer_preferences = delegate_->GetRendererPrefs(this);
params->web_preferences = delegate_->GetOrCreateWebPreferences(this);
params->color_provider_colors = delegate_->GetColorProviderColorMaps();
params->opener_frame_token = opener_frame_token;
params->replication_state =
frame_tree_node->current_replication_state().Clone();
params->devtools_main_frame_token =
frame_tree_node->current_frame_host()->devtools_frame_token();
DCHECK_EQ(&frame_tree_node->frame_tree(), frame_tree_);
params->navigation_metrics_token = navigation_metrics_token;
if (frame_tree_->is_prerendering() ||
frame_tree_->page_delegate()->IsPageInPreviewMode()) {
auto prerender_param = blink::mojom::PrerenderParam::New();
if (frame_tree_->is_prerendering()) {
auto& prerender_host = PrerenderHost::GetFromFrameTree(frame_tree_);
prerender_param->page_metric_suffix = prerender_host.GetHistogramSuffix();
prerender_param->should_warm_up_compositor =
prerender_host.should_warm_up_compositor();
prerender_param->should_prepare_paint_tree =
prerender_host.should_prepare_paint_tree();
prerender_param->should_pause_javascript_execution =
prerender_host.should_pause_javascript_execution();
} else {
prerender_param->page_metric_suffix = ".Preview";
prerender_param->should_warm_up_compositor = false;
prerender_param->should_prepare_paint_tree = false;
prerender_param->should_pause_javascript_execution = false;
}
params->prerender_param = std::move(prerender_param);
}
params->attribution_support = delegate_->GetAttributionSupport();
if (main_rfh) {
auto local_frame_params = mojom::CreateLocalMainFrameParams::New();
local_frame_params->frame_token = main_rfh->GetFrameToken();
local_frame_params->routing_id = main_frame_routing_id_;
mojo::PendingAssociatedRemote<mojom::Frame> pending_frame_remote;
local_frame_params->frame =
pending_frame_remote.InitWithNewEndpointAndPassReceiver();
main_rfh->SetMojomFrameRemote(std::move(pending_frame_remote));
main_rfh->BindBrowserInterfaceBrokerReceiver(
local_frame_params->interface_broker.InitWithNewPipeAndPassReceiver());
main_rfh->BindAssociatedInterfaceProviderReceiver(
local_frame_params->associated_interface_provider_remote
.InitWithNewEndpointAndPassReceiver());
local_frame_params->is_on_initial_empty_document =
main_rfh->frame_tree_node()->is_on_initial_empty_document();
// It is safe to ignore safety restrictions here, since it is necessary to
// retrieve the document token, even if the frame is speculative, in order
// to create the corresponding renderer-side objects.
local_frame_params->document_token =
main_rfh->GetDocumentTokenIgnoringSafetyRestrictions();
// If this is a new RenderFrameHost for a frame that has already committed a
// document, we don't have a PolicyContainerHost yet. Indeed, in that case,
// this RenderFrameHost will not display any document until it commits a
// navigation. The policy container for the navigated document will be sent
// to Blink at CommitNavigation time and then stored in this RenderFrameHost
// in DidCommitNewDocument.
if (main_rfh->policy_container_host()) {
local_frame_params->policy_container =
main_rfh->policy_container_host()->CreatePolicyContainerForBlink();
}
local_frame_params->widget_params =
main_rfh->GetRenderWidgetHost()
->BindAndGenerateCreateFrameWidgetParams();
local_frame_params->subresource_loader_factories =
main_rfh->CreateSubresourceLoaderFactoriesForInitialEmptyDocument();
// If the speculative RenderViewHost and the current RenderFrameHost have
// the same SiteInstanceGroup, this is a local -> local main frame swap for
// RenderDocument.
if (is_speculative_ &&
frame_tree_node->current_frame_host()->IsRenderFrameLive() &&
frame_tree_node->current_frame_host()->GetSiteInstance()->group() ==
site_instance_group_.get()) {
local_frame_params->widget_params->reuse_compositor =
frame_tree_node->current_frame_host()->ShouldReuseCompositing(
*main_rfh->GetSiteInstance());
if (local_frame_params->widget_params->reuse_compositor) {
main_rfh->NotifyWillCreateRenderWidgetOnCommit();
}
params->main_frame =
mojom::CreateMainFrameUnion::NewProvisionalLocalParams(
mojom::CreateProvisionalLocalMainFrameParams::New(
std::move(local_frame_params),
frame_tree_node->current_frame_host()->GetFrameToken()));
} else if (frame_tree_->is_prerendering() &&
(!base::FeatureList::IsEnabled(
features::kPrerenderMoreCorrectSpeculativeRFHCreation) ||
main_rfh->lifecycle_state() ==
RenderFrameHostImpl::LifecycleStateImpl::kSpeculative)) {
// During prerender, the browser may need to create new speculative local
// main frames. Normally, creating a speculative local main frame is a
// two step process: the browser first creates a RenderViewHost with a
// main RenderFrameProxyHost and then creates the speculative main
// RenderFrameHost.
//
// Prerender skips the RenderFrameProxyHost creation step, but the new
// RenderViewHost must still start with a provisional RenderFrame in the
// renderer. Otherwise, discarding a speculative RFH during prerender
// navigation causes the browser and the renderer to go out of sync. See
// https://crbug.com/40076091 for more background and details.
params->main_frame =
mojom::CreateMainFrameUnion::NewProvisionalLocalParams(
mojom::CreateProvisionalLocalMainFrameParams::New(
std::move(local_frame_params), std::nullopt));
} else {
params->main_frame = mojom::CreateMainFrameUnion::NewLocalParams(
std::move(local_frame_params));
}
} else {
params->main_frame = mojom::CreateMainFrameUnion::NewRemoteParams(
mojom::CreateRemoteMainFrameParams::New(
main_rfph->GetFrameToken(),
main_rfph->CreateAndBindRemoteFrameInterfaces(),
main_rfph->CreateAndBindRemoteMainFrameInterfaces()));
}
params->session_storage_namespace_id =
frame_tree_->controller()
.GetSessionStorageNamespace(storage_partition_config_)
->id();
params->hidden = frame_tree_->delegate()->IsHidden();
params->never_composited = delegate_->IsNeverComposited();
params->window_was_opened_by_another_window =
window_was_opened_by_another_window;
params->base_background_color = delegate_->GetBaseBackgroundColor();
if (auto* parent_rfh = frame_tree_node->GetParentOrOuterDocument()) {
url::Origin outermost_origin =
parent_rfh->GetOutermostMainFrame()->GetLastCommittedOrigin();
if (GetContentClient()->browser()->ShouldSendOutermostOriginToRenderer(
outermost_origin)) {
params->outermost_origin = outermost_origin;
}
}
params->type = ViewWidgetType();
if (params->type == mojom::ViewWidgetType::kFencedFrame) {
params->fenced_frame_mode =
frame_tree_->root()->GetDeprecatedFencedFrameMode();
}
// Send the current page's browsing context group to the renderer. It is
// guaranteed to be consistent for the entire FrameTree, main frame and
// subframes. For this reason we simply use the main frame's browsing context
// group. Note that we cannot use this RenderViewHost's site_instance_group(),
// which may not match in a popup case. For example, if A opens a
// cross-browsing-context-group popup to B, the RenderViewHost for the opener
// in B's process should have A's Browsing Context Group Token, which is the
// current page in the opener.
params->browsing_context_group_token =
frame_tree_->GetMainFrame()->GetSiteInstance()->browsing_instance_token();
// RenderViewHostImpl is reused after a crash, so reset any endpoint that
// might be a leftover from a crash.
page_broadcast_.reset();
params->blink_page_broadcast =
page_broadcast_.BindNewEndpointAndPassReceiver();
// We must send access information relative to the popin opener in order for
// the renderer to properly conduct checks.
// See https://explainers-by-googlers.github.io/partitioned-popins/
if (frame_tree_->GetMainFrame()->ShouldPartitionAsPopin()) {
params->partitioned_popin_params =
frame_tree_->GetMainFrame()
->delegate()
->GetPartitionedPopinOpenerProperties()
.AsMojom();
}
if (base::FeatureList::IsEnabled(features::kSetHistoryInfoOnViewCreation)) {
params->history_index =
frame_tree()->controller().GetLastCommittedEntryIndex();
params->history_length = frame_tree()->controller().GetEntryCount();
}
// The renderer process's `blink::WebView` is owned by this lifecycle of
// the `page_broadcast_` channel.
GetAgentSchedulingGroup().CreateView(std::move(params));
// Set the bit saying we've made the `blink::WebView` in the renderer and
// notify content public observers.
RenderViewCreated(main_rfh);
// This must be posted after the RenderViewHost is marked live, with
// `renderer_view_created_`.
PostRenderViewReady();
return true;
}
void RenderViewHostImpl::SetMainFrameRoutingId(int routing_id) {
main_frame_routing_id_ = routing_id;
render_widget_host_->ClearVisualProperties();
// RenderWidgetHostImpl changes its contribution to the process priority based
// on whether the main frame is active. UpdatePriority() to reflect the
// change.
GetWidget()->UpdatePriority();
// TODO(crbug.com/40387047): If a local main frame is no longer attached to
// this `blink::WebView` then the RenderWidgetHostImpl owned by this class
// should be informed that its renderer widget is no longer created. The
// RenderViewHost will need to track its own live-ness then.
}
void RenderViewHostImpl::SetFrameTree(FrameTree& frame_tree) {
TRACE_EVENT("navigation", "RenderViewHostImpl::SetFrameTree",
ChromeTrackEvent::kRenderViewHost, *this);
DCHECK(registered_with_frame_tree_);
frame_tree_->UnregisterRenderViewHost(render_view_host_map_id_, this);
frame_tree_ = &frame_tree;
frame_tree_->RegisterRenderViewHost(render_view_host_map_id_, this);
render_widget_host_->SetFrameTree(frame_tree);
}
void RenderViewHostImpl::EnterBackForwardCache() {
if (!will_enter_back_forward_cache_callback_for_testing_.is_null())
will_enter_back_forward_cache_callback_for_testing_.Run();
TRACE_EVENT("navigation", "RenderViewHostImpl::EnterBackForwardCache",
ChromeTrackEvent::kRenderViewHost, *this);
DCHECK(registered_with_frame_tree_);
// Only unregister the RenderViewHost if the FrameTree is the primary
// FrameTree, inner FrameTrees hold their state when they enter back/forward
// cache.
if (frame_tree_->is_primary()) {
frame_tree_->UnregisterRenderViewHost(render_view_host_map_id_, this);
registered_with_frame_tree_ = false;
}
is_in_back_forward_cache_ = true;
page_lifecycle_state_manager_->SetIsInBackForwardCache(
is_in_back_forward_cache_, /*page_restore_params=*/nullptr);
}
void RenderViewHostImpl::PrepareToLeaveBackForwardCache(
base::OnceClosure done_cb) {
// We wrap `done_cb` in a default invoke because if this RenderViewHostImpl
// disappears we still need to call `done_cb` otherwise the navigation
// will be blocked indefinitely.
page_lifecycle_state_manager_->SetIsLeavingBackForwardCache(
mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(done_cb)));
}
void RenderViewHostImpl::LeaveBackForwardCache(
blink::mojom::PageRestoreParamsPtr page_restore_params) {
TRACE_EVENT("navigation", "RenderViewHostImpl::LeaveBackForwardCache",
ChromeTrackEvent::kRenderViewHost, *this);
// At this point, the frames |this| RenderViewHostImpl belongs to are
// guaranteed to be committed, so it should be reused going forward.
// `registered_with_frame_tree_` will already be true for inner frame
// trees.
if (!registered_with_frame_tree_) {
registered_with_frame_tree_ = true;
frame_tree_->RegisterRenderViewHost(render_view_host_map_id_, this);
}
is_in_back_forward_cache_ = false;
page_lifecycle_state_manager_->SetIsInBackForwardCache(
is_in_back_forward_cache_, std::move(page_restore_params));
}
void RenderViewHostImpl::ActivatePrerenderedPage(
blink::mojom::PrerenderPageActivationParamsPtr
prerender_page_activation_params,
base::OnceClosure callback) {
// TODO(crbug.com/40185437): Consider using a ScopedClosureRunner here
// in case the renderer crashes before it can send us the callback. But we
// can't do that until the linked bug is fixed, or else we can reach
// DidActivateForPrerendering() outside of a Mojo message dispatch which
// breaks the DCHECK for releasing Mojo Capability Control.
page_broadcast_->ActivatePrerenderedPage(
std::move(prerender_page_activation_params), std::move(callback));
}
void RenderViewHostImpl::SetFrameTreeVisibility(
blink::mojom::PageVisibilityState visibility) {
page_lifecycle_state_manager_->SetFrameTreeVisibility(visibility);
}
void RenderViewHostImpl::SetIsFrozen(bool frozen) {
page_lifecycle_state_manager_->SetIsFrozen(frozen);
}
void RenderViewHostImpl::OnBackForwardCacheTimeout() {
auto entries = frame_tree_->controller()
.GetBackForwardCache()
.GetEntriesForRenderViewHostImpl(this);
for (auto* entry : entries) {
entry->render_frame_host()->EvictFromBackForwardCacheWithReason(
BackForwardCacheMetrics::NotRestoredReason::kTimeoutPuttingInCache);
}
}
void RenderViewHostImpl::MaybeEvictFromBackForwardCache() {
auto entries = frame_tree_->controller()
.GetBackForwardCache()
.GetEntriesForRenderViewHostImpl(this);
for (auto* entry : entries) {
entry->render_frame_host()->MaybeEvictFromBackForwardCache();
}
}
void RenderViewHostImpl::EnforceBackForwardCacheSizeLimit() {
frame_tree_->controller().GetBackForwardCache().EnforceCacheSizeLimit();
}
bool RenderViewHostImpl::DidReceiveBackForwardCacheAck() {
return GetPageLifecycleStateManager()->DidReceiveBackForwardCacheAck();
}
bool RenderViewHostImpl::IsRenderViewLive() const {
return GetProcess()->IsInitializedAndNotDead() && renderer_view_created_;
}
void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) {
GetWidget()->GetAssociatedFrameWidget()->SetBackgroundOpaque(opaque);
}
bool RenderViewHostImpl::IsMainFrameActive() {
return is_active();
}
blink::web_pref::WebPreferences
RenderViewHostImpl::GetWebkitPreferencesForWidget() {
if (!delegate_)
return blink::web_pref::WebPreferences();
return delegate_->GetOrCreateWebPreferences(this);
}
void RenderViewHostImpl::RenderViewCreated(
RenderFrameHostImpl* local_main_frame) {
renderer_view_created_ = true;
if (local_main_frame) {
// If there is a main frame in this RenderViewHost, then the renderer-side
// main frame will be created along with the `blink::WebView`. The
// RenderFrameHost initializes its RenderWidgetHost as well, if it exists.
local_main_frame->RenderFrameCreated();
}
}
RenderFrameHostImpl* RenderViewHostImpl::GetMainRenderFrameHost() {
// Only active RenderViewHosts have a main frame RenderFrameHostImpl.
// Inactive RenderViewHosts would have a main frame RenderFrameProxyHost
// instead.
if (!is_active()) {
return nullptr;
}
return RenderFrameHostImpl::FromID(GetProcess()->GetDeprecatedID(),
main_frame_routing_id_);
}
void RenderViewHostImpl::ZoomToFindInPageRect(const gfx::Rect& rect_to_zoom) {
GetMainRenderFrameHost()->GetAssociatedLocalMainFrame()->ZoomToFindInPageRect(
rect_to_zoom);
}
void RenderViewHostImpl::RenderProcessExited(
RenderProcessHost* host,
const ChildProcessTerminationInfo& info) {
renderer_view_created_ = false;
GetWidget()->RendererExited();
delegate_->RenderViewTerminated(this, info.status, info.exit_code);
// |this| might have been deleted. Do not add code here.
}
RenderWidgetHostImpl* RenderViewHostImpl::GetWidget() const {
return render_widget_host_.get();
}
AgentSchedulingGroupHost& RenderViewHostImpl::GetAgentSchedulingGroup() const {
return render_widget_host_->agent_scheduling_group();
}
RenderProcessHost* RenderViewHostImpl::GetProcess() const {
return GetAgentSchedulingGroup().GetProcess();
}
int RenderViewHostImpl::GetRoutingID() const {
return routing_id_;
}
void RenderViewHostImpl::RenderWidgetGotFocus() {
RenderViewHostDelegateView* view = delegate_->GetDelegateView();
if (view)
view->GotFocus(GetWidget());
}
void RenderViewHostImpl::RenderWidgetLostFocus() {
RenderViewHostDelegateView* view = delegate_->GetDelegateView();
if (view)
view->LostFocus(GetWidget());
}
void RenderViewHostImpl::SetInitialFocus(bool reverse) {
GetMainRenderFrameHost()->GetAssociatedLocalMainFrame()->SetInitialFocus(
reverse);
}
void RenderViewHostImpl::AnimateDoubleTapZoom(const gfx::Point& point,
const gfx::Rect& rect) {
GetMainRenderFrameHost()->GetAssociatedLocalMainFrame()->AnimateDoubleTapZoom(
point, rect);
}
///////////////////////////////////////////////////////////////////////////////
// RenderViewHostImpl, IPC message handlers:
std::string RenderViewHostImpl::ToDebugString() {
return "RVHI:" + delegate_->GetCreatorLocation().ToString();
}
void RenderViewHostImpl::OnTakeFocus(bool reverse) {
RenderViewHostDelegateView* view = delegate_->GetDelegateView();
if (view)
view->TakeFocus(reverse);
}
void RenderViewHostImpl::OnFocus() {
// Note: We allow focus and blur from swapped out RenderViewHosts, even when
// the active RenderViewHost is in a different BrowsingInstance (e.g., WebUI).
delegate_->Activate();
}
void RenderViewHostImpl::BindPageBroadcast(
mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> page_broadcast) {
page_broadcast_.reset();
page_broadcast_.Bind(std::move(page_broadcast));
}
const mojo::AssociatedRemote<blink::mojom::PageBroadcast>&
RenderViewHostImpl::GetAssociatedPageBroadcast() {
return page_broadcast_;
}
void RenderViewHostImpl::RenderWidgetDidForwardMouseEvent(
const blink::WebMouseEvent& mouse_event) {
if (mouse_event.GetType() == WebInputEvent::Type::kMouseWheel &&
GetWidget()->IsIgnoringWebInputEvents(mouse_event)) {
delegate_->OnIgnoredUIEvent();
}
}
bool RenderViewHostImpl::MayRenderWidgetForwardKeyboardEvent(
const input::NativeWebKeyboardEvent& key_event) {
if (GetWidget()->IsIgnoringWebInputEvents(key_event)) {
if (key_event.GetType() == WebInputEvent::Type::kRawKeyDown ||
key_event.GetType() == WebInputEvent::Type::kKeyDown) {
delegate_->OnIgnoredUIEvent();
}
return false;
}
return true;
}
bool RenderViewHostImpl::ShouldContributePriorityToProcess() {
return is_active();
}
void RenderViewHostImpl::SendWebPreferencesToRenderer() {
if (auto& broadcast = GetAssociatedPageBroadcast()) {
if (!will_send_web_preferences_callback_for_testing_.is_null()) {
will_send_web_preferences_callback_for_testing_.Run();
}
broadcast->UpdateWebPreferences(delegate_->GetOrCreateWebPreferences(this));
}
}
void RenderViewHostImpl::SendRendererPreferencesToRenderer(
const blink::RendererPreferences& preferences) {
if (auto& broadcast = GetAssociatedPageBroadcast()) {
if (!will_send_renderer_preferences_callback_for_testing_.is_null())
will_send_renderer_preferences_callback_for_testing_.Run(preferences);
broadcast->UpdateRendererPreferences(preferences);
}
}
void RenderViewHostImpl::EnablePreferredSizeMode() {
if (is_active()) {
GetMainRenderFrameHost()
->GetAssociatedLocalMainFrame()
->EnablePreferredSizeChangedMode();
}
}
void RenderViewHostImpl::PostRenderViewReady() {
GetProcess()->PostTaskWhenProcessIsReady(base::BindOnce(
&RenderViewHostImpl::RenderViewReady, weak_factory_.GetWeakPtr()));
}
void RenderViewHostImpl::RenderViewReady() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_->RenderViewReady(this);
}
std::vector<viz::SurfaceId> RenderViewHostImpl::CollectSurfaceIdsForEviction() {
std::vector<viz::SurfaceId> ids;
if (is_active()) {
RenderFrameHostImpl* rfh = GetMainRenderFrameHost();
if (!rfh || !rfh->IsActive()) {
return {};
}
FrameTreeNode* root = rfh->frame_tree_node();
FrameTree& tree = root->frame_tree();
// Inner tree nodes are used for several purposes, e.g. fenced frames,
// <webview>, and PDF. These may have a compositor surface as well,
// in which case we need to explore not the outer node only, but the inner
// ones as well.
FrameTree::NodeRange node_range = tree.NodesIncludingInnerTreeNodes();
CollectSurfaceIdsForEvictionForFrameTreeNodeRange(node_range, ids);
} else if (is_in_back_forward_cache_) {
// `FrameTree::SubtreeAndInnerTreeNodes` starts with the children of `rfh`
// so we need to add our current viz::SurfaceId to ensure it is evicted.
if (render_widget_host_) {
auto* view = render_widget_host_->GetView();
if (view) {
if (view->GetCurrentSurfaceId().is_valid()) {
ids.push_back(view->GetCurrentSurfaceId());
view->set_is_evicted();
}
}
}
auto entries = frame_tree_->controller()
.GetBackForwardCache()
.GetEntriesForRenderViewHostImpl(this);
for (auto* entry : entries) {
auto* rfh = entry->render_frame_host();
if (!rfh) {
continue;
}
// While `is_in_back_forward_cache_` there is no `main_frame_routing_id_`
// so there is no `GetMainRenderFrameHost`. Furthermore the root of the
// `FrameTree` is now associated to the foreground
// `RenderWidgetHostView*`. Due to this `NodesIncludingInnerTreeNodes`
// does not find the children nodes associated with the BFCache entry.
//
// Instead we build a `FrameTree::NodeRange` that starts with the children
// of `rfh`. This will also be equivalent to
// `should_descend_into_inner_trees=true`. Thus finding all the compositor
// surfaces in the BFCache.
FrameTree::NodeRange node_range = FrameTree::SubtreeAndInnerTreeNodes(
rfh,
/*include_delegate_nodes_for_inner_frame_trees=*/true);
CollectSurfaceIdsForEvictionForFrameTreeNodeRange(node_range, ids);
}
}
return ids;
}
bool RenderViewHostImpl::IsTestRenderViewHost() const {
return false;
}
void RenderViewHostImpl::SetWillEnterBackForwardCacheCallbackForTesting(
const WillEnterBackForwardCacheCallbackForTesting& callback) {
will_enter_back_forward_cache_callback_for_testing_ = callback;
}
void RenderViewHostImpl::SetWillSendRendererPreferencesCallbackForTesting(
const WillSendRendererPreferencesCallbackForTesting& callback) {
will_send_renderer_preferences_callback_for_testing_ = callback;
}
void RenderViewHostImpl::SetWillSendWebPreferencesCallbackForTesting(
const WillSendWebPreferencesCallbackForTesting& callback) {
will_send_web_preferences_callback_for_testing_ = callback;
}
void RenderViewHostImpl::WriteIntoTrace(
perfetto::TracedProto<TraceProto> proto) const {
proto->set_rvh_map_id(render_view_host_map_id_.value());
proto->set_routing_id(GetRoutingID());
proto.Set(TraceProto::kProcess, GetProcess());
proto->set_is_in_back_forward_cache(is_in_back_forward_cache_);
proto->set_renderer_view_created(renderer_view_created_);
}
base::SafeRef<RenderViewHostImpl> RenderViewHostImpl::GetSafeRef() {
return weak_factory_.GetSafeRef();
}
mojom::ViewWidgetType RenderViewHostImpl::ViewWidgetType() {
if (view_widget_type_) {
return *view_widget_type_;
}
bool is_guest_view = frame_tree_->is_guest() || delegate_->IsGuest();
bool is_fenced_frame = frame_tree_->is_fenced_frame();
if (is_fenced_frame) {
view_widget_type_ = mojom::ViewWidgetType::kFencedFrame;
} else if (is_guest_view) {
view_widget_type_ = mojom::ViewWidgetType::kGuestView;
} else {
view_widget_type_ = mojom::ViewWidgetType::kTopLevel;
}
return *view_widget_type_;
}
} // namespace content