// 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 <optional>
#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/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/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/fenced_frame/fenced_frame.h"
#include "content/browser/global_privacy_control_util.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/perfetto/include/perfetto/tracing/track.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/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) {
    CHECK(process, base::NotFatalUntil::M152);
    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) {
  CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M152);
  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) {
  CHECK(rwh, base::NotFatalUntil::M152);
  RenderWidgetHostOwnerDelegate* owner_delegate =
      RenderWidgetHostImpl::From(rwh)->owner_delegate();
  if (!owner_delegate)
    return nullptr;
  RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(owner_delegate);
  CHECK_EQ(rwh, rvh->GetWidget(), base::NotFatalUntil::M152);
  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);

#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
  prefs->is_global_privacy_control_setting_enabled =
      IsGlobalPrivacyControlSettingEnabled();
}

// 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::NamedTrack::FromPointer("RenderViewHost", this),
                    "render_view_host_when_created", this);

  CHECK(delegate_, base::NotFatalUntil::M152);
  CHECK_NE(GetRoutingID(), render_widget_host_->GetRoutingID(),
           base::NotFatalUntil::M152);

  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::NamedTrack::FromPointer("RenderViewHost", 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;
  CHECK(GetProcess()->IsInitializedAndNotDead(), base::NotFatalUntil::M152);
  CHECK(GetProcess()->GetBrowserContext(), base::NotFatalUntil::M152);

  // 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_);
    CHECK(main_rfh, base::NotFatalUntil::M152);
  } else {
    main_rfph = RenderFrameProxyHost::FromID(GetProcess()->GetDeprecatedID(),
                                             proxy_route_id);
    CHECK(main_rfph, base::NotFatalUntil::M152);
  }
  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();
  CHECK_EQ(&frame_tree_node->frame_tree(), frame_tree_,
           base::NotFatalUntil::M152);
  params->navigation_metrics_token = navigation_metrics_token;

  if (frame_tree_->is_prerendering()) {
    auto prerender_param = blink::mojom::PrerenderParam::New();
    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();
    params->prerender_param = std::move(prerender_param);
  }

  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();
    }

    // Populate the sandbox origin token if available.
    if (auto token = main_rfh->GetPage().TakeSandboxOriginTokenForPopup()) {
      local_frame_params->sandbox_origin_token = *token;
    }

    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()) {

      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();

  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);
  CHECK(registered_with_frame_tree_, base::NotFatalUntil::M152);
  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);
  CHECK(registered_with_frame_tree_, base::NotFatalUntil::M152);
  // 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() {
  if (!frame_tree_->is_primary()) {
    return;
  }
  auto entries = frame_tree_->controller()
                     .GetBackForwardCache()
                     .GetEntriesForRenderViewHostImpl(this);
  for (auto* entry : entries) {
    entry->render_frame_host()->EvictFromBackForwardCacheWithReason(
        BackForwardCacheMetrics::NotRestoredReason::kTimeoutPuttingInCache);
  }
}

void RenderViewHostImpl::MaybeEvictFromBackForwardCache() {
  if (!frame_tree_->is_primary()) {
    return;
  }
  auto entries = frame_tree_->controller()
                     .GetBackForwardCache()
                     .GetEntriesForRenderViewHostImpl(this);
  for (auto* entry : entries) {
    entry->render_frame_host()->MaybeEvictFromBackForwardCache();
  }
}

void RenderViewHostImpl::EnforceBackForwardCacheSizeLimit() {
  if (!frame_tree_->is_primary()) {
    return;
  }
  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) {
  if (is_active()) {
    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;
}

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() {
  CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M152);
  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();
        }
      }
    }

    if (frame_tree_->is_primary()) {
      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
