// Copyright 2014 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_frame_proxy_host.h"

#include <memory>
#include <tuple>
#include <utility>
#include <vector>

#include "base/containers/circular_deque.h"
#include "base/feature_list.h"
#include "base/functional/callback.h"
#include "base/hash/hash.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/typed_macros.h"
#include "base/types/optional_util.h"
#include "content/browser/bad_message.h"
#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include "content/browser/renderer_host/batched_proxy_ipc_sender.h"
#include "content/browser/renderer_host/cross_process_frame_connector.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/initiator_navigation_state_impl.h"
#include "content/browser/renderer_host/ipc_utils.h"
#include "content/browser/renderer_host/navigation_metrics_utils.h"
#include "content/browser/renderer_host/navigator.h"
#include "content/browser/renderer_host/policy_container_host.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
#include "content/browser/security/cpsp/child_process_security_policy_impl.h"
#include "content/browser/site_instance_group.h"
#include "content/browser/site_instance_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/referrer_type_converters.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
#include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "ui/gfx/geometry/rect_f.h"

namespace content {

namespace {

RenderFrameProxyHost::TestObserver* g_observer_for_testing = nullptr;

// The (process id, routing id) pair that identifies one RenderFrameProxy.
typedef std::pair<int32_t, int32_t> RenderFrameProxyHostID;
using RoutingIDFrameProxyMap =
    absl::flat_hash_map<RenderFrameProxyHostID, RenderFrameProxyHost*>;
base::LazyInstance<RoutingIDFrameProxyMap>::DestructorAtExit
    g_routing_id_frame_proxy_map = LAZY_INSTANCE_INITIALIZER;

using TokenFrameMap =
    absl::flat_hash_map<blink::RemoteFrameToken, RenderFrameProxyHost*>;
TokenFrameMap& GetTokenFrameProxyMap() {
  static base::NoDestructor<TokenFrameMap> token_frame_proxy_map;
  return *token_frame_proxy_map;
}

// A kill switch for the enforcement of cross-BrowsingInstance checks in
// RenderFrameProxyHost IPC handlers. See https://crbug.com/495933780.
BASE_FEATURE(kEnforceCrossBrowsingInstanceChecks,
             base::FEATURE_ENABLED_BY_DEFAULT);

}  // namespace

// static
void RenderFrameProxyHost::SetObserverForTesting(TestObserver* observer) {
  // Prevent clobbering by previously set TestObserver.
  CHECK(!observer || (observer && !g_observer_for_testing),
        base::NotFatalUntil::M153);
  g_observer_for_testing = observer;
}

// static
RenderFrameProxyHost* RenderFrameProxyHost::FromID(int process_id,
                                                   int routing_id) {
  CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M152);
  RoutingIDFrameProxyMap* frames = g_routing_id_frame_proxy_map.Pointer();
  auto it = frames->find(RenderFrameProxyHostID(process_id, routing_id));
  return it == frames->end() ? nullptr : it->second;
}

// static
RenderFrameProxyHost* RenderFrameProxyHost::FromFrameToken(
    int process_id,
    const blink::RemoteFrameToken& frame_token) {
  CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M152);
  TokenFrameMap* frames = &GetTokenFrameProxyMap();
  auto it = frames->find(frame_token);
  // The check against |process_id| isn't strictly necessary, but represents
  // an extra level of protection against a renderer trying to force a frame
  // token.
  return it != frames->end() &&
                 it->second->GetProcess()->GetDeprecatedID() == process_id
             ? it->second
             : nullptr;
}

// static
bool RenderFrameProxyHost::IsFrameTokenInUse(
    const blink::RemoteFrameToken& frame_token) {
  CHECK_CURRENTLY_ON(BrowserThread::UI, base::NotFatalUntil::M152);
  TokenFrameMap* frames = &GetTokenFrameProxyMap();
  return frames->find(frame_token) != frames->end();
}

RenderFrameProxyHost::RenderFrameProxyHost(
    SiteInstanceGroup* site_instance_group,
    scoped_refptr<RenderViewHostImpl> render_view_host,
    FrameTreeNode* frame_tree_node,
    const blink::RemoteFrameToken& frame_token)
    : routing_id_(site_instance_group->process()->GetNextRoutingID()),
      site_instance_group_(site_instance_group),
      process_(site_instance_group->process()),
      frame_tree_node_(frame_tree_node),
      render_frame_proxy_created_(false),
      render_view_host_(std::move(render_view_host)),
      frame_token_(frame_token) {
  TRACE_EVENT_BEGIN(
      "navigation.debug", "RenderFrameProxyHost",
      perfetto::NamedTrack::FromPointer("RenderFrameProxyHost", this),
      "render_frame_proxy_host_when_created", *this);
  GetAgentSchedulingGroup().AddRoute(routing_id_, this);
  CHECK(g_routing_id_frame_proxy_map.Get()
            .insert(std::make_pair(
                RenderFrameProxyHostID(GetProcess()->GetDeprecatedID(),
                                       routing_id_),
                this))
            .second);
  CHECK(GetTokenFrameProxyMap()
            .insert(std::make_pair(frame_token_, this))
            .second);
  CHECK(render_view_host_ ||
        frame_tree_node_->render_manager()->IsMainFrameForInnerDelegate());

  bool is_proxy_to_parent =
      !frame_tree_node_->IsMainFrame() &&
      frame_tree_node_->parent()->GetSiteInstance()->group() ==
          site_instance_group;
  bool is_proxy_to_outer_delegate =
      frame_tree_node_->render_manager()->IsMainFrameForInnerDelegate();

  // If this is a proxy to parent frame or this proxy is for the inner
  // WebContents's FrameTreeNode in outer WebContents's SiteInstance, then we
  // need a CrossProcessFrameConnector.
  if (is_proxy_to_parent || is_proxy_to_outer_delegate) {
    // The RenderFrameHost navigating cross-process is destroyed and a proxy for
    // it is created in the parent's process. CrossProcessFrameConnector
    // initialization only needs to happen on an initial cross-process
    // navigation, when the RenderFrameHost leaves the same process as its
    // parent. The same CrossProcessFrameConnector is used for subsequent cross-
    // process navigations, but it will be destroyed if the frame is
    // navigated back to the same SiteInstance as its parent.
    cross_process_frame_connector_ =
        std::make_unique<CrossProcessFrameConnector>(this);
  }

  if (g_observer_for_testing)
    g_observer_for_testing->OnCreated(this);
}

RenderFrameProxyHost::~RenderFrameProxyHost() {
  if (g_observer_for_testing)
    g_observer_for_testing->OnDeleted(this);

  if (GetProcess()->IsInitializedAndNotDead()) {
    // TODO(nasko): For now, don't send this IPC for top-level frames, as
    // the top-level RenderFrame will delete the RenderFrameProxy.
    // This can be removed once we don't have a swapped out state on
    // RenderFrame. See https://crbug.com/357747
    if (!frame_tree_node_->IsMainFrame() && is_render_frame_proxy_live())
      GetAssociatedRemoteFrame()->DetachAndDispose();
  }

  // TODO(arthursonzogni): There are no known reason for removing the
  // RenderViewHostImpl here instead of automatically at the end of the
  // destructor. This line can be removed.
  render_view_host_.reset();

  GetAgentSchedulingGroup().RemoveRoute(routing_id_);
  g_routing_id_frame_proxy_map.Get().erase(
      RenderFrameProxyHostID(GetProcess()->GetDeprecatedID(), routing_id_));
  GetTokenFrameProxyMap().erase(frame_token_);
  TRACE_EVENT_END("navigation.debug", perfetto::NamedTrack::FromPointer(
                                          "RenderFrameProxyHost", this));
}

void RenderFrameProxyHost::SetChildRWHView(RenderWidgetHostViewChildFrame* view,
                                           const gfx::Size* initial_frame_size,
                                           bool allow_paint_holding) {
  cross_process_frame_connector_->SetView(view, allow_paint_holding);
  if (initial_frame_size)
    cross_process_frame_connector_->SetLocalFrameSize(*initial_frame_size);
}

RenderViewHostImpl* RenderFrameProxyHost::GetRenderViewHost() {
  return render_view_host_.get();
}

std::string RenderFrameProxyHost::ToDebugString() {
  return "RFPH:" + frame_tree_node_->current_frame_host()->ToDebugString();
}

bool RenderFrameProxyHost::InitRenderFrameProxy(
    const std::optional<base::UnguessableToken>& navigation_metrics_token,
    BatchedProxyIPCSender* batched_proxy_ipc_sender) {
  // TODO(b/545449767): CHECK-exclusion: Convert to a CHECK once we are
  // confident it won't be triggered.
  DCHECK(!render_frame_proxy_created_);
  // We shouldn't be creating proxies for subframes of frames in
  // BackForwardCache.
  // TODO(crbug.com/547219514): CHECK-exclusion: Convert to a CHECK once we are
  // confident it won't be triggered.
  DCHECK(!frame_tree_node_->current_frame_host()->IsInBackForwardCache());

  // If the current RenderFrameHost is pending deletion, no new proxies should
  // be created for it, since this frame should no longer be visible from other
  // processes. We can get here with postMessage while trying to recreate
  // proxies for the sender.
  if (frame_tree_node_->current_frame_host()->IsPendingDeletion())
    return false;

  // It is possible to reach this when the process is dead (in particular, when
  // creating proxies from CreateProxiesForChildFrame).  In that case, don't
  // create the proxy.  The process shouldn't be resurrected just to create
  // RenderFrameProxies; it should be restored only if it needs to host a
  // RenderFrame.  When that happens, the process will be reinitialized, and
  // all necessary proxies, including any of the ones we skipped here, will be
  // created by CreateProxiesForSiteInstance. See https://crbug.com/476846
  if (!GetProcess()->IsInitializedAndNotDead())
    return false;

  std::optional<blink::FrameToken> opener_frame_token;
  if (frame_tree_node_->opener()) {
    opener_frame_token =
        frame_tree_node_->render_manager()->GetOpenerFrameToken(
            site_instance_group());
  }

  // The current `RenderFrameHost`'s `devtools_frame_token` can be used here
  // because it is not expected to differ when there is a
  // `RenderFrameProxyHost` in a separate window. The token may change on
  // MPArch activations in the main frame (e.g., prerender), but those
  // cannot occur if the `BrowsingInstance` has more than one window.
  const ::base::UnguessableToken& devtools_frame_token =
      frame_tree_node_->current_frame_host()->devtools_frame_token();

  if (frame_tree_node_->parent()) {
    // It is safe to use GetRenderFrameProxyHost to get the parent proxy, since
    // new child frames always start out as local frames, so a new proxy should
    // never have a RenderFrameHost as a parent.
    RenderFrameProxyHost* parent_proxy =
        frame_tree_node_->parent()
            ->browsing_context_state()
            ->GetRenderFrameProxyHost(site_instance_group());
    CHECK(parent_proxy);

    // Proxies that aren't live in the parent node should not be initialized
    // here, since there is no valid parent `blink::RemoteFrame` on the renderer
    // side. This can happen when adding a new child frame after an opener
    // process crashed and was reloaded. See https://crbug.com/501152.
    //
    // Note that with `batched_proxy_ipc_sender`, the parent proxy could be
    // non-live but pending creation. In that case, it is fine to initialize
    // this proxy, as `batched_proxy_ipc_sender` guarantees that its parent
    // will be created first in the renderer.
    GlobalRoutingID parent_global_id = parent_proxy->GetGlobalID();
    bool is_parent_proxy_creation_pending =
        batched_proxy_ipc_sender &&
        batched_proxy_ipc_sender->IsProxyCreationPending(parent_global_id);
    if (!parent_proxy->is_render_frame_proxy_live() &&
        !is_parent_proxy_creation_pending) {
      return false;
    }

    // TODO(crbug.com/40248300): Support main frame proxy batch creation
    // with batched_proxy_ipc_sender.
    if (batched_proxy_ipc_sender) {
      batched_proxy_ipc_sender->AddNewChildProxyCreationTask(
          GetSafeRef(), frame_token_, opener_frame_token,
          frame_tree_node_->tree_scope_type(),
          frame_tree_node_->current_replication_state().Clone(),
          frame_tree_node_->frame_owner_properties().Clone(),
          frame_tree_node_->IsLoading(), devtools_frame_token,
          CreateAndBindRemoteFrameInterfaces(), parent_global_id);

      // Don't call `SetRenderFrameProxyCreated(true)` here, since the proxy
      // wasn't actually created. This will be called for all
      // `RenderFrameProxyHosts` later in
      // `BatchedProxyIPCSender::CreateAllProxies`, after all proxies
      // are created.
    } else {
      // Note that `navigation_metrics_token` is intentionally *not* passed in
      // this IPC. This is because in practice, this path is no longer used for
      // creating proxies during a navigation; this is done via
      // `batched_proxy_ipc_sender` above instead. This path is still used for
      // non-navigation proxy creation, such as creating proxies for a newly
      // created subframe.
      parent_proxy->GetAssociatedRemoteFrame()->CreateRemoteChild(
          frame_token_, opener_frame_token, frame_tree_node_->tree_scope_type(),
          frame_tree_node_->current_replication_state().Clone(),
          frame_tree_node_->frame_owner_properties().Clone(),
          frame_tree_node_->IsLoading(), devtools_frame_token,
          CreateAndBindRemoteFrameInterfaces());
      SetRenderFrameProxyCreated(true);
    }
  } else {
    GetRenderViewHost()->GetAssociatedPageBroadcast()->CreateRemoteMainFrame(
        frame_token_, opener_frame_token,
        frame_tree_node_->current_replication_state().Clone(),
        frame_tree_node_->IsLoading(), devtools_frame_token,
        navigation_metrics_token, CreateAndBindRemoteFrameInterfaces(),
        CreateAndBindRemoteMainFrameInterfaces());
    SetRenderFrameProxyCreated(true);
  }

  return true;
}

AgentSchedulingGroupHost& RenderFrameProxyHost::GetAgentSchedulingGroup() {
  return site_instance_group_->agent_scheduling_group();
}

bool RenderFrameProxyHost::IsRelatedToCurrentFrameHost(
    CrossBrowsingInstanceExemption exemption) const {
  if (!base::FeatureList::IsEnabled(kEnforceCrossBrowsingInstanceChecks)) {
    return true;
  }

  // This check ensures that an IPC sent via a specific proxy (in the sender's
  // process) can only affect a target frame if the proxy and the target belong
  // to the same BrowsingInstance. Normally the active sender frame in this
  // proxy's process will be in the same SiteInstanceGroup and BCG as this
  // proxy, but even in compromised renderers we can guarantee that this proxy
  // is legitimately in the BCG and under the renderer process's control.
  //
  // In some cases, it may also be useful to check the BCG of the sender frame
  // when it will be used in response, such as DidChangeOpener or
  // RouteMessageEvent.
  RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
  if (site_instance_group_->IsRelatedSiteInstanceGroup(
          current_rfh->GetSiteInstance()->group())) {
    return true;
  }

  switch (exemption) {
    case CrossBrowsingInstanceExemption::kNone:
      break;
    case CrossBrowsingInstanceExemption::kEmbedderToInnerTree:
      // An embedder could only target the inner frame tree's main frame. Note
      // that using GetParent() instead of GetParentOrOuterDocument() allows any
      // kind of outer-to-inner FrameTree interaction, including both <webview>
      // tags and fenced frames. This is necessary because certain IPCs (such as
      // AdvanceFocus) need to work with fenced frames.
      if (!current_rfh->GetParent()) {
        if (RenderFrameHostImpl* embedder =
                current_rfh->GetParentOrOuterDocumentOrEmbedder()) {
          if (site_instance_group_ == embedder->GetSiteInstance()->group()) {
            return true;
          }
        }
      }
      break;
  }
  return false;
}

void RenderFrameProxyHost::SetRenderFrameProxyCreated(bool created) {
  render_frame_proxy_created_ = created;

  // Reset the mojo channels when the associated renderer is gone. It allows
  // reuse of the mojo channels when this RenderFrameProxyHost is reused.
  if (!render_frame_proxy_created_)
    TearDownMojoConnection();
}

const mojo::AssociatedRemote<blink::mojom::RemoteFrame>&
RenderFrameProxyHost::GetAssociatedRemoteFrame() {
  // TODO(crbug.com/544915927): CHECK-exclusion: Convert to a CHECK once we are
  // confident it won't be triggered.
  DCHECK(remote_frame_.is_bound());
  return remote_frame_;
}

const mojo::AssociatedRemote<blink::mojom::RemoteMainFrame>&
RenderFrameProxyHost::GetAssociatedRemoteMainFrame() {
  CHECK(remote_main_frame_.is_bound(), base::NotFatalUntil::M152);
  return remote_main_frame_;
}

void RenderFrameProxyHost::SetInheritedEffectiveTouchAction(
    cc::TouchAction touch_action) {
  cross_process_frame_connector_->OnSetInheritedEffectiveTouchAction(
      touch_action);
}

void RenderFrameProxyHost::UpdateRenderThrottlingStatus(bool is_throttled,
                                                        bool subtree_throttled,
                                                        bool display_locked) {
  cross_process_frame_connector_->UpdateRenderThrottlingStatus(
      is_throttled, subtree_throttled, display_locked);
}

void RenderFrameProxyHost::VisibilityChanged(
    blink::mojom::FrameVisibility visibility) {
  cross_process_frame_connector_->OnVisibilityChanged(visibility);
}

void RenderFrameProxyHost::UpdateOpener() {
  // Another frame in this proxy's SiteInstanceGroup may reach the new opener by
  // first reaching this proxy and then referencing its window.opener.  Ensure
  // the new opener's proxy exists in this case.
  if (frame_tree_node_->opener()) {
    frame_tree_node_->opener()->render_manager()->CreateOpenerProxies(
        site_instance_group(), frame_tree_node_,
        frame_tree_node_->current_frame_host()->browsing_context_state(),
        /*navigation_metrics_token=*/std::nullopt);
  }

  if (!is_render_frame_proxy_live())
    return;
  auto opener_frame_token =
      frame_tree_node_->render_manager()->GetOpenerFrameToken(
          site_instance_group());
  GetAssociatedRemoteFrame()->UpdateOpener(opener_frame_token);
}

void RenderFrameProxyHost::SetFocusedFrame() {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->Focus();
}

void RenderFrameProxyHost::ScrollRectToVisible(
    const gfx::RectF& rect_to_scroll,
    blink::mojom::ScrollIntoViewParamsPtr params) {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->ScrollRectToVisible(rect_to_scroll,
                                                  std::move(params));
}

void RenderFrameProxyHost::Detach() {
  if (frame_tree_node_->render_manager()->IsMainFrameForInnerDelegate()) {
    frame_tree_node_->render_manager()->RemoveOuterDelegateFrame();
    return;
  }

  // For a main frame with no outer delegate, no further work is needed. In this
  // case, detach can only be triggered by closing the entire RenderViewHost.
  // Instead, this cleanup relies on the destructors of RenderFrameHost and
  // RenderFrameProxyHost decrementing the refcounts of their associated
  // RenderViewHost. When the refcount hits 0, the corresponding renderer object
  // is cleaned up. Since WebContents destruction will also destroy
  // RenderFrameHost/RenderFrameProxyHost objects in FrameTree, this eventually
  // results in all the associated RenderViewHosts being closed.
  if (frame_tree_node_->IsMainFrame())
    return;

  // Otherwise, a remote child frame has been removed from the frame tree.
  // Make sure that this action is mirrored to all the other renderers, so
  // the frame tree remains consistent.
  frame_tree_node_->current_frame_host()->DetachFromProxy();
}

void RenderFrameProxyHost::CheckCompleted() {
  if (!IsRelatedToCurrentFrameHost()) {
    return;
  }
  RenderFrameHostImpl* target_rfh = frame_tree_node()->current_frame_host();
  target_rfh->GetAssociatedLocalFrame()->CheckCompleted();
}

void RenderFrameProxyHost::EnableAutoResize(const gfx::Size& min_size,
                                            const gfx::Size& max_size) {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->EnableAutoResize(min_size, max_size);
}

void RenderFrameProxyHost::DisableAutoResize() {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->DisableAutoResize();
}

void RenderFrameProxyHost::DidUpdateVisualProperties(
    const cc::RenderFrameMetadata& metadata) {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->DidUpdateVisualProperties(metadata);
}

void RenderFrameProxyHost::ChildProcessGone() {
  if (!is_render_frame_proxy_live())
    return;
  GetAssociatedRemoteFrame()->ChildProcessGone();
}

void RenderFrameProxyHost::DidFocusFrame() {
  TRACE_EVENT("navigation", "RenderFrameProxyHost::DidFocusFrame",
              ChromeTrackEvent::kFrameTreeNodeInfo, *frame_tree_node_,
              ChromeTrackEvent::kSiteInstanceGroup, *site_instance_group());
  // If a fenced frame has requested focus something wrong has gone on. We do
  // not support programmatic focus between the embedder and embeddee because
  // that could be a side channel.
  if (frame_tree_node_->IsInFencedFrameTree() &&
      frame_tree_node_->render_manager()->GetProxyToOuterDelegate() == this) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFPH_FOCUSED_FENCED_FRAME);
    return;
  }

  RenderFrameHostImpl* render_frame_host =
      frame_tree_node_->current_frame_host();
  // Do not focus inactive RenderFrameHost.
  if (!render_frame_host->IsActive())
    return;
  // Do not focus a RenderFrameHost in a different BrowsingInstance, except
  // when focusing an inner FrameTree (e.g., for <webview> tags).
  if (!IsRelatedToCurrentFrameHost(
          CrossBrowsingInstanceExemption::kEmbedderToInnerTree)) {
    return;
  }
  frame_tree_node_->SetFocusedFrame(site_instance_group());
}

void RenderFrameProxyHost::CapturePaintPreviewOfCrossProcessSubframe(
    const gfx::Rect& clip_rect,
    const base::UnguessableToken& guid) {
  if (!IsRelatedToCurrentFrameHost(
          CrossBrowsingInstanceExemption::kEmbedderToInnerTree)) {
    return;
  }
  RenderFrameHostImpl* rfh = frame_tree_node_->current_frame_host();
  // Do not capture paint on behalf of inactive RenderFrameHost.
  if (rfh->IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kCapturePaintPreviewProxy)) {
    return;
  }
  rfh->delegate()->CapturePaintPreviewOfCrossProcessSubframe(clip_rect, guid,
                                                             rfh);
}

void RenderFrameProxyHost::SetIsInert(bool inert) {
  cross_process_frame_connector_->SetIsInert(inert);
}

void RenderFrameProxyHost::RouteMessageEvent(
    const std::optional<blink::LocalFrameToken>& source_frame_token,
    const url::Origin& source_origin,
    const std::optional<url::Origin>& target_origin,
    blink::TransferableMessage message) {
  RenderFrameHostImpl* target_rfh = frame_tree_node()->current_frame_host();

  RenderFrameHostImpl* source_rfh = nullptr;
  if (source_frame_token) {
    source_rfh = RenderFrameHostImpl::FromFrameToken(
        GetProcess()->GetID(), source_frame_token.value());
  }

  // Give the embedder a chance to override the target for this postMessage.
  RenderFrameHost* override_target =
      GetContentClient()->browser()->GetPostMessageTargetOverride(
          target_rfh, source_frame_token, source_origin, target_origin);
  bool was_overridden = (override_target != nullptr);
  if (override_target) {
    target_rfh = RenderFrameHostImpl::From(override_target);
  }

  if (!target_rfh->IsRenderFrameLive()) {
    // Check if there is an inner delegate involved; if so target its main
    // frame or otherwise return since there is no point in forwarding the
    // message.
    FrameTreeNode* target_ftn = FrameTreeNode::GloballyFindByID(
        target_rfh->inner_tree_main_frame_tree_node_id());
    target_rfh = target_ftn ? target_ftn->current_frame_host() : nullptr;

    if (!target_rfh || !target_rfh->IsRenderFrameLive())
      return;
  }

  // |serialized_target_origin| argument of postMessage is already checked by
  // blink::LocalDOMWindow::DispatchMessageEventWithOriginCheck (needed for
  // messages sent within the same process - e.g. same-site, cross-origin),
  // but this check needs to be duplicated below in case the recipient renderer
  // process got compromised (i.e. in case the renderer-side check may be
  // bypassed), or navigation has changed the origin of the target frame in the
  // meantime. In either case, drop the message on the floor rather than
  // delivering it.
  if (target_origin &&
      !target_origin->IsSameOriginWith(target_rfh->GetLastCommittedOrigin())) {
    return;
  }

  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
  if (!policy->HostsOrigin(GetProcess()->GetDeprecatedID(), source_origin)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFPH_POST_MESSAGE_INVALID_SOURCE_ORIGIN);
    return;
  }

  if (message.delegated_capability !=
      blink::mojom::DelegatedCapability::kNone) {
    if (!source_frame_token) {
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFPH_POST_MESSAGE_INVALID_DELEGATED_CAPABILITY);
      return;
    }
    if (!source_rfh) {
      // If the source frame was detached while the message was in flight,
      // silently drop the message. Do not kill the renderer process because
      // this is a benign race condition.
      return;
    }

    // Check if the source frame currently has transient user activation, or if
    // it consumed transient user activation recently. Legitimate delegation
    // consumes user activation in the renderer process synchronously, which
    // asynchronously propagates to the browser via an IPC on the LocalFrameHost
    // Mojo interface. To prevent race conditions where the consumption IPC is
    // processed before RouteMessageEvent, we allow delegation if transient user
    // activation was active or consumed recently.
    static constexpr base::TimeDelta kMaxDelegationDelay = base::Seconds(1);
    bool has_activation =
        source_rfh->IsActiveUserActivation() ||
        (!source_rfh->last_user_activation_consumed_time().is_null() &&
         base::TimeTicks::Now() -
                 source_rfh->last_user_activation_consumed_time() <
             kMaxDelegationDelay);
    if (!has_activation) {
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFPH_POST_MESSAGE_INVALID_DELEGATED_CAPABILITY);
      return;
    }
  }

  SiteInstanceGroup* target_group = target_rfh->GetSiteInstance()->group();

  bool is_embedder_to_guest_communication = false;
  // An embedder could only target a guest's main frame, so it's enough to check
  // for the `target_rfh` having an embedder RFH.
  if (!target_rfh->GetParentOrOuterDocument()) {
    RenderFrameHostImpl* target_embedder_rfh =
        target_rfh->GetParentOrOuterDocumentOrEmbedder();
    // Note that this is not checking that the source and target are related,
    // but that the source is related to the embedder, allowing frames related
    // to the embedder to also message the guest.
    if (target_embedder_rfh &&
        site_instance_group()->IsRelatedSiteInstanceGroup(
            target_embedder_rfh->GetSiteInstance()->group())) {
      is_embedder_to_guest_communication = true;
    }
  }

  bool is_guest_to_embedder_communication = false;
  if (source_rfh) {
    RenderFrameHostImpl* source_outermost_rfh =
        source_rfh->GetOutermostMainFrame();
    RenderFrameHostImpl* source_embedder_rfh =
        source_outermost_rfh->GetParentOrOuterDocumentOrEmbedder();
    // Note that this is not checking that the source and target are related,
    // but that the target is related to the embedder.
    if (source_embedder_rfh &&
        target_group->IsRelatedSiteInstanceGroup(
            source_embedder_rfh->GetSiteInstance()->group())) {
      is_guest_to_embedder_communication = true;
    }
  }

  // Only deliver the message if the request came from a RenderFrameHost in the
  // same BrowsingInstance or if this is a message between a guest and its
  // embedder.
  // If the target was overridden, assume that it can be in a different
  // BrowsingInstance, so allow this check to pass.
  if (!target_group->IsRelatedSiteInstanceGroup(site_instance_group()) &&
      !is_embedder_to_guest_communication &&
      !is_guest_to_embedder_communication && !was_overridden) {
    return;
  }

  // Don't deliver any messages to PDF content frames.
  if (target_rfh->GetSiteInstance()->GetSiteInfo().is_pdf()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFPH_POST_MESSAGE_PDF_CONTENT_FRAME);
    return;
  }

  // If there is a |source_frame_token|, translate it to the frame token of the
  // equivalent RenderFrameProxyHost in the target process.
  std::optional<blink::RemoteFrameToken> translated_source_token;
  if (source_rfh) {
    // https://crbug.com/822958: If the postMessage is going to a descendant
    // frame, ensure that any pending visual properties such as size are sent
    // to the target frame before the postMessage, as sites might implicitly
    // be relying on this ordering.
    if (target_rfh->IsDescendantOfWithinFrameTree(source_rfh)) {
      target_rfh->GetRenderWidgetHost()
          ->SynchronizeVisualPropertiesIgnoringPendingAck();
    }

    if (was_overridden) {
      // If the target_rfh was overridden, skip creating a
      // RenderFrameProxyHost because a persistent communication channel
      // shouldn't be persisted after the message is sent.
    } else if (is_embedder_to_guest_communication) {
      CHECK(target_rfh->is_main_frame());
      // Create proxies for the source frame and its ancestors up to the main
      // frame. We explicitly avoid creating proxies for the entire frame tree
      // to prevent leaking the embedder's full frame structure and the
      // embedder's opener chain to the guest.

      source_rfh->frame_tree_node()
          ->GetRenderFrameHostManager()
          .CreateRenderFrameProxyAndAncestorChainIfNeeded(
              target_rfh->GetSiteInstance()->group());
    } else if (is_guest_to_embedder_communication) {
      // A RenderFrameProxyHost was already created when the guest was
      // attached.
      // We do not create proxies for the subframes of a guest. Note that the
      // computation of `is_embedder_to_guest_communication` above assumes
      // that guest subframes are not targetable.
    } else {
      // Ensure that we have a proxy for the source frame in the target
      // SiteInstance. If it doesn't exist, create it on demand and also
      // create its opener chain, since that will also be accessible to the
      // target page. This may be needed in rare cases such as a popup sending
      // a postMessage to a subframe of its opener, where the subframe has no
      // prior references to the popup.
      //
      // All cases where a proxy might be missing must involve a message being
      // sent across different FrameTrees, since all frames within the same
      // FrameTree always have references to one another; therefore, only run
      // proxy creation code for those cases as an optimization. For inner
      // frame trees, guests are already handled above, and fenced frames
      // disallow postMessage to/from their embedder.
      if (&source_rfh->frame_tree_node()->frame_tree() !=
          &target_rfh->frame_tree_node()->frame_tree()) {
        // Subtle: postMessages may be sent between frames after their page
        // has entered the back-forward cache (e.g., when dispatched from
        // pagehide events) - see
        // BackForwardCacheBrowserTest.PostMessageDelivered. (These messages
        // are subsequently deferred by the target renderer until the cached
        // page is re-activated.) In that case, it's neither correct nor
        // possible to create proxies, as that requires going through
        // FrameTreeNode and RenderFrameHostManager, where the current
        // RenderFrameHost is no longer `source_rfh` but rather some other RFH
        // in an unrelated SiteInstance. Fortunately, back-forward cache
        // restricts GetRelatedActiveContentsCount to 1, and new on-demand
        // proxies shouldn't ever be needed within a single FrameTree, where
        // all frames already have references to one another. So, this case
        // can simply be skipped. The same constraint would also apply to
        // pending deletion RenderFrameHosts where messages could be sent from
        // unload handlers, where it's also incorrect to create proxies in a
        // FrameTreeNode that has moved on to some other unrelated RFH.
        if (!source_rfh->IsInBackForwardCache() &&
            !source_rfh->IsPendingDeletion()) {
          // After skipping back-forward cache and pending deletion cases, we
          // should only get here when source_rfh is the current RFH in its
          // FrameTreeNode, since we shouldn't receive messages from
          // speculative or pending-commit RenderFrameHosts.
          CHECK_EQ(source_rfh,
                   source_rfh->frame_tree_node()->current_frame_host());
          source_rfh->frame_tree_node()->render_manager()->CreateOpenerProxies(
              target_rfh->GetSiteInstance()->group(), nullptr,
              source_rfh->browsing_context_state(),
              /*navigation_metrics_token=*/std::nullopt);
        }
      }
    }

    // If the message source is a cross-process subframe, its proxy will only
    // be created in --site-per-process mode, which is the case when we set an
    // actual non-empty value for |translated_source_token|. Otherwise (if the
    // proxy wasn't created), use an empty |translated_source_token| (see
    // https://crbug.com/485520 for discussion on why this is ok).
    // The proxy may be in a different BrowsingContextState in the case of
    // postMessages exchanged across inner and outer delegates.
    RenderFrameProxyHost* source_proxy_in_target_group =
        source_rfh->browsing_context_state()->GetRenderFrameProxyHost(
            target_group,
            BrowsingContextState::ProxyAccessMode::kAllowOuterDelegate);
    if (source_proxy_in_target_group) {
      translated_source_token = source_proxy_in_target_group->GetFrameToken();
    }
  }

  target_rfh->PostMessageEvent(
      translated_source_token, &source_origin,
      target_origin.has_value() ? &(*target_origin) : nullptr,
      std::move(message));
}

void RenderFrameProxyHost::PrintCrossProcessSubframe(const gfx::Rect& rect,
                                                     int32_t document_cookie) {
  if (!IsRelatedToCurrentFrameHost(
          CrossBrowsingInstanceExemption::kEmbedderToInnerTree)) {
    return;
  }
  RenderFrameHostImpl* rfh = frame_tree_node_->current_frame_host();
  rfh->delegate()->PrintCrossProcessSubframe(rect, document_cookie, rfh);
}

void RenderFrameProxyHost::SynchronizeVisualProperties(
    const blink::FrameVisualProperties& frame_visual_properties) {
  cross_process_frame_connector_->OnSynchronizeVisualProperties(
      frame_visual_properties);
}

void RenderFrameProxyHost::FocusPage() {
  if (!IsRelatedToCurrentFrameHost()) {
    return;
  }
  frame_tree_node_->current_frame_host()->FocusPage();
}

void RenderFrameProxyHost::TakeFocus(bool reverse) {
  if (!IsRelatedToCurrentFrameHost()) {
    return;
  }
  frame_tree_node_->current_frame_host()->TakeFocus(reverse);
}

void RenderFrameProxyHost::UpdateTargetURL(
    const GURL& url,
    blink::mojom::RemoteMainFrameHost::UpdateTargetURLCallback callback) {
  if (!IsRelatedToCurrentFrameHost()) {
    std::move(callback).Run();
    return;
  }
  frame_tree_node_->current_frame_host()->UpdateTargetURL(url,
                                                          std::move(callback));
}

void RenderFrameProxyHost::RouteCloseEvent() {
  // The renderer already ensures that this can only be called on an outermost
  // main frame - see DOMWindow::Close().  Terminate the renderer if this is
  // not the case.
  RenderFrameHostImpl* rfh = frame_tree_node_->current_frame_host();
  if (!rfh->IsOutermostMainFrame()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFPH_WINDOW_CLOSE_ON_NON_OUTERMOST_FRAME);
    return;
  }

  // Tell the active RenderFrameHost to run unload handlers and close, as long
  // as the request came from a RenderFrameHost in the same BrowsingInstance.
  // We receive this from a WebViewImpl when it receives a request to close the
  // window containing the active RenderFrameHost. Different BrowsingInstances
  // should not be able to close each other's windows.
  if (IsRelatedToCurrentFrameHost()) {
    rfh->ClosePage(RenderFrameHostImpl::ClosePageSource::kRenderer);
  }
}

void RenderFrameProxyHost::OpenURL(blink::mojom::OpenURLParamsPtr params) {
  // Verify and unpack IPC payload.
  GURL validated_url;
  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();

  if (!VerifyOpenURLParams(current_rfh, GetProcess(), params, &validated_url,
                           &blob_url_loader_factory)) {
    return;
  }

  // Only active documents are allowed to navigate from frame proxy:
  // - If the document is in pending deletion, ignore the navigation, because
  // the frame is going to disappear soon anyway.
  // - If the document is in back-forward cache, it's not allowed to navigate as
  // it should remain frozen. Ignore the request and evict the document from
  // back-forward cache.
  // - If the document is prerendering, we don't expect to get here because
  // prerendering pages are expected to defer cross-origin iframes, so there
  // should not be any OOPIFs. Just cancel prerendering if we get here.
  if (current_rfh->IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kOpenURL)) {
    return;
  }

  // Verify that we are in the same BrowsingInstance as the current
  // RenderFrameHost. Different BrowsingInstances should not be able to navigate
  // each other's frames.
  if (!IsRelatedToCurrentFrameHost()) {
    return;
  }

  if (params->initiator_frame_token) {
    RenderFrameHostImpl* initiator_frame = RenderFrameHostImpl::FromFrameToken(
        GetProcess()->GetID(), params->initiator_frame_token.value());
    if (current_rfh->IsOutermostMainFrame()) {
      MaybeRecordAdClickMainFrameNavigationMetrics(
          /*initiator_frame=*/initiator_frame, /*target_frame=*/current_rfh,
          validated_url, params->user_gesture, params->started_by_ad);
    }
  }

  blink::LocalFrameToken* initiator_frame_token =
      base::OptionalToPtr(params->initiator_frame_token);
  // TODO(crbug.com/510258191): Ensure that a well behaving renderer always has
  // an associated |initiator_navigation_state|, and terminate renderer
  // processes whose |initiator_navigation_state| we cannot find.
  scoped_refptr<InitiatorNavigationState> initiator_navigation_state =
      RenderFrameHostImpl::GetInitiatorNavigationStateFromFrameToken(
          initiator_frame_token, GetProcess()->GetDeprecatedID(),
          current_rfh->GetBrowserContext());

  bool is_initiator_sandboxed_with_forms = false;
  if (initiator_navigation_state) {
    auto* initiator_navigation_state_impl =
        static_cast<InitiatorNavigationStateImpl*>(
            initiator_navigation_state.get());
    is_initiator_sandboxed_with_forms =
        (initiator_navigation_state_impl->policy_container_policies()
             .sandbox_flags &
         network::mojom::WebSandboxFlags::kForms) !=
        network::mojom::WebSandboxFlags::kNone;
  }

  if ((params->is_form_submission || params->post_body) &&
      is_initiator_sandboxed_with_forms) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFPH_FORM_SUBMISSION_FROM_SANDBOXED_FRAME);
    return;
  }

  // Since this navigation targeted a specific RenderFrameProxy, it should stay
  // in the current tab.
  CHECK_EQ(WindowOpenDisposition::CURRENT_TAB, params->disposition,
           base::NotFatalUntil::M152);

  // Augment |download_policy| for situations that were not covered on the
  // renderer side, e.g. status not available on remote frame, etc.
  blink::NavigationDownloadPolicy download_policy = params->download_policy;
  GetContentClient()->browser()->AugmentNavigationDownloadPolicy(
      current_rfh, params->user_gesture, &download_policy);

  if ((frame_tree_node_->pending_frame_policy().sandbox_flags &
       network::mojom::WebSandboxFlags::kDownloads) !=
      network::mojom::WebSandboxFlags::kNone) {
    download_policy.SetDisallowed(blink::NavigationDownloadType::kSandbox);
  }

  // This will be used to set the Navigation Timing API navigationStart
  // parameter for renderer navigations in the remote frame. If the navigation
  // must wait on the current RenderFrameHost to execute its BeforeUnload event,
  // the navigation start will be updated when the BeforeUnload ack is received.
  const auto navigation_start_time = base::TimeTicks::Now();

  // TODO(lfg, lukasza): Remove |extra_headers| parameter from
  // RequestTransferURL method once both RenderFrameProxyHost and
  // RenderFrameHostImpl call RequestOpenURL from their OnOpenURL handlers.
  // See also https://crbug.com/647772.
  // TODO(clamy): The transition should probably be changed for POST navigations
  // to PAGE_TRANSITION_FORM_SUBMIT. See https://crbug.com/829827.
  frame_tree_node_->navigator().NavigateFromFrameProxy(
      current_rfh, validated_url, initiator_frame_token, GetProcess()->GetID(),
      params->initiator_origin, params->initiator_base_url,
      initiator_navigation_state, params->referrer.To<content::Referrer>(),
      ui::PAGE_TRANSITION_LINK, params->should_replace_current_entry,
      download_policy, params->post_body ? "POST" : "GET", params->post_body,
      params->extra_headers, std::move(blob_url_loader_factory),
      std::move(params->source_location), params->user_gesture,
      params->is_form_submission, params->started_by_ad,
      params->actual_navigation_start, navigation_start_time,
      /*is_embedder_initiated_fenced_frame_navigation=*/false,
      /*is_unfenced_top_navigation=*/false,
      /*force_new_browsing_instance=*/false, params->is_container_initiated,
      params->has_rel_opener);
}

void RenderFrameProxyHost::UpdateViewportIntersection(
    blink::mojom::ViewportIntersectionStatePtr intersection_state,
    const std::optional<blink::FrameVisualProperties>& visual_properties) {
  cross_process_frame_connector_->UpdateViewportIntersection(
      *intersection_state, visual_properties);
}

void RenderFrameProxyHost::DidChangeOpener(
    const std::optional<blink::LocalFrameToken>& opener_frame_token) {
  if (!IsRelatedToCurrentFrameHost()) {
    return;
  }
  // Note that this call internally protects against `opener_frame_token`
  // referring to an inactive frame.
  frame_tree_node_->render_manager()->DidChangeOpener(opener_frame_token,
                                                      site_instance_group());
}

void RenderFrameProxyHost::AdvanceFocus(
    blink::mojom::FocusType focus_type,
    const blink::LocalFrameToken& source_frame_token) {
  // Do not advance focus into a RenderFrameHost in a different
  // BrowsingInstance.
  if (!IsRelatedToCurrentFrameHost(
          CrossBrowsingInstanceExemption::kEmbedderToInnerTree)) {
    return;
  }

  // Translate the source RenderFrameHost in this process to its equivalent
  // RenderFrameProxyHost in the target SiteInstanceGroup.  This is needed for
  // continuing the focus traversal from correct place in a parent frame after
  // one of its child frames finishes its traversal.
  RenderFrameHostImpl* source_rfh = RenderFrameHostImpl::FromFrameToken(
      GetProcess()->GetID(), source_frame_token);
  RenderFrameHostImpl* target_rfh = frame_tree_node_->current_frame_host();
  RenderFrameProxyHost* source_proxy =
      source_rfh
          ? source_rfh->browsing_context_state()->GetRenderFrameProxyHost(
                target_rfh->GetSiteInstance()->group())
          : nullptr;

  if (source_rfh && (source_rfh->HasTransientUserActivation() ||
                     source_rfh->FocusSourceHasTransientUserActivation())) {
    target_rfh->ActivateFocusSourceUserActivation();
    source_rfh->DeactivateFocusSourceUserActivation();
  }

  if (!target_rfh->IsRenderFrameLive()) {
    // Do not advance focus if target renderer is gone and continue
    // focus traversal in the source frame.
    source_rfh->AdvanceFocus(focus_type, this);
    source_rfh->delegate()->OnAdvanceFocus(target_rfh);
  } else {
    target_rfh->AdvanceFocus(focus_type, source_proxy);
    target_rfh->delegate()->OnAdvanceFocus(source_rfh);
  }
}

bool RenderFrameProxyHost::IsInertForTesting() {
  return cross_process_frame_connector_->IsInert();
}

mojo::PendingAssociatedReceiver<blink::mojom::RemoteFrame>
RenderFrameProxyHost::BindRemoteFrameReceiverForTesting() {
  remote_frame_.reset();
  return remote_frame_.BindNewEndpointAndPassDedicatedReceiver();
}

mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrame>
RenderFrameProxyHost::BindRemoteMainFrameReceiverForTesting() {
  remote_main_frame_.reset();
  return remote_main_frame_.BindNewEndpointAndPassDedicatedReceiver();
}

blink::mojom::RemoteFrameInterfacesFromBrowserPtr
RenderFrameProxyHost::CreateAndBindRemoteFrameInterfaces() {
  auto params = blink::mojom::RemoteFrameInterfacesFromBrowser::New();
  BindRemoteFrameInterfaces(
      params->frame_receiver.InitWithNewEndpointAndPassRemote(),
      params->frame_host.InitWithNewEndpointAndPassReceiver());
  return params;
}

blink::mojom::RemoteMainFrameInterfacesPtr
RenderFrameProxyHost::CreateAndBindRemoteMainFrameInterfaces() {
  auto params = blink::mojom::RemoteMainFrameInterfaces::New();
  BindRemoteMainFrameInterfaces(
      params->main_frame.InitWithNewEndpointAndPassRemote(),
      params->main_frame_host.InitWithNewEndpointAndPassReceiver());
  return params;
}

void RenderFrameProxyHost::BindRemoteFrameInterfaces(
    mojo::PendingAssociatedRemote<blink::mojom::RemoteFrame> remote_frame,
    mojo::PendingAssociatedReceiver<blink::mojom::RemoteFrameHost>
        remote_frame_host_receiver) {
  // TODO(crbug.com/529425548): CHECK-exclusion: Convert to CHECKs once we are
  // confident it won't be triggered.
  DCHECK(!remote_frame_.is_bound());
  DCHECK(!remote_frame_host_receiver_.is_bound());

  remote_frame_.Bind(std::move(remote_frame));
  remote_frame_host_receiver_.Bind(std::move(remote_frame_host_receiver));

  if (g_observer_for_testing)
    g_observer_for_testing->OnRemoteFrameBound(this);
}

void RenderFrameProxyHost::BindRemoteMainFrameInterfaces(
    mojo::PendingAssociatedRemote<blink::mojom::RemoteMainFrame>
        remote_main_frame,
    mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrameHost>
        remote_main_frame_host_receiver) {
  CHECK(!remote_main_frame_.is_bound(), base::NotFatalUntil::M152);
  CHECK(!remote_main_frame_host_receiver_.is_bound(),
        base::NotFatalUntil::M152);

  remote_main_frame_.Bind(std::move(remote_main_frame));
  remote_main_frame_host_receiver_.Bind(
      std::move(remote_main_frame_host_receiver));

  if (g_observer_for_testing)
    g_observer_for_testing->OnRemoteMainFrameBound(this);
}

void RenderFrameProxyHost::TearDownMojoConnection() {
  remote_frame_.reset();
  remote_frame_host_receiver_.reset();
  remote_main_frame_.reset();
  remote_main_frame_host_receiver_.reset();
}

void RenderFrameProxyHost::WriteIntoTrace(
    perfetto::TracedProto<TraceProto> proto) const {
  proto->set_routing_id(GetRoutingID());
  proto->set_process_id(GetProcess()->GetDeprecatedID());
  proto->set_is_render_frame_proxy_live(is_render_frame_proxy_live());
  if (site_instance_group()) {
    proto->set_rvh_map_id(frame_tree_node_->frame_tree()
                              .GetRenderViewHostMapId(site_instance_group())
                              .value());
    proto->set_site_instance_id(site_instance_group()->GetId().value());
  }
}

base::SafeRef<RenderFrameProxyHost> RenderFrameProxyHost::GetSafeRef() {
  return weak_factory_.GetSafeRef();
}

}  // namespace content
