// Copyright 2013 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_host_impl.h"

#include <cstdint>
#include <memory>
#include <tuple>
#include <unordered_map>
#include <utility>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/lazy_instance.h"
#include "base/memory/memory_pressure_monitor.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/kill.h"
#include "base/ranges/algorithm.h"
#include "base/state_transitions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/syslog_logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/optional_trace_event.h"
#include "base/types/optional_util.h"
#include "build/build_config.h"
#include "components/download/public/common/download_url_parameters.h"
#include "content/browser/about_url_loader_factory.h"
#include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/render_accessibility_host.h"
#include "content/browser/bad_message.h"
#include "content/browser/bluetooth/web_bluetooth_service_impl.h"
#include "content/browser/broadcast_channel/broadcast_channel_provider.h"
#include "content/browser/broadcast_channel/broadcast_channel_service.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/browsing_topics/browsing_topics_url_loader_service.h"
#include "content/browser/can_commit_status.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/code_cache/generated_code_cache_context.h"
#include "content/browser/data_url_loader_factory.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/download/data_url_blob_reader.h"
#include "content/browser/feature_observer.h"
#include "content/browser/fenced_frame/fenced_frame.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
#include "content/browser/file_system/file_system_manager_impl.h"
#include "content/browser/file_system/file_system_url_loader_factory.h"
#include "content/browser/file_system_access/file_system_access_manager_impl.h"
#include "content/browser/font_access/font_access_manager.h"
#include "content/browser/generic_sensor/sensor_provider_proxy_impl.h"
#include "content/browser/geolocation/geolocation_service_impl.h"
#include "content/browser/idle/idle_manager_impl.h"
#include "content/browser/installedapp/installed_app_provider_impl.h"
#include "content/browser/interest_group/ad_auction_document_data.h"
#include "content/browser/loader/file_url_loader_factory.h"
#include "content/browser/loader/keep_alive_url_loader_service.h"
#include "content/browser/loader/navigation_early_hints_manager.h"
#include "content/browser/loader/prefetch_url_loader_service.h"
#include "content/browser/loader/resource_cache_manager.h"
#include "content/browser/log_console_message.h"
#include "content/browser/media/media_interface_proxy.h"
#include "content/browser/media/webaudio/audio_context_manager_impl.h"
#include "content/browser/navigation_or_document_handle.h"
#include "content/browser/navigation_subresource_loader_params.h"
#include "content/browser/network/cross_origin_embedder_policy_reporter.h"
#include "content/browser/network/cross_origin_opener_policy_reporter.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/permissions/permission_service_context.h"
#include "content/browser/portal/portal.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/preloading/prerender/prerender_host_registry.h"
#include "content/browser/preloading/prerender/prerender_metrics.h"
#include "content/browser/presentation/presentation_service_impl.h"
#include "content/browser/process_lock.h"
#include "content/browser/push_messaging/push_messaging_manager.h"
#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/renderer_host/back_forward_cache_impl.h"
#include "content/browser/renderer_host/close_listener_host.h"
#include "content/browser/renderer_host/code_cache_host_impl.h"
#include "content/browser/renderer_host/cookie_utils.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/input/input_injector_impl.h"
#include "content/browser/renderer_host/input/input_router.h"
#include "content/browser/renderer_host/input/timeout_monitor.h"
#include "content/browser/renderer_host/ipc_utils.h"
#include "content/browser/renderer_host/local_network_access_util.h"
#include "content/browser/renderer_host/media/peer_connection_tracker_host.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/navigator.h"
#include "content/browser/renderer_host/page_factory.h"
#include "content/browser/renderer_host/pending_beacon_host.h"
#include "content/browser/renderer_host/pending_beacon_service.h"
#include "content/browser/renderer_host/render_frame_host_delegate.h"
#include "content/browser/renderer_host/render_frame_host_owner.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_impl.h"
#include "content/browser/renderer_host/render_widget_host_factory.h"
#include "content/browser/renderer_host/render_widget_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/renderer_host/view_transition_opt_in_state.h"
#include "content/browser/runtime_feature_state/runtime_feature_state_document_data.h"
#include "content/browser/scoped_active_url.h"
#include "content/browser/service_worker/service_worker_container_host.h"
#include "content/browser/service_worker/service_worker_object_host.h"
#include "content/browser/site_info.h"
#include "content/browser/sms/webotp_service.h"
#include "content/browser/speech/speech_synthesis_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/url_loader_factory_params_helper.h"
#include "content/browser/usb/web_usb_service_impl.h"
#include "content/browser/web_exposed_isolation_info.h"
#include "content/browser/web_package/prefetched_signed_exchange_cache.h"
#include "content/browser/web_package/subresource_web_bundle_navigation_info.h"
#include "content/browser/webauth/authenticator_impl.h"
#include "content/browser/webauth/webauth_request_security_checker.h"
#include "content/browser/webid/federated_auth_request_impl.h"
#include "content/browser/webid/flags.h"
#include "content/browser/websockets/websocket_connector_impl.h"
#include "content/browser/webtransport/web_transport_connector_impl.h"
#include "content/browser/webui/url_data_manager_backend.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/browser/worker_host/dedicated_worker_host.h"
#include "content/browser/worker_host/dedicated_worker_host_factory_impl.h"
#include "content/browser/worker_host/dedicated_worker_hosts_for_document.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/debug_utils.h"
#include "content/common/features.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_messages.mojom.h"
#include "content/common/navigation_client.mojom.h"
#include "content/common/navigation_params_utils.h"
#include "content/public/browser/active_url_message_filter.h"
#include "content/public/browser/ax_event_notification_details.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/document_ref.h"
#include "content/public/browser/document_service_internal.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/peak_gpu_memory_tracker.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/sms_fetcher.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/weak_document_ptr.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/common/alternative_error_page_override_info.mojom.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/extra_mojo_js_features.mojom.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/network_service_util.h"
#include "content/public/common/origin_util.h"
#include "content/public/common/page_visibility_state.h"
#include "content/public/common/referrer.h"
#include "content/public/common/referrer_type_converters.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "media/base/media_switches.h"
#include "media/learning/common/value.h"
#include "media/media_buildflags.h"
#include "media/mojo/mojom/remoting.mojom.h"
#include "media/mojo/services/video_decode_perf_history.h"
#include "media/render_frame_audio_output_stream_factory.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "mojo/public/cpp/system/data_pipe.h"
#if BUILDFLAG(IS_OHOS)
#include "mojo/public/cpp/system/platform_handle.h"
#endif
#include "net/base/schemeful_site.h"
#include "net/net_buildflags.h"
#include "ppapi/buildflags/buildflags.h"
#include "render_frame_host_impl.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/cors/origin_access_list.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/network_service_buildflags.h"
#include "services/network/public/cpp/not_implemented_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "storage/browser/blob/blob_url_store_impl.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
#include "third_party/blink/public/common/frame/fenced_frame_sandbox_flags.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "third_party/blink/public/common/loader/inter_process_time_ticks_converter.h"
#include "third_party/blink/public/common/loader/resource_type_util.h"
#include "third_party/blink/public/common/messaging/transferable_message.h"
#include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/permissions_policy/document_policy.h"
#include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_study_document_created.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom.h"
#include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "third_party/blink/public/mojom/frame/media_player_action.mojom.h"
#include "third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
#include "third_party/blink/public/mojom/opengraph/metadata.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
#include "third_party/blink/public/mojom/storage_key/ancestor_chain_bit.mojom.h"
#include "third_party/blink/public/mojom/timing/resource_timing.mojom.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
#include "ui/accessibility/ax_action_handler_registry.h"
#include "ui/accessibility/ax_common.h"
#include "ui/accessibility/ax_tree_update.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"

#if BUILDFLAG(IS_ANDROID)
#include "content/browser/android/content_url_loader_factory.h"
#include "content/browser/android/java_interfaces_impl.h"
#include "content/browser/renderer_host/render_frame_host_android.h"
#include "content/public/browser/android/java_interfaces.h"
#include "content/public/browser/authenticator_request_client_delegate.h"
#else
#include "content/browser/hid/hid_service.h"
#include "content/browser/host_zoom_map_impl.h"
#include "content/browser/serial/serial_service.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "content/browser/smart_card/smart_card_service.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "content/browser/renderer_host/popup_menu_helper_mac.h"
#endif

#if BUILDFLAG(ENABLE_PPAPI)
#include "content/browser/renderer_host/render_frame_host_impl_ppapi_support.h"
#endif

#if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#endif

#if defined(OHOS_LOGGER_REPORT)
#include "url/ohos/log_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/browser/web_contents/web_contents_impl.h"
#endif

namespace features {
BASE_FEATURE(kDisableFrameNameUpdateOnNonCurrentRenderFrameHost,
             "DisableFrameNameUpdateOnNonCurrentRenderFrameHost",
             base::FEATURE_ENABLED_BY_DEFAULT);

// Evict when accessibility events occur while in back/forward cache. Remove
// once the https://crbug.com/1341507 is resolved. This crash started to happen
// on Android with bfcache experiments, so we're enabling this flag only on
// Android.
BASE_FEATURE(kEvictOnAXEvents,
             "EvictOnAXEvents",
#if BUILDFLAG(IS_ANDROID)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

BASE_FEATURE(kUnblockSpeechSynthesisForBFCache,
             "UnblockSpeechSynthesisForBFCache",
             base::FEATURE_DISABLED_BY_DEFAULT);

}  // namespace features

namespace content {

#if defined(AX_FAIL_FAST_BUILD)
// Enable fast fails on clusterfuzz and other builds used to debug Chrome,
// which should help narrow down illegal ax trees more quickly.
// static
int RenderFrameHostImpl::max_accessibility_resets_ = 0;
#else
int RenderFrameHostImpl::max_accessibility_resets_ = 4;
#endif  // AX_FAIL_FAST_BUILD

class RenderFrameHostOrProxy {
 public:
  RenderFrameHostOrProxy(RenderFrameHostImpl* frame,
                         RenderFrameProxyHost* proxy) {
    DCHECK(!frame || !proxy)
        << "Both frame and proxy can't be non-null at the same time";
    if (proxy) {
      frame_or_proxy_ = proxy;
      return;
    }
    if (frame) {
      frame_or_proxy_ = frame;
      return;
    }
  }

  explicit operator bool() { return frame_or_proxy_.index() > 0; }

  FrameTreeNode* GetFrameTreeNode() {
    if (auto* proxy = GetProxy()) {
      return proxy->frame_tree_node();
    } else if (auto* frame = GetFrame()) {
      return frame->frame_tree_node();
    }
    return nullptr;
  }

  RenderFrameHostImpl* GetCurrentFrameHost() {
    if (auto* proxy = GetProxy()) {
      return proxy->frame_tree_node()->current_frame_host();
    } else if (auto* frame = GetFrame()) {
      return frame;
    }
    return nullptr;
  }

 private:
  RenderFrameProxyHost* GetProxy() {
    if (auto** proxy = absl::get_if<RenderFrameProxyHost*>(&frame_or_proxy_)) {
      return *proxy;
    }
    return nullptr;
  }

  RenderFrameHostImpl* GetFrame() {
    if (auto** frame = absl::get_if<RenderFrameHostImpl*>(&frame_or_proxy_)) {
      return *frame;
    }
    return nullptr;
  }

  absl::variant<absl::monostate, RenderFrameHostImpl*, RenderFrameProxyHost*>
      frame_or_proxy_;
};

namespace {

// Maximum amount of time to wait for beforeunload/unload handlers to be
// processed by the renderer process.
constexpr int kUnloadTimeoutInMSec = 500;
constexpr base::TimeDelta kUnloadTimeout =
    base::Milliseconds(kUnloadTimeoutInMSec);

constexpr int kSubframeProcessShutdownDelayInMSec = 2 * 1000;
static_assert(kSubframeProcessShutdownDelayInMSec + kUnloadTimeoutInMSec <
                  RenderProcessHostImpl::kKeepAliveHandleFactoryTimeoutInMSec,
              "The maximum process shutdown delay should not exceed the "
              "keepalive timeout. This has security implications, see "
              "https://crbug.com/1177674.");

#if BUILDFLAG(IS_ANDROID)
const void* const kRenderFrameHostAndroidKey = &kRenderFrameHostAndroidKey;
#endif  // BUILDFLAG(IS_ANDROID)

// Causes RenderAccessibilityHost HandleAXEvents messages to be handled with
// minimal copying of the data.
//
// TODO(nuskos): Once we've conducted a retroactive study of chrometto
// improvements clean up this feature.
BASE_FEATURE(kRenderAccessibilityHostAvoidCopying,
             "RenderAccessibilityHostAvoidCopying",
             base::FEATURE_ENABLED_BY_DEFAULT);

// The next value to use for the accessibility reset token.
int g_next_accessibility_reset_token = 1;

// Whether to allow injecting javascript into any kind of frame, for Android
// WebView, WebLayer, Fuchsia web.ContextProvider and CastOS content shell.
bool g_allow_injecting_javascript = false;

const char kDotGoogleDotCom[] = ".google.com";

typedef std::unordered_map<GlobalRenderFrameHostId,
                           RenderFrameHostImpl*,
                           GlobalRenderFrameHostIdHasher>
    RoutingIDFrameMap;
base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit g_routing_id_frame_map =
    LAZY_INSTANCE_INITIALIZER;

// A global set of all sandboxed RenderFrameHosts that could be isolated from
// the rest of their SiteInstance.
typedef std::unordered_set<GlobalRenderFrameHostId,
                           GlobalRenderFrameHostIdHasher>
    RoutingIDIsolatableSandboxedIframesSet;
base::LazyInstance<RoutingIDIsolatableSandboxedIframesSet>::DestructorAtExit
    g_routing_id_isolatable_sandboxed_iframes_set = LAZY_INSTANCE_INITIALIZER;

using TokenFrameMap = std::unordered_map<blink::LocalFrameToken,
                                         RenderFrameHostImpl*,
                                         blink::LocalFrameToken::Hasher>;
base::LazyInstance<TokenFrameMap>::Leaky g_token_frame_map =
    LAZY_INSTANCE_INITIALIZER;

auto& GetDocumentTokenMap() {
  static base::NoDestructor<std::unordered_map<
      blink::DocumentToken, RenderFrameHostImpl*, blink::DocumentToken::Hasher>>
      map;
  return *map;
}

BackForwardCacheMetrics::NotRestoredReason
RendererEvictionReasonToNotRestoredReason(
    blink::mojom::RendererEvictionReason reason) {
  switch (reason) {
    case blink::mojom::RendererEvictionReason::kJavaScriptExecution:
      return BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution;
    case blink::mojom::RendererEvictionReason::
        kNetworkRequestDatapipeDrainedAsBytesConsumer:
      return BackForwardCacheMetrics::NotRestoredReason::
          kNetworkRequestDatapipeDrainedAsBytesConsumer;
    case blink::mojom::RendererEvictionReason::kNetworkRequestRedirected:
      return BackForwardCacheMetrics::NotRestoredReason::
          kNetworkRequestRedirected;
    case blink::mojom::RendererEvictionReason::kNetworkRequestTimeout:
      return BackForwardCacheMetrics::NotRestoredReason::kNetworkRequestTimeout;
    case blink::mojom::RendererEvictionReason::kNetworkExceedsBufferLimit:
      return BackForwardCacheMetrics::NotRestoredReason::
          kNetworkExceedsBufferLimit;
  }
  NOTREACHED();
  return BackForwardCacheMetrics::NotRestoredReason::kUnknown;
}

// Ensure that we reset nav_entry_id_ in DidCommitProvisionalLoad if any of
// the validations fail and lead to an early return.  Call disable() once we
// know the commit will be successful.  Resetting nav_entry_id_ avoids acting on
// any UpdateState or UpdateTitle messages after an ignored commit.
class ScopedCommitStateResetter {
 public:
  explicit ScopedCommitStateResetter(RenderFrameHostImpl* render_frame_host)
      : render_frame_host_(render_frame_host) {}

  ~ScopedCommitStateResetter() {
    if (!disabled_) {
      render_frame_host_->set_nav_entry_id(0);
    }
  }

  void disable() { disabled_ = true; }

 private:
  raw_ptr<RenderFrameHostImpl> render_frame_host_;
  bool disabled_ = false;
};

void GrantFileAccess(int child_id,
                     const std::vector<base::FilePath>& file_paths) {
  ChildProcessSecurityPolicyImpl* policy =
      ChildProcessSecurityPolicyImpl::GetInstance();

  for (const auto& file : file_paths) {
    if (!policy->CanReadFile(child_id, file))
      policy->GrantReadFile(child_id, file);
  }
}

#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
// RemoterFactory that delegates Create() calls to the ContentBrowserClient.
//
// Since Create() could be called at any time, perhaps by a stray task being run
// after a RenderFrameHost has been destroyed, the RemoterFactoryImpl uses the
// process/routing IDs as a weak reference to the RenderFrameHostImpl.
class RemoterFactoryImpl final : public media::mojom::RemoterFactory {
 public:
  RemoterFactoryImpl(int process_id, int routing_id)
      : process_id_(process_id), routing_id_(routing_id) {}

  RemoterFactoryImpl(const RemoterFactoryImpl&) = delete;
  RemoterFactoryImpl& operator=(const RemoterFactoryImpl&) = delete;

 private:
  void Create(mojo::PendingRemote<media::mojom::RemotingSource> source,
              mojo::PendingReceiver<media::mojom::Remoter> receiver) final {
    if (auto* host = RenderFrameHostImpl::FromID(process_id_, routing_id_)) {
      GetContentClient()->browser()->CreateMediaRemoter(host, std::move(source),
                                                        std::move(receiver));
    }
  }

  const int process_id_;
  const int routing_id_;
};
#endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)

RenderFrameHostOrProxy LookupRenderFrameHostOrProxy(int process_id,
                                                    int routing_id) {
  RenderFrameHostImpl* rfh =
      RenderFrameHostImpl::FromID(process_id, routing_id);
  RenderFrameProxyHost* proxy = nullptr;
  if (!rfh)
    proxy = RenderFrameProxyHost::FromID(process_id, routing_id);
  return RenderFrameHostOrProxy(rfh, proxy);
}

RenderFrameHostOrProxy LookupRenderFrameHostOrProxy(
    int process_id,
    const blink::FrameToken& frame_token) {
  if (frame_token.Is<blink::LocalFrameToken>()) {
    auto it = g_token_frame_map.Get().find(
        frame_token.GetAs<blink::LocalFrameToken>());
    // 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.
    if (it == g_token_frame_map.Get().end() ||
        process_id != it->second->GetProcess()->GetID()) {
      return RenderFrameHostOrProxy(nullptr, nullptr);
    }
    return RenderFrameHostOrProxy(it->second, nullptr);
  }
  DCHECK(frame_token.Is<blink::RemoteFrameToken>());
  return RenderFrameHostOrProxy(
      nullptr, RenderFrameProxyHost::FromFrameToken(
                   process_id, frame_token.GetAs<blink::RemoteFrameToken>()));
}

// Set crash keys that will help understand the circumstances of a renderer
// kill.  Note that the commit URL is already reported in a crash key, and
// additional keys are logged in RenderProcessHostImpl::ShutdownForBadMessage.
void LogRendererKillCrashKeys(const SiteInfo& site_info) {
  static auto* const site_info_key = base::debug::AllocateCrashKeyString(
      "current_site_info", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(site_info_key, site_info.GetDebugString());
}

void LogCanCommitOriginAndUrlFailureReason(const std::string& failure_reason) {
  static auto* const failure_reason_key = base::debug::AllocateCrashKeyString(
      "rfhi_can_commit_failure_reason", base::debug::CrashKeySize::Size64);
  base::debug::SetCrashKeyString(failure_reason_key, failure_reason);
}

std::unique_ptr<blink::PendingURLLoaderFactoryBundle> CloneFactoryBundle(
    scoped_refptr<blink::URLLoaderFactoryBundle> bundle) {
  return base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>(
      bundle->Clone().release()));
}

// Helper method to download a URL on UI thread.
void StartDownload(
    std::unique_ptr<download::DownloadUrlParameters> parameters,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  RenderProcessHost* render_process_host =
      RenderProcessHost::FromID(parameters->render_process_host_id());
  if (!render_process_host)
    return;

  BrowserContext* browser_context = render_process_host->GetBrowserContext();

  DownloadManager* download_manager = browser_context->GetDownloadManager();
  parameters->set_download_source(download::DownloadSource::FROM_RENDERER);
  download_manager->DownloadUrl(std::move(parameters),
                                std::move(blob_url_loader_factory));
}

// Called on the UI thread when the data URL in the BlobDataHandle
// is read.
void OnDataURLRetrieved(
    std::unique_ptr<download::DownloadUrlParameters> parameters,
    GURL data_url) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!data_url.is_valid())
    return;
  parameters->set_url(std::move(data_url));
  StartDownload(std::move(parameters), nullptr);
}

// Analyzes trusted sources of a frame's trust-token-redemption Permissions
// Policy feature to see if the feature is definitely disabled or potentially
// enabled.
//
// This information will be bound to a URLLoaderFactory; if the answer is
// "definitely disabled," the network service will report a bad message if it
// receives a request from the renderer to execute a Trust Tokens redemption or
// signing operation in the frame.
//
// A return value of kForbid denotes that the feature is disabled for the
// frame. A return value of kPotentiallyPermit means that all trusted
// information sources say that the policy is enabled.
network::mojom::TrustTokenOperationPolicyVerdict
DetermineWhetherToForbidTrustTokenOperation(
    const RenderFrameHostImpl* frame,
    const blink::mojom::CommitNavigationParams& commit_params,
    const url::Origin& subframe_origin,
    absl::optional<blink::FencedFrame::DeprecatedFencedFrameMode>
        fenced_frame_mode_for_navigation,
    const network::mojom::TrustTokenOperationType& operation) {
  std::unique_ptr<blink::PermissionsPolicy> subframe_policy;
  // TODO(https://crbug.com/1430514): Add WPT to test how TrustTokens behave in
  // a FencedFrame's subframe.
  if (frame->IsFencedFrameRoot()) {
    // Fenced frames have a list of required permission policies to load and
    // can't be granted extra policies, so use the required policies instead of
    // inheriting from its parent. Note that the parent policies must allow the
    // required policies, which is checked separately in
    // NavigationRequest::CheckPermissionsPoliciesForFencedFrames.
    CHECK(fenced_frame_mode_for_navigation);
    subframe_policy = blink::PermissionsPolicy::CreateForFencedFrame(
        subframe_origin, fenced_frame_mode_for_navigation.value());
  } else {
    // For main frame loads, the frame's permissions policy is determined
    // entirely by response headers, which are provided by the renderer.
    if (!frame->GetParent())
      return network::mojom::TrustTokenOperationPolicyVerdict::
          kPotentiallyPermit;

    const blink::PermissionsPolicy* parent_policy =
        frame->GetParent()->permissions_policy();
    blink::ParsedPermissionsPolicy container_policy =
        commit_params.frame_policy.container_policy;

    subframe_policy = blink::PermissionsPolicy::CreateFromParentPolicy(
        parent_policy, container_policy, subframe_origin);
  }

  switch (operation) {
    case network::mojom::TrustTokenOperationType::kRedemption:
    case network::mojom::TrustTokenOperationType::kSigning:
      if (subframe_policy->IsFeatureEnabled(
              blink::mojom::PermissionsPolicyFeature::kTrustTokenRedemption)) {
        return network::mojom::TrustTokenOperationPolicyVerdict::
            kPotentiallyPermit;
      }
      return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
    case network::mojom::TrustTokenOperationType::kIssuance:
      if (subframe_policy->IsFeatureEnabled(
              blink::mojom::PermissionsPolicyFeature::
                  kPrivateStateTokenIssuance)) {
        return network::mojom::TrustTokenOperationPolicyVerdict::
            kPotentiallyPermit;
      }
      return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
  }
  return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
}

// When a frame creates its initial subresource loaders, it needs to know
// whether the trust-token-redemption Permissions Policy feature will be enabled
// after the commit finishes, which is a little involved (see
// DetermineWhetherToForbidTrustTokenOperation). In contrast, if it needs to
// make this decision once the frame has committted---for instance, to create
// more loaders after the network service crashes---it can directly consult the
// current Permissions Policy state to determine whether the feature is enabled.
network::mojom::TrustTokenOperationPolicyVerdict
DetermineAfterCommitWhetherToForbidTrustTokenOperation(
    RenderFrameHostImpl& impl,
    const network::mojom::TrustTokenOperationType& operation) {
  switch (operation) {
    case network::mojom::TrustTokenOperationType::kRedemption:
    case network::mojom::TrustTokenOperationType::kSigning:
      if (impl.IsFeatureEnabled(
              blink::mojom::PermissionsPolicyFeature::kTrustTokenRedemption)) {
        return network::mojom::TrustTokenOperationPolicyVerdict::
            kPotentiallyPermit;
      }
      return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
    case network::mojom::TrustTokenOperationType::kIssuance:
      if (impl.IsFeatureEnabled(blink::mojom::PermissionsPolicyFeature::
                                    kPrivateStateTokenIssuance)) {
        return network::mojom::TrustTokenOperationPolicyVerdict::
            kPotentiallyPermit;
      }
      return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
  }
  return network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
}

// Verify that |browser_side_origin| and |renderer_side_origin| match.  See also
// https://crbug.com/888079. Returns true if the origins match, and false
// otherwise.
bool VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(
    NavigationRequest* navigation_request,
    const mojom::DidCommitProvisionalLoadParams& params) {
  DCHECK(navigation_request);

  // This should be called only when a new document is created. Navigations in
  // the same document and page activations do not create a new document.
  DCHECK(!navigation_request->IsSameDocument());
  DCHECK(!navigation_request->IsPageActivation());

  // Ignore for now cases where the NavigationRequest is in an unexpectedly
  // early state. Triggered by the following tests:
  // NavigationBrowserTest.OpenerNavigation_DownloadPolicy,
  // WebContentsImplBrowserTest.NewNamedWindow.
  if (navigation_request->state() < NavigationRequest::WILL_PROCESS_RESPONSE)
    return true;

  // Blob urls with content scheme are opaque on browser side because the
  // browser doesn't have access to the BlobURLNullOriginMap.
  // (https://crbug.com/1295268).
  const url::Origin& renderer_side_origin = params.origin;
  std::pair<absl::optional<url::Origin>, std::string>
      browser_side_origin_and_debug_info =
          navigation_request->GetOriginToCommitWithDebugInfo();
  if (renderer_side_origin.scheme() == url::kContentScheme &&
      browser_side_origin_and_debug_info.first->opaque()) {
    return true;
  }

  // For non-opaque origins, we say the browser and renderer calculated origins
  // match if they are exactly the same.
  bool origins_match = (browser_side_origin_and_debug_info.first.value() ==
                        renderer_side_origin);

  // For opaque origins, we say the browser and renderer calculated origins
  // match if their precursor origins match (their nonces might not match).
  if (renderer_side_origin.opaque() &&
      browser_side_origin_and_debug_info.first->opaque()) {
    origins_match = (renderer_side_origin.GetTupleOrPrecursorTupleIfOpaque() ==
                     browser_side_origin_and_debug_info.first
                         ->GetTupleOrPrecursorTupleIfOpaque());
  }

  // TODO(https://crbug.com/888079): Remove the DumpWithoutCrashing below, once
  // we are sure that the `browser_side_origin` is always the same as the
  // `renderer_side_origin`.
  if (!origins_match) {
    NavigationRequest::ScopedCrashKeys navigation_request_crash_keys(
        *navigation_request);
    SCOPED_CRASH_KEY_STRING256(
        "", "browser_origin",
        browser_side_origin_and_debug_info.first->GetDebugString());
    SCOPED_CRASH_KEY_STRING256("", "browser_debug_info",
                               browser_side_origin_and_debug_info.second);

    SCOPED_CRASH_KEY_STRING256(
        "", "browser_ready_to_commit_origin",
        navigation_request->browser_side_origin_to_commit_with_debug_info()
            .first->GetDebugString());
    SCOPED_CRASH_KEY_STRING256(
        "", "browser_ready_to_commit_debug_info",
        navigation_request->browser_side_origin_to_commit_with_debug_info()
            .second);

    SCOPED_CRASH_KEY_STRING256("", "renderer_origin",
                               renderer_side_origin.GetDebugString());
    SCOPED_CRASH_KEY_STRING256("", "renderer_debug_info",
                               params.origin_calculation_debug_info);
    CaptureTraceForNavigationDebugScenario(
        DebugScenario::kDebugBrowserVsRendererOriginToCommit);
    base::debug::DumpWithoutCrashing();
    DCHECK_EQ(browser_side_origin_and_debug_info.first.value(),
              renderer_side_origin)
        << "; navigation_request->GetURL() = " << navigation_request->GetURL();
    return false;
  }

  return true;
}

// Enum used for Navigation.VerifyDidCommitParams histogram, to indicate which
// DidCommitProvisionalLoadParams differ when comparing browser- vs
// renderer-calculated values.
// Do NOT delete or reorder existing entries.
enum class VerifyDidCommitParamsDifference {
  kIntendedAsNewEntry = 0,
  kMethod = 1,
  kURLIsUnreachable = 2,
  kBaseURL = 3,
  kPostID = 4,
  kIsOverridingUserAgent = 5,
  kHTTPStatusCode = 6,
  kShouldUpdateHistory = 7,
  kGesture = 8,
  kShouldReplaceCurrentEntry = 9,
  kURL = 10,
  kDidCreateNewEntry = 11,
  kTransition = 12,
  kHistoryListWasCleared = 13,
  kOrigin = 14,
  kMaxValue = kOrigin,
};

// A simplified version of Blink's WebFrameLoadType, used to simulate renderer
// calculations. See CalculateRendererLoadType() further below.
// TODO(https://crbug.com/1131832): This should only be here temporarily.
// Remove this once the renderer behavior at commit time is more consistent with
// what the browser instructed it to do (e.g. reloads will always be classified
// as kReload).
enum class RendererLoadType {
  kStandard,
  kBackForward,
  kReload,
  kReplaceCurrentItem,
};

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Used to log whether the call to SSLManager::DidStartResourceResponse()
// resulted in a no-op or if the exceptions were cleared out when a good
// certificate was seen. Matches histogram enum (SSLSubresourceResponseType).
enum class SSLSubresourceResponseType {
  // Includes cases when call resulted in a no-op.
  kIgnored = 0,
  // Includes cases when exceptions were cleared after seeing a good cert.
  kProcessed = 1,
  kMaxValue = kProcessed,
};

bool ValidateCSPAttribute(const std::string& value) {
  static const size_t kMaxLengthCSPAttribute = 4096;
  if (!base::IsStringASCII(value))
    return false;
  if (value.length() > kMaxLengthCSPAttribute ||
      value.find('\n') != std::string::npos ||
      value.find('\r') != std::string::npos) {
    return false;
  }
  return true;
}

perfetto::protos::pbzero::FrameDeleteIntention FrameDeleteIntentionToProto(
    mojom::FrameDeleteIntention intent) {
  using ProtoLevel = perfetto::protos::pbzero::FrameDeleteIntention;
  switch (intent) {
    case mojom::FrameDeleteIntention::kNotMainFrame:
      return ProtoLevel::FRAME_DELETE_INTENTION_NOT_MAIN_FRAME;
    case mojom::FrameDeleteIntention::kSpeculativeMainFrameForShutdown:
      return ProtoLevel::
          FRAME_DELETE_INTENTION_SPECULATIVE_MAIN_FRAME_FOR_SHUTDOWN;
    case mojom::FrameDeleteIntention::
        kSpeculativeMainFrameForNavigationCancelled:
      return ProtoLevel::
          FRAME_DELETE_INTENTION_SPECULATIVE_MAIN_FRAME_FOR_NAVIGATION_CANCELLED;
  }
  // All cases should've been handled by the switch case above.
  NOTREACHED();
  return ProtoLevel::FRAME_DELETE_INTENTION_NOT_MAIN_FRAME;
}

void WriteRenderFrameImplDeletion(perfetto::EventContext& ctx,
                                  RenderFrameHostImpl* rfh,
                                  mojom::FrameDeleteIntention intent) {
  auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
  auto* data = event->set_render_frame_impl_deletion();
  data->set_has_pending_commit(rfh->HasPendingCommitNavigation());
  data->set_has_pending_cross_document_commit(
      rfh->HasPendingCommitForCrossDocumentNavigation());
  data->set_frame_tree_node_id(rfh->GetFrameTreeNodeId());
  data->set_intent(FrameDeleteIntentionToProto(intent));
}

// Returns an experimental process shutdown delay if the SubframeShutdownDelay
// experiment is enabled, 0 if not or if under memory pressure. This is used to
// keep subframe processes alive for a few seconds in case they can be reused.
base::TimeDelta GetSubframeProcessShutdownDelay(
    BrowserContext* browser_context) {
  static constexpr base::TimeDelta kZeroDelay;
  if (!base::FeatureList::IsEnabled(features::kSubframeShutdownDelay))
    return kZeroDelay;

  // Don't delay process shutdown under memory pressure. Does not cancel
  // existing shutdown delays for processes already in delayed-shutdown state.
  const auto* const memory_monitor = base::MemoryPressureMonitor::Get();
  if (memory_monitor &&
      memory_monitor->GetCurrentPressureLevel() >=
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
    return kZeroDelay;
  }

  return base::Milliseconds(kSubframeProcessShutdownDelayInMSec);
}

// Returns the "document" URL used for a navigation, which might be different
// than the commit URL (CommonNavigationParam's URL) for certain cases such as
// error document and loadDataWithBaseURL() commits.
GURL GetLastDocumentURL(
    NavigationRequest* request,
    const mojom::DidCommitProvisionalLoadParams& params,
    bool last_document_is_error_document,
    const RenderFrameHostImpl::RendererURLInfo& renderer_url_info) {
  if (request->DidEncounterError() ||
      (request->IsSameDocument() && last_document_is_error_document)) {
    // If the navigation happens on an error document, the document URL is set
    // to kUnreachableWebDataURL. Note that if a same-document navigation
    // happens in an error document it's possible for the document URL to have
    // changed, but the browser has no way of knowing that URL since it isn't
    // exposed in any way. Additionally, all current known ways to do a
    // same-document navigation on an error document
    // (history.pushState/replaceState without changing the URL) won't change
    // the URL, so it's probably OK to keep using kUnreachableWebDataURL here.
    return GURL(kUnreachableWebDataURL);
  }
  if (request->IsLoadDataWithBaseURL()) {
    // loadDataWithBaseURL() navigation can set its own "base URL", which is
    // also used by the renderer as the document URL unless the navigation
    // failed (which is already accounted for in the error document case above).
    return request->common_params().base_url_for_data_url;
  }
  if (renderer_url_info.was_loaded_from_load_data_with_base_url &&
      request->IsSameDocument()) {
    // If this is a same-document navigation on a document loaded from
    // loadDataWithBaseURL(), it is not currently possible to figure out the
    // document URL. This is because the renderer can navigate to any
    // same-document URL, but that URL will not be used for
    // DidCommitProvisionalLoadParams' `url` if the loading URL for the document
    // is set to the data: URL. In this case, just return the last document URL,
    // since at least it will have the correct origin.
    return renderer_url_info.last_document_url;
  }
  // For all other navigations, the document URL should be the same as the URL
  // that is used to commit.
  return params.url;
}

bool IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled() {
  const bool is_feature_enabled = base::FeatureList::IsEnabled(
      features::kAvoidUnnecessaryBeforeUnloadCheckSync);
#if BUILDFLAG(IS_ANDROID)
  return is_feature_enabled &&
         GetContentClient()
             ->browser()
             ->SupportsAvoidUnnecessaryBeforeUnloadCheckSync();
#else
  return is_feature_enabled;
#endif
}

bool IsAvoidUnnecessaryBeforeUnloadCheckPostTaskEnabled() {
  // Only one of sync or posttask should be used. If both are set, use sync.
  return !IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled();
}

// Returns true if `host` has the Window Management permission granted.
bool IsWindowManagementGranted(RenderFrameHost* host) {
  content::PermissionController* permission_controller =
      host->GetBrowserContext()->GetPermissionController();
  DCHECK(permission_controller);

  return permission_controller->GetPermissionStatusForCurrentDocument(
             blink::PermissionType::WINDOW_MANAGEMENT, host) ==
         blink::mojom::PermissionStatus::GRANTED;
}

bool IsOpenGraphMetadataValid(const blink::mojom::OpenGraphMetadata* metadata) {
  return !metadata->image || metadata->image->SchemeIsHTTPOrHTTPS();
}

void ForwardOpenGraphMetadataIfValid(
    base::OnceCallback<void(blink::mojom::OpenGraphMetadataPtr)> callback,
    blink::mojom::OpenGraphMetadataPtr metadata) {
  if (IsOpenGraphMetadataValid(metadata.get()))
    std::move(callback).Run(std::move(metadata));
  else
    std::move(callback).Run({});
}

// Creates a JavaScriptExecuteRequestForTestsCallback callback that delegates
// to the given JavaScriptResultCallback.
blink::mojom::LocalFrame::JavaScriptExecuteRequestForTestsCallback
CreateJavaScriptExecuteRequestForTestsCallback(
    RenderFrameHost::JavaScriptResultCallback callback) {
  if (!callback)
    return base::NullCallback();
  return base::BindOnce(
      [](RenderFrameHost::JavaScriptResultCallback callback,
         blink::mojom::JavaScriptExecutionResultType type, base::Value value) {
        if (type == blink::mojom::JavaScriptExecutionResultType::kSuccess)
          std::move(callback).Run(value.Clone());
        else
          std::move(callback).Run(base::Value());
      },
      std::move(callback));
}

bool ValidateUnfencedTopNavigation(
    RenderFrameHostImpl* render_frame_host,
    GURL& url,
    int initiator_process_id,
    const scoped_refptr<network::ResourceRequestBody>& post_body,
    bool user_gesture) {
  // Validate and modify `url` as needed.
  render_frame_host->GetSiteInstance()->GetProcess()->FilterURL(
      /*empty_allowed=*/false, &url);

  // It should only be possible to send this IPC with this flag from an
  // opaque-ads fenced frame. Opaque-ads fenced frames should always
  // have the sandbox flag `allow-top-navigation-by-user-activation`.
  if ((render_frame_host->frame_tree_node()->GetDeprecatedFencedFrameMode() !=
       blink::FencedFrame::DeprecatedFencedFrameMode::kOpaqueAds) ||
      render_frame_host->IsSandboxed(
          network::mojom::WebSandboxFlags::kTopNavigationByUserActivation)) {
    // If we get the IPC elsewhere, assume the renderer is compromised.
    bad_message::ReceivedBadMessage(
        initiator_process_id,
        bad_message::RFHI_UNFENCED_TOP_IPC_OUTSIDE_FENCED_FRAME);
    return false;
  }

  // Perform checks that normally would be performed in
  // `blink::CanNavigate` but that we skipped because the target
  // frame wasn't available in the renderer.
  // TODO(crbug.com/1123606): Change these checks to send a BadMessage
  // when the renderer-side refactor is complete.

  // Javascript URLs are not allowed, because they can be used to
  // communicate from the fenced frame to the embedder.
  // TODO(crbug.com/1315802): It does not seem possible to reach this code
  // with an uncompromised renderer, because javascript URLs don't reach
  // the same IPC; instead they run inside the fenced frame as _self.
  // It also seems that Javascript URLs would be caught earlier in this
  // particular code path by VerifyOpenURLParams().
  // In this code's final IPC resting place after the factor, make sure
  // to check whether this code is redundant.
  if (url.SchemeIs(url::kJavaScriptScheme)) {
    render_frame_host->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kError,
        "The frame attempting navigation must be in the same fenced "
        "frame tree as the target if navigating to a javascript: url");
    return false;
  }

  // Blob URLs are not allowed, because you should not be able to exfiltrate
  // arbitrary amounts of information from a fenced frame.
  if (url.SchemeIs(url::kBlobScheme)) {
    render_frame_host->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kError,
        "_unfencedTop may not be used with a blob: url");
    return false;
  }

  // POST requests are not allowed, because they are asynchronous and more
  // difficult to account for in privacy budgets.
  if (post_body) {
    render_frame_host->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kError,
        "_unfencedTop may not be used to send POST requests");
    return false;
  }

  // User activation is required, because fenced frames use the sandbox
  // flag `allow-top-navigation-by-user-activation`.
  // It would be better to instead check
  // `render_frame_host->HasTransientUserActivation()`,
  // but it has already been consumed at this point.
  // TODO(crbug.com/848778): use the browser's source of truth for user
  // activation here (and elsewhere in this file) rather than trust the
  // renderer.
  if (!user_gesture) {
    render_frame_host->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kError,
        "The frame attempting navigation of the top-level window is "
        "sandboxed with the 'allow-top-navigation-by-user-activation' "
        "flag, but has no user activation (aka gesture). See "
        "https://www.chromestatus.com/feature/5629582019395584.");
    return false;
  }

  return true;
}

// Records the identifiable surface metric associated with a document created
// event when the identifiability study is active.
void RecordIdentifiabilityDocumentCreatedMetrics(
    const ukm::SourceId document_ukm_source_id,
    ukm::UkmRecorder* ukm_recorder,
    ukm::SourceId navigation_source_id,
    bool is_cross_origin_frame,
    bool is_cross_site_frame,
    bool is_main_frame) {
  if (blink::IdentifiabilityStudySettings::Get()->IsActive()) {
    blink::IdentifiabilityStudyDocumentCreated(document_ukm_source_id)
        .SetNavigationSourceId(navigation_source_id)
        .SetIsMainFrame(is_main_frame)
        .SetIsCrossOriginFrame(is_cross_origin_frame)
        .SetIsCrossSiteFrame(is_cross_site_frame)
        .Record(ukm_recorder);
  }
}

bool PopupInheritCOOP(const RenderFrameHostImpl* opener) {
  return opener->GetLastCommittedOrigin() ==
         opener->GetMainFrame()->GetLastCommittedOrigin();
}

// See https://html.spec.whatwg.org/C/#browsing-context-names (step 8)
// ```
// If current's top-level browsing context's active document's
// cross-origin opener policy's value is "same-origin" or
// "same-origin-plus-COEP", then [...] set noopener to true, name to
// "_blank", and windowType to "new with no opener".
// ```
bool CoopSuppressOpener(const RenderFrameHostImpl* opener) {
  // Those values are explicitly listed here, to force creator of new
  // values to make an explicit decision in the future.
  // See regression: https://crbug.com/1181673
  switch (opener->GetMainFrame()->cross_origin_opener_policy().value) {
    case network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone:
    case network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups:
      return false;

    // TODO(https://crbug.com/1385827): Ideally, we'd like to have support
    // for cross-origin iframes in COOP: restrict-properties pages opening
    // popups. This is somewhat complex, because it would break some COOP
    // invariants and other changes need to happen first. See the bug for
    // details. For now, set no-opener.
    case network::mojom::CrossOriginOpenerPolicyValue::kRestrictProperties:
    case network::mojom::CrossOriginOpenerPolicyValue::
        kRestrictPropertiesPlusCoep:

    case network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin:
    case network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep:
      return !PopupInheritCOOP(opener);
  }
}

}  // namespace

class RenderFrameHostImpl::SubresourceLoaderFactoriesConfig {
 public:
  static SubresourceLoaderFactoriesConfig ForLastCommittedNavigation(
      RenderFrameHostImpl& frame) {
    SubresourceLoaderFactoriesConfig result;
    result.origin_ = frame.GetLastCommittedOrigin();
    result.isolation_info_ = frame.GetIsolationInfoForSubresources();
    result.client_security_state_ = frame.BuildClientSecurityState();
    if (frame.coep_reporter_) {
      frame.coep_reporter_->Clone(
          result.coep_reporter_.BindNewPipeAndPassReceiver());
    }
    result.trust_token_redemption_policy_ =
        DetermineAfterCommitWhetherToForbidTrustTokenOperation(
            frame, network::mojom::TrustTokenOperationType::kRedemption);
    result.trust_token_issuance_policy_ =
        DetermineAfterCommitWhetherToForbidTrustTokenOperation(
            frame, network::mojom::TrustTokenOperationType::kIssuance);
    result.ukm_source_id_ =
        ukm::SourceIdObj::FromInt64(frame.GetPageUkmSourceId());

    if (frame.GetIsThirdPartyCookiesUserBypassEnabled()) {
      result.cookie_setting_overrides_.Put(
          net::CookieSettingOverride::kForceThirdPartyByUser);
    }

    return result;
  }

  static SubresourceLoaderFactoriesConfig ForPendingNavigation(
      NavigationRequest& navigation_request) {
    SubresourceLoaderFactoriesConfig result;
#if BUILDFLAG(IS_OHOS_PRPP)
    result.main_url_ = navigation_request.common_params().url;
    result.addr_web_handle_ = navigation_request.GetAddrWebHandle();
#endif
    result.origin_ = navigation_request.GetOriginToCommit().value();
    result.client_security_state_ =
        navigation_request.BuildClientSecurityState();
    result.ukm_source_id_ = ukm::SourceIdObj::FromInt64(
        navigation_request.GetNextPageUkmSourceId());

    // TODO(lukasza): Consider pushing the ok-vs-error differentiation into
    // NavigationRequest methods (e.g. into |isolation_info_for_subresources|
    // and/or |coep_reporter| methods).
    if (navigation_request.DidEncounterError()) {
      // Error frames gets locked down `isolation_info_`,
      // `trust_token_redemption_policy_` and `trust_token_issuance_policy_`
      // plus an empty/uninitialized `coep_reporter_`.
      result.isolation_info_ = net::IsolationInfo::CreateTransient();
      result.trust_token_redemption_policy_ =
          network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
      result.trust_token_issuance_policy_ =
          network::mojom::TrustTokenOperationPolicyVerdict::kForbid;
    } else {
      result.isolation_info_ =
          navigation_request.isolation_info_for_subresources();
      if (navigation_request.coep_reporter()) {
        navigation_request.coep_reporter()->Clone(
            result.coep_reporter_.BindNewPipeAndPassReceiver());
      }
      result.trust_token_redemption_policy_ =
          DetermineWhetherToForbidTrustTokenOperation(
              navigation_request.GetRenderFrameHost(),
              navigation_request.commit_params(), result.origin(),
              navigation_request.ComputeDeprecatedFencedFrameMode(),
              network::mojom::TrustTokenOperationType::kRedemption);
      result.trust_token_issuance_policy_ =
          DetermineWhetherToForbidTrustTokenOperation(
              navigation_request.GetRenderFrameHost(),
              navigation_request.commit_params(), result.origin(),
              navigation_request.ComputeDeprecatedFencedFrameMode(),
              network::mojom::TrustTokenOperationType::kIssuance);

      if (navigation_request.GetIsThirdPartyCookiesUserBypassEnabled()) {
        result.cookie_setting_overrides_.Put(
            net::CookieSettingOverride::kForceThirdPartyByUser);
      }
    }

    return result;
  }

  // ForPendingOrLastCommittedNavigation is useful in scenarios where there is
  // no coordination between the timing of 1) a navigation commit and 2)
  // subresource loader factories bundle creation.  For example, using
  // ForPendingOrLastCommittedNavigation from UpdateSubresourceLoaderFactories
  // leads to using the correct SubresourceLoaderFactoriesConfig regardless of
  // the timing of when a NetworkService crash triggers a call to
  // UpdateSubresourceLoaderFactories:
  // 1. If the crash happens when there is an in-flight Commit IPC to the
  //    renderer process, then the newly created subresource loader factories
  //    will arrive at the renderer *after* the Commit IPC and therefore the
  //    factories need to use the configuration (e.g. the origin) based on the
  //    pending navigation.
  // 2. OTOH, if the crash happens when there is no in-flight Commit IPC then
  //    the newly created factories should use the configuration based on the
  //    last committed navigation.
  //
  // TODO(https://crbug.com/729021): ForPendingOrLastCommittedNavigation might
  // not be needed once we have RenderDocumentHost (e.g. we swap on every
  // cross-document navigation), because with RenderDocumentHost there is no
  // risk of sending last-commited-navigation-based subresource loaders to a
  // document different from the last-committed one.
  static SubresourceLoaderFactoriesConfig ForPendingOrLastCommittedNavigation(
      RenderFrameHostImpl& frame) {
    NavigationRequest* navigation_request =
        frame.FindLatestNavigationRequestThatIsStillCommitting();
    return navigation_request ? ForPendingNavigation(*navigation_request)
                              : ForLastCommittedNavigation(frame);
  }

  ~SubresourceLoaderFactoriesConfig() = default;

  SubresourceLoaderFactoriesConfig(SubresourceLoaderFactoriesConfig&&) =
      default;
  SubresourceLoaderFactoriesConfig& operator=(
      SubresourceLoaderFactoriesConfig&&) = default;

  SubresourceLoaderFactoriesConfig(const SubresourceLoaderFactoriesConfig&) =
      delete;
  SubresourceLoaderFactoriesConfig& operator=(
      const SubresourceLoaderFactoriesConfig&) = delete;

  const url::Origin& origin() const { return origin_; }
#if BUILDFLAG(IS_OHOS_PRPP)
  const GURL& main_url() const { return main_url_; }
  uint64_t addr_web_handle() const { return addr_web_handle_; }
#endif
  const net::IsolationInfo& isolation_info() const { return isolation_info_; }

  network::mojom::ClientSecurityStatePtr GetClientSecurityState() const {
    return mojo::Clone(client_security_state_);
  }

  mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
  GetCoepReporter() const {
    mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> p;
    if (coep_reporter_) {
      coep_reporter_->Clone(p.InitWithNewPipeAndPassReceiver());
    }
    return p;
  }

  const network::mojom::TrustTokenOperationPolicyVerdict&
  trust_token_redemption_policy() const {
    return trust_token_redemption_policy_;
  }

  const network::mojom::TrustTokenOperationPolicyVerdict&
  trust_token_issuance_policy() const {
    return trust_token_issuance_policy_;
  }

  const ukm::SourceIdObj& ukm_source_id() const { return ukm_source_id_; }

  const net::CookieSettingOverrides& cookie_setting_overrides() const {
    return cookie_setting_overrides_;
  }

 private:
  // Private constructor - please go through the static For... methods.
  SubresourceLoaderFactoriesConfig() = default;

#if BUILDFLAG(IS_OHOS_PRPP)
  GURL main_url_;
  uint64_t addr_web_handle_;
#endif
  url::Origin origin_;
  net::IsolationInfo isolation_info_;
  network::mojom::ClientSecurityStatePtr client_security_state_;
  mojo::Remote<network::mojom::CrossOriginEmbedderPolicyReporter>
      coep_reporter_;
  network::mojom::TrustTokenOperationPolicyVerdict trust_token_issuance_policy_;
  network::mojom::TrustTokenOperationPolicyVerdict
      trust_token_redemption_policy_;
  ukm::SourceIdObj ukm_source_id_;
  net::CookieSettingOverrides cookie_setting_overrides_;
};

struct PendingNavigation {
  blink::mojom::CommonNavigationParamsPtr common_params;
  blink::mojom::BeginNavigationParamsPtr begin_navigation_params;
  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client;
  mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
      renderer_cancellation_listener;

  PendingNavigation(
      blink::mojom::CommonNavigationParamsPtr common_params,
      blink::mojom::BeginNavigationParamsPtr begin_navigation_params,
      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
      mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
      mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
          renderer_cancellation_listener);
};

PendingNavigation::PendingNavigation(
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::BeginNavigationParamsPtr begin_navigation_params,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
    mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
    mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
        renderer_cancellation_listener)
    : common_params(std::move(common_params)),
      begin_navigation_params(std::move(begin_navigation_params)),
      blob_url_loader_factory(std::move(blob_url_loader_factory)),
      navigation_client(std::move(navigation_client)),
      renderer_cancellation_listener(
          std::move(renderer_cancellation_listener)) {}

// static
RenderFrameHost* RenderFrameHost::FromID(const GlobalRenderFrameHostId& id) {
  return RenderFrameHostImpl::FromID(id);
}

// static
RenderFrameHost* RenderFrameHost::FromID(int render_process_id,
                                         int render_frame_id) {
  return RenderFrameHostImpl::FromID(
      GlobalRenderFrameHostId(render_process_id, render_frame_id));
}

// static
RenderFrameHost* RenderFrameHost::FromFrameToken(
    int process_id,
    const blink::LocalFrameToken& token) {
  return RenderFrameHostImpl::FromFrameToken(process_id, token);
}

// static
void RenderFrameHost::AllowInjectingJavaScript() {
  g_allow_injecting_javascript = true;
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromID(GlobalRenderFrameHostId id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer();
  auto it = frames->find(id);
  return it == frames->end() ? nullptr : it->second;
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromID(int render_process_id,
                                                 int render_frame_id) {
  return RenderFrameHostImpl::FromID(
      GlobalRenderFrameHostId(render_process_id, render_frame_id));
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromFrameToken(
    int process_id,
    const blink::LocalFrameToken& frame_token,
    mojo::ReportBadMessageCallback* process_mismatch_callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto it = g_token_frame_map.Get().find(frame_token);
  if (it == g_token_frame_map.Get().end())
    return nullptr;

  if (it->second->GetProcess()->GetID() != process_id) {
    if (process_mismatch_callback) {
      SYSLOG(WARNING)
          << "Denying illegal RenderFrameHost::FromFrameToken request.";
      std::move(*process_mismatch_callback)
          .Run(
              "Unknown LocalFrame made RenderFrameHost::FromFrameToken "
              "request.");
    }
    return nullptr;
  }

  return it->second;
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromDocumentToken(
    int process_id,
    const blink::DocumentToken& document_token,
    mojo::ReportBadMessageCallback* process_mismatch_callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  const auto it = GetDocumentTokenMap().find(document_token);
  if (it == GetDocumentTokenMap().end())
    return nullptr;

  if (it->second->GetProcess()->GetID() != process_id) {
    if (process_mismatch_callback) {
      SYSLOG(WARNING)
          << "Denying illegal RenderFrameHost::FromDocumentToken request.";
      std::move(*process_mismatch_callback)
          .Run("process ID does not match requested DocumentToken");
    }
    return nullptr;
  }

  return it->second;
}

// static
RenderFrameHost* RenderFrameHost::FromAXTreeID(const ui::AXTreeID& ax_tree_id) {
  return RenderFrameHostImpl::FromAXTreeID(ax_tree_id);
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromAXTreeID(
    ui::AXTreeID ax_tree_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  ui::AXActionHandlerRegistry::FrameID frame_id =
      ui::AXActionHandlerRegistry::GetInstance()->GetFrameID(ax_tree_id);
  return RenderFrameHostImpl::FromID(frame_id.first, frame_id.second);
}

// static
RenderFrameHostImpl* RenderFrameHostImpl::FromOverlayRoutingToken(
    const base::UnguessableToken& token) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto it = g_token_frame_map.Get().find(blink::LocalFrameToken(token));
  return it == g_token_frame_map.Get().end() ? nullptr : it->second;
}

// static
void RenderFrameHostImpl::ClearAllPrefetchedSignedExchangeCache() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer();
  for (auto it : *frames)
    it.second->ClearPrefetchedSignedExchangeCache();
}

// TODO(crbug.com/1213818): Get/SetCodeCacheHostReceiverHandler are used only
// for a test in content/browser/service_worker/service_worker_browsertest
// that tests a bad message is returned on an incorrect origin. Try to find a
// way to test this without adding these additional methods.
RenderFrameHostImpl::CodeCacheHostReceiverHandler&
GetCodeCacheHostReceiverHandler() {
  static base::NoDestructor<RenderFrameHostImpl::CodeCacheHostReceiverHandler>
      instance;
  return *instance;
}

// static
void RenderFrameHostImpl::SetCodeCacheHostReceiverHandlerForTesting(
    CodeCacheHostReceiverHandler handler) {
  GetCodeCacheHostReceiverHandler() = handler;
}

// static
const char* RenderFrameHostImpl::LifecycleStateImplToString(
    RenderFrameHostImpl::LifecycleStateImpl state) {
  using LifecycleStateImpl = RenderFrameHostImpl::LifecycleStateImpl;
  switch (state) {
    case LifecycleStateImpl::kSpeculative:
      return "Speculative";
    case LifecycleStateImpl::kPrerendering:
      return "Prerendering";
    case LifecycleStateImpl::kPendingCommit:
      return "PendingCommit";
    case LifecycleStateImpl::kActive:
      return "Active";
    case LifecycleStateImpl::kInBackForwardCache:
      return "InBackForwardCache";
    case LifecycleStateImpl::kRunningUnloadHandlers:
      return "RunningUnloadHandlers";
    case LifecycleStateImpl::kReadyToBeDeleted:
      return "ReadyToBeDeleted";
  }
}

RenderFrameHostImpl::RenderFrameHostImpl(
    SiteInstance* site_instance,
    scoped_refptr<RenderViewHostImpl> render_view_host,
    RenderFrameHostDelegate* delegate,
    FrameTree* frame_tree,
    FrameTreeNode* frame_tree_node,
    int32_t routing_id,
    mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
    const blink::LocalFrameToken& frame_token,
    const blink::DocumentToken& document_token,
    base::UnguessableToken devtools_frame_token,
    bool renderer_initiated_creation_of_main_frame,
    LifecycleStateImpl lifecycle_state,
    scoped_refptr<BrowsingContextState> browsing_context_state,
    blink::FrameOwnerElementType frame_owner_element_type,
    RenderFrameHostImpl* parent,
    FencedFrameStatus fenced_frame_status)
    : render_view_host_(std::move(render_view_host)),
      delegate_(delegate),
      site_instance_(static_cast<SiteInstanceImpl*>(site_instance)),
      agent_scheduling_group_(
          site_instance_->GetOrCreateAgentSchedulingGroup().GetSafeRef()),
      frame_tree_(frame_tree),
      frame_tree_node_(frame_tree_node),
      owner_(frame_tree_node),
      browsing_context_state_(std::move(browsing_context_state)),
      frame_owner_element_type_(frame_owner_element_type),
      parent_(parent),
      depth_(parent_ ? parent_->GetFrameDepth() + 1 : 0),
      last_committed_site_info_(site_instance_->GetBrowserContext()),
      routing_id_(routing_id),
      beforeunload_timeout_delay_(kUnloadTimeout),
      frame_(std::move(frame_remote)),
      waiting_for_init_(renderer_initiated_creation_of_main_frame),
      frame_token_(frame_token),
      keep_alive_handle_factory_(
          agent_scheduling_group_->GetProcess(),
          RenderProcessHostImpl::kKeepAliveHandleFactoryTimeout),
      subframe_unload_timeout_(kUnloadTimeout),
      media_device_id_salt_base_(
          BrowserContext::CreateRandomMediaDeviceIDSalt()),
      document_associated_data_(absl::in_place, *this, document_token),
      lifecycle_state_(lifecycle_state),
      inner_tree_main_frame_tree_node_id_(
          FrameTreeNode::kFrameTreeNodeInvalidId),
      code_cache_host_receivers_(
          GetProcess()->GetStoragePartition()->GetGeneratedCodeCacheContext()),
      fenced_frame_status_(fenced_frame_status),
      devtools_frame_token_(devtools_frame_token) {
  TRACE_EVENT_BEGIN("navigation", "RenderFrameHostImpl",
                    perfetto::Track::FromPointer(this),
                    "render_frame_host_when_created", this);
  // Update lifecycle state on track of RenderFrameHostImpl.
  TRACE_EVENT_BEGIN(
      "navigation",
      perfetto::StaticString{LifecycleStateImplToString(lifecycle_state_)},
      perfetto::Track::FromPointer(this));

  DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
  DCHECK(delegate_);
  DCHECK(lifecycle_state_ == LifecycleStateImpl::kSpeculative ||
         lifecycle_state_ == LifecycleStateImpl::kPrerendering ||
         lifecycle_state_ == LifecycleStateImpl::kActive);
  // Only main frames have `waiting_for_init_` set.
  DCHECK(!waiting_for_init_ || !parent_);

  GetAgentSchedulingGroup().AddRoute(routing_id_, this);
  g_routing_id_frame_map.Get().emplace(
      GlobalRenderFrameHostId(GetProcess()->GetID(), routing_id_), this);
  g_token_frame_map.Get().insert(std::make_pair(frame_token_, this));
  site_instance_->group()->AddObserver(this);
  auto* process = GetProcess();
  process->RegisterRenderFrameHost(GetGlobalId());
  GetSiteInstance()->group()->IncrementActiveFrameCount();

  if (parent_) {
    // All frames in a frame tree should use the same storage partition.
    CHECK_EQ(parent_->GetStoragePartition(), GetStoragePartition());

    // New child frames should inherit the nav_entry_id of their parent.
    set_nav_entry_id(parent_->nav_entry_id());
  }

  if (frame_tree_->is_prerendering()) {
    // TODO(https://crbug.com/1132752): Check the prerendering page is
    // same-origin to the prerender trigger page.
    mojo_binder_policy_applier_ =
        MojoBinderPolicyApplier::CreateForSameOriginPrerendering(base::BindOnce(
            &RenderFrameHostImpl::CancelPrerenderingByMojoBinderPolicy,
            base::Unretained(this)));
    broker_.ApplyMojoBinderPolicies(mojo_binder_policy_applier_.get());
  }

  if (lifecycle_state_ != LifecycleStateImpl::kSpeculative) {
    // Creating a RFH in kActive state implies that it is the RFH for a
    // newly-created FTN, which should still be on its initial empty document.
    DCHECK(frame_tree_node_->is_on_initial_empty_document());
  }

  InitializePolicyContainerHost(renderer_initiated_creation_of_main_frame);

  InitializeLocalNetworkRequestPolicy();

  unload_event_monitor_timeout_ =
      std::make_unique<TimeoutMonitor>(base::BindRepeating(
          &RenderFrameHostImpl::OnUnloaded, weak_ptr_factory_.GetWeakPtr()));
  beforeunload_timeout_ = std::make_unique<TimeoutMonitor>(
      base::BindRepeating(&RenderFrameHostImpl::BeforeUnloadTimeout,
                          weak_ptr_factory_.GetWeakPtr()));

  // Local roots are:
  // - main frames; or
  // - subframes that use a proxy to talk to their parent.
  //
  // Local roots require a RenderWidget for input/layout/painting.
  // Note: We cannot use is_local_root() here because this block sets up the
  // fields that are used by that method.
  const bool setup_local_render_widget_host =
      is_main_frame() || RequiresProxyToParent();
  if (setup_local_render_widget_host) {
    if (is_main_frame()) {
      // For main frames, the RenderWidgetHost is owned by the RenderViewHost.
      // TODO(https://crbug.com/545684): Once RenderViewHostImpl has-a
      // RenderWidgetHostImpl, the main render frame should probably start
      // owning the RenderWidgetHostImpl itself.
      DCHECK(GetLocalRenderWidgetHost());
    } else {
      // For local child roots, the RenderFrameHost directly creates and owns
      // its RenderWidgetHost.
      int32_t widget_routing_id =
          site_instance->GetProcess()->GetNextRoutingID();
      DCHECK_EQ(nullptr, GetLocalRenderWidgetHost());
      owned_render_widget_host_ = RenderWidgetHostFactory::Create(
          frame_tree_, frame_tree_->render_widget_delegate(),
          site_instance_->group()->GetSafeRef(), widget_routing_id,
          /*hidden=*/true,
          /*renderer_initiated_creation=*/false);
    }

    if (is_main_frame())
      GetLocalRenderWidgetHost()->SetIntersectsViewport(true);
    GetLocalRenderWidgetHost()->SetFrameDepth(depth_);
  }
  // Verify is_local_root() now indicates whether this frame is a local root or
  // not. It is safe to use this method anywhere beyond this point.
  DCHECK_EQ(setup_local_render_widget_host, is_local_root());
  ResetPermissionsPolicy();

  // New RenderFrameHostImpl are put in their own virtual browsing context
  // group. Then, they can inherit from:
  // 1) Their opener in RenderFrameHostImpl::CreateNewWindow().
  // 2) Their navigation in RenderFrameHostImpl::DidCommitNavigationInternal().
  virtual_browsing_context_group_ = CrossOriginOpenerPolicyAccessReportManager::
      NextVirtualBrowsingContextGroup();
  soap_by_default_virtual_browsing_context_group_ =
      CrossOriginOpenerPolicyAccessReportManager::
          NextVirtualBrowsingContextGroup();

  // IdleManager should be unique per RenderFrame to provide proper isolation
  // of overrides.
  idle_manager_ = std::make_unique<IdleManagerImpl>(this);
}

RenderFrameHostImpl::~RenderFrameHostImpl() {
  SCOPED_CRASH_KEY_STRING256("Bug1407526", "lifecycle",
                             LifecycleStateImplToString(lifecycle_state()));
  TRACE_EVENT("navigation", "~RenderFrameHostImpl()",
              ChromeTrackEvent::kRenderFrameHost, this);
  // See https://crbug.com/1276535
  if (check_deletion_for_bug_1276535_) {
    base::debug::DumpWithoutCrashing();
  }

  // The lifetime of this object has ended, so remove it from the id map before
  // calling any delegates/observers, so that any calls to |FromID| no longer
  // return |this|.
  g_routing_id_frame_map.Get().erase(
      GlobalRenderFrameHostId(GetProcess()->GetID(), routing_id_));

  // Remove this object from the isolatable sandboxed iframe set as well, if
  // necessary.
  g_routing_id_isolatable_sandboxed_iframes_set.Get().erase(GetGlobalId());

  // When a RenderFrameHostImpl is deleted, it may still contain children. This
  // can happen with the unload timer. It causes a RenderFrameHost to delete
  // itself even if it is still waiting for its children to complete their
  // unload handlers.
  //
  // Observers expect children to be deleted first. Do it now before notifying
  // them.
  ResetChildren();

  // Destroying NavigationRequests may call into delegates/observers,
  // so we do it early while |this| object is still in a sane state.
  ResetOwnedNavigationRequests(
      NavigationDiscardReason::kRenderFrameHostDestruction);

  // Release the WebUI instances before all else as the WebUI may accesses the
  // RenderFrameHost during cleanup.
  base::WeakPtr<RenderFrameHostImpl> self = GetWeakPtr();
  ClearWebUI();
  // `ClearWebUI()` may indirectly call content's embedders and delete this.
  // There are no known occurrences of it, so we assume this never happen and
  // crash immediately if it does, because there are no easy ways to recover.
  CHECK(self);

  SetLastCommittedSiteInfo(UrlInfo());

  g_token_frame_map.Get().erase(frame_token_);

  auto* process = GetProcess();
  SCOPED_CRASH_KEY_BOOL("Bug1407526", "si_exists", !!site_instance_);
  SCOPED_CRASH_KEY_BOOL("Bug1407526", "sig_exists", !!site_instance_->group());
  SCOPED_CRASH_KEY_BOOL("Bug1407526", "process_exists", !!process);
  site_instance_->group()->RemoveObserver(this);
  process->UnregisterRenderFrameHost(GetGlobalId());

  const bool was_created = is_render_frame_created();
  SCOPED_CRASH_KEY_BOOL("Bug1407526", "was_created", !!was_created);
  SCOPED_CRASH_KEY_BOOL("Bug1407526", "delegate_exists", !!delegate_);
  render_frame_state_ = RenderFrameState::kDeleted;
  if (was_created)
    delegate_->RenderFrameDeleted(this);

  // Ensure that the render process host has been notified that all audio
  // streams from this frame have terminated. This is required to ensure the
  // process host has the correct media stream count, which affects its
  // background priority.
  if (is_audible_) {
    OnAudibleStateChanged(false);
  }

  // Resetting `document_associated_data_` destroys live `DocumentService` and
  // `DocumentUserData` instances. It is important for them to be
  // destroyed before the body of the `RenderFrameHostImpl` destructor
  // completes. Among other things, this ensures that any `SafeRef`s from
  // `DocumentService` and `RenderFrameHostUserData` subclasses are still valid
  // when their destructors run.
  document_associated_data_.reset();

  // If this was the last active frame in the SiteInstanceGroup, the
  // DecrementActiveFrameCount call will trigger the deletion of the
  // SiteInstanceGroup's proxies.
  GetSiteInstance()->group()->DecrementActiveFrameCount();

  // Once a RenderFrame is created in the renderer, there are three possible
  // clean-up paths:
  // 1. The RenderFrame can be the main frame. In this case, closing the
  //    associated `blink::WebView` will clean up the resources associated with
  //    the main RenderFrame.
  // 2. The RenderFrame can be unloaded. In this case, the browser sends a
  //    mojom::FrameNavigationControl::UnloadFrame message for the RenderFrame
  //    to replace itself with a `blink::RemoteFrame`and release its associated
  //    resources. |lifecycle_state_| is advanced to
  //    LifecycleStateImpl::kRunningUnloadHandlers to track that this IPC is in
  //    flight.
  // 3. The RenderFrame can be detached, as part of removing a subtree (due to
  //    navigation, unload, or DOM mutation). In this case, the browser sends
  //    a mojom::FrameNavigationControl::Delete message for the RenderFrame
  //    to detach itself and release its associated resources. If the subframe
  //    contains an unload handler, |lifecycle_state_| is advanced to
  //    LifecycleStateImpl::kRunningUnloadHandlers to track that the detach is
  //    in progress; otherwise, it is advanced directly to
  //    LifecycleStateImpl::kReadyToBeDeleted.
  //
  // For BackForwardCache or Prerender case:
  //
  // Deleting the BackForwardCache::Entry deletes immediately all the
  // Render{View,Frame,FrameProxy}Host. This will destroy the main RenderFrame
  // eventually as part of path #1 above:
  //
  // - The RenderFrameHost/RenderFrameProxyHost of the main frame are owned by
  //   the BackForwardCache::Entry.
  // - RenderFrameHost/RenderFrameProxyHost for sub-frames are owned by their
  //   parent RenderFrameHost.
  // - The RenderViewHost(s) are refcounted by the
  //   RenderFrameHost/RenderFrameProxyHost of the page. They are guaranteed not
  //   to be referenced by any other pages.
  //
  // The browser side gives the renderer a small timeout to finish processing
  // unload / detach messages. When the timeout expires, the RFH will be
  // removed regardless of whether or not the renderer acknowledged that it
  // completed the work, to avoid indefinitely leaking browser-side state. To
  // avoid leaks, ~RenderFrameHostImpl still validates that the appropriate
  // cleanup IPC was sent to the renderer, by checking IsPendingDeletion().
  //
  // TODO(dcheng): Due to how frame detach is signalled today, there are some
  // bugs in this area. In particular, subtree detach is reported from the
  // bottom up, so the replicated mojom::FrameNavigationControl::Delete
  // messages actually operate on a node-by-node basis rather than detaching an
  // entire subtree at once...
  //
  // Note that this logic is fairly subtle. It needs to include all subframes
  // and all speculative frames, but it should exclude case #1 (a main
  // RenderFrame owned by the `blink::WebView`). It can't simply check
  // |frame_tree_node_->render_manager()->speculative_frame_host()| for
  // equality against |this|. The speculative frame host is unset before the
  // speculative frame host is destroyed, so this condition would never be
  // matched for a speculative RFH that needs to be destroyed.
  //
  // Directly comparing against
  // |RenderViewHostImpl::GetMainRenderFrameHost()| still has one
  // additional subtlety though: |GetMainRenderFrameHost()| can sometimes
  // return a speculative RFH! For subframes, this obviously does not matter: a
  // subframe will always pass the condition
  // |render_view_host_->GetMainRenderFrameHost() != this|. However, it
  // turns out that a speculative main frame being deleted will *always* pass
  // this condition as well: a speculative RFH being deleted will *always* first
  // be unassociated from its corresponding RFHM. Thus, it follows that
  // |GetMainRenderFrameHost()| will never return the speculative main
  // frame being deleted, since it must have already been unset.
  if (was_created && render_view_host_->GetMainRenderFrameHost() != this) {
    CHECK(IsPendingDeletion() || IsInBackForwardCache() ||
          lifecycle_state() == LifecycleStateImpl::kPrerendering ||
          lifecycle_state() == LifecycleStateImpl::kSpeculative)
        << lifecycle_state();
  }

  GetAgentSchedulingGroup().RemoveRoute(routing_id_);

  // Null out the unload timer; in crash dumps this member will be null only if
  // the dtor has run.  (It may also be null in tests.)
  unload_event_monitor_timeout_.reset();

  // Delete this before destroying the widget, to guard against reentrancy
  // by in-process screen readers such as JAWS.
  browser_accessibility_manager_.reset();

  // Note: The RenderWidgetHost of the main frame is owned by the RenderViewHost
  // instead. In this case the RenderViewHost is responsible for shutting down
  // its RenderViewHost.
  if (owned_render_widget_host_)
    owned_render_widget_host_->ShutdownAndDestroyWidget(false);

  ResourceCacheManager* resource_cache_manager =
      GetStoragePartition()->GetResourceCacheManager();
  if (resource_cache_manager) {
    resource_cache_manager->RenderFrameHostBecameIneligible(*this);
  }

  render_view_host_.reset();

  // If another frame is waiting for a beforeunload completion callback from
  // this frame, simulate it now.
  RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
  if (beforeunload_initiator && beforeunload_initiator != this) {
    base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
    beforeunload_initiator->ProcessBeforeUnloadCompletedFromFrame(
        /*proceed=*/true, /*treat_as_final_completion_callback=*/false, this,
        /*is_frame_being_destroyed=*/true, approx_renderer_start_time,
        base::TimeTicks::Now(), /*for_legacy=*/false);
  }

  if (prefetched_signed_exchange_cache_)
    prefetched_signed_exchange_cache_->RecordHistograms();

  // Matches the pair of TRACE_EVENT_BEGINS in the constructor: one for
  // "RenderFrameHostImpl" slice itself, one for the slice with the lifecycle
  // state name.
  TRACE_EVENT_END("navigation", perfetto::Track::FromPointer(this));
  TRACE_EVENT_END("navigation", perfetto::Track::FromPointer(this));
}

int RenderFrameHostImpl::GetRoutingID() const {
  return routing_id_;
}

const blink::LocalFrameToken& RenderFrameHostImpl::GetFrameToken() const {
  return frame_token_;
}

const base::UnguessableToken& RenderFrameHostImpl::GetReportingSource() {
  DCHECK(!document_associated_data_->reporting_source().is_empty());
  return document_associated_data_->reporting_source();
}

ui::AXTreeID RenderFrameHostImpl::GetAXTreeID() {
  return ax_tree_id();
}

const blink::LocalFrameToken& RenderFrameHostImpl::GetTopFrameToken() {
  RenderFrameHostImpl* frame = this;
  while (frame->parent_) {
    frame = frame->parent_;
  }
  return frame->GetFrameToken();
}

void RenderFrameHostImpl::AudioContextPlaybackStarted(int audio_context_id) {
  delegate_->AudioContextPlaybackStarted(this, audio_context_id);
}

void RenderFrameHostImpl::AudioContextPlaybackStopped(int audio_context_id) {
  delegate_->AudioContextPlaybackStopped(this, audio_context_id);
}

// The current frame went into the BackForwardCache.
void RenderFrameHostImpl::DidEnterBackForwardCache() {
  TRACE_EVENT0("navigation", "RenderFrameHostImpl::EnterBackForwardCache");
  DCHECK(IsBackForwardCacheEnabled());
  DCHECK(IsInPrimaryMainFrame());

  DidEnterBackForwardCacheInternal();
  // Pages in the back-forward cache are automatically evicted after a certain
  // time.
  StartBackForwardCacheEvictionTimer();

  for (FrameTreeNode* node : FrameTree::SubtreeAndInnerTreeNodes(
           this,
           /*include_delegate_nodes_for_inner_frame_trees=*/true)) {
    if (RenderFrameHostImpl* rfh = node->current_frame_host())
      rfh->DidEnterBackForwardCacheInternal();
  }
}

void RenderFrameHostImpl::DidEnterBackForwardCacheInternal() {
  DCHECK_EQ(lifecycle_state(), LifecycleStateImpl::kActive);
  SetLifecycleState(LifecycleStateImpl::kInBackForwardCache);

  // If we are a delegate node we don't need to do anything else.
  if (inner_tree_main_frame_tree_node_id_ !=
      FrameTreeNode::kFrameTreeNodeInvalidId) {
    return;
  }

  for (auto& entry : service_worker_container_hosts_) {
    if (base::WeakPtr<ServiceWorkerContainerHost> host = entry.second)
      host->OnEnterBackForwardCache();
  }

  DedicatedWorkerHostsForDocument::GetOrCreateForCurrentDocument(this)
      ->OnEnterBackForwardCache();
#if BUILDFLAG(IS_P2P_ENABLED)
  GetProcess()->PauseSocketManagerForRenderFrameHost(GetGlobalId());
#endif  // BUILDFLAG(IS_P2P_ENABLED)

#if BUILDFLAG(IS_OHOS)
  if (delegate_) {
    delegate_->OnRenderFrameHostEnterBackForwardCache(GetGlobalId());
  }
#endif  // BUILDFLAG(IS_OHOS)

  if (auto* permission_service_context =
          PermissionServiceContext::GetForCurrentDocument(this)) {
    permission_service_context->StoreStatusAtBFCacheEntry();
  }
}

// The frame as been restored from the BackForwardCache.
void RenderFrameHostImpl::WillLeaveBackForwardCache() {
  TRACE_EVENT0("navigation", "RenderFrameHostImpl::LeaveBackForwardCache");
  DCHECK(IsBackForwardCacheEnabled());
  DCHECK(IsOutermostMainFrame());
  if (back_forward_cache_eviction_timer_.IsRunning())
    back_forward_cache_eviction_timer_.Stop();

  WillLeaveBackForwardCacheInternal();
  for (FrameTreeNode* node : FrameTree::SubtreeAndInnerTreeNodes(
           this,
           /*include_delegate_nodes_for_inner_frame_trees=*/true)) {
    if (RenderFrameHostImpl* rfh = node->current_frame_host())
      rfh->WillLeaveBackForwardCacheInternal();
  }
}

void RenderFrameHostImpl::WillLeaveBackForwardCacheInternal() {
  DCHECK_EQ(lifecycle_state(), LifecycleStateImpl::kInBackForwardCache);
  DCHECK(!back_forward_cache_eviction_timer_.IsRunning());

  // If we are a delegate node we don't need to do anything else.
  if (inner_tree_main_frame_tree_node_id_ !=
      FrameTreeNode::kFrameTreeNodeInvalidId) {
    return;
  }

  for (auto& entry : service_worker_container_hosts_) {
    if (base::WeakPtr<ServiceWorkerContainerHost> host = entry.second)
      host->OnRestoreFromBackForwardCache();
  }

  DedicatedWorkerHostsForDocument::GetOrCreateForCurrentDocument(this)
      ->OnRestoreFromBackForwardCache();
#if BUILDFLAG(IS_P2P_ENABLED)
  GetProcess()->ResumeSocketManagerForRenderFrameHost(GetGlobalId());
#endif  // BUILDFLAG(IS_P2P_ENABLED)

#if BUILDFLAG(IS_OHOS)
  if (delegate_) {
    delegate_->OnRenderFrameHostLeaveBackForwardCache(GetGlobalId());
  }
#endif  // BUILDFLAG(IS_OHOS)
}

mojom::DidCommitProvisionalLoadParamsPtr
RenderFrameHostImpl::TakeLastCommitParams() {
  return std::move(last_commit_params_);
}

void RenderFrameHostImpl::StartBackForwardCacheEvictionTimer() {
  DCHECK(IsInBackForwardCache());
#ifdef OHOS_BFCACHE
  base::TimeDelta evict_after =
      GetBackForwardCache().ArkWebGetTimeToLiveInBackForwardCache();
#else
  base::TimeDelta evict_after =
      BackForwardCacheImpl::GetTimeToLiveInBackForwardCache();
#endif

  back_forward_cache_eviction_timer_.SetTaskRunner(
      GetBackForwardCache().GetTaskRunner());

  back_forward_cache_eviction_timer_.Start(
      FROM_HERE, evict_after,
      base::BindOnce(&RenderFrameHostImpl::EvictFromBackForwardCacheWithReason,
                     weak_ptr_factory_.GetWeakPtr(),
                     BackForwardCacheMetrics::NotRestoredReason::kTimeout));
}

void RenderFrameHostImpl::DisableBackForwardCache(
    BackForwardCache::DisabledReason reason,
    absl::optional<ukm::SourceId> source_id) {
  back_forward_cache_disabled_reasons_[reason].insert(source_id);
  MaybeEvictFromBackForwardCache();
}

void RenderFrameHostImpl::DisableProactiveBrowsingInstanceSwapForTesting() {
  // This should only be called on primary main frames.
  DCHECK(IsInPrimaryMainFrame());
  has_test_disabled_proactive_browsing_instance_swap_ = true;
}

void RenderFrameHostImpl::OnGrantedMediaStreamAccess() {
  was_granted_media_access_ = true;
  MaybeEvictFromBackForwardCache();
}

void RenderFrameHostImpl::OnPortalActivated(
    std::unique_ptr<Portal> predecessor,
    mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal,
    mojo::PendingAssociatedReceiver<blink::mojom::PortalClient> client_receiver,
    blink::TransferableMessage data,
    uint64_t trace_id,
    base::OnceCallback<void(blink::mojom::PortalActivateResult)> callback) {
  auto it = portals_.insert(std::move(predecessor)).first;

  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameHostImpl::OnPortalActivated",
                         TRACE_ID_GLOBAL(trace_id),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);

  GetAssociatedLocalMainFrame()->OnPortalActivated(
      (*it)->portal_token(), std::move(pending_portal),
      std::move(client_receiver), std::move(data), trace_id,
      base::BindOnce(
          [](base::OnceCallback<void(blink::mojom::PortalActivateResult)>
                 callback,
             blink::mojom::PortalActivateResult result) {
            switch (result) {
              case blink::mojom::PortalActivateResult::kPredecessorWillUnload:
              case blink::mojom::PortalActivateResult::kPredecessorWasAdopted:
                // These values are acceptable from the renderer.
                break;
              case blink::mojom::PortalActivateResult::
                  kRejectedDueToPredecessorNavigation:
              case blink::mojom::PortalActivateResult::
                  kRejectedDueToPortalNotReady:
              case blink::mojom::PortalActivateResult::
                  kRejectedDueToErrorInPortal:
              case blink::mojom::PortalActivateResult::kDisconnected:
              case blink::mojom::PortalActivateResult::kNotImplemented:
              case blink::mojom::PortalActivateResult::kAbortedDueToBug:
                // The renderer is misbehaving.
                mojo::ReportBadMessage(
                    "Unexpected PortalActivateResult from renderer");
                result = blink::mojom::PortalActivateResult::kAbortedDueToBug;
                break;
            }
            std::move(callback).Run(result);
          },
          std::move(callback)));
}

void RenderFrameHostImpl::OnPortalCreatedForTesting(
    std::unique_ptr<Portal> portal) {
  portals_.insert(std::move(portal));
}

Portal* RenderFrameHostImpl::FindPortalByToken(
    const blink::PortalToken& portal_token) {
  auto it = base::ranges::find(portals_, portal_token, &Portal::portal_token);
  return it == portals_.end() ? nullptr : it->get();
}

std::vector<Portal*> RenderFrameHostImpl::GetPortals() const {
  std::vector<Portal*> result;
  for (const auto& portal : portals_)
    result.push_back(portal.get());
  return result;
}

void RenderFrameHostImpl::DestroyPortal(Portal* portal) {
  auto it = portals_.find(portal);
  CHECK(it != portals_.end());
  std::unique_ptr<Portal> owned_portal(std::move(*it));
  portals_.erase(it);
  // |owned_portal| is deleted as it goes out of scope.
}

void RenderFrameHostImpl::ForwardMessageFromHost(
    blink::TransferableMessage message,
    const url::Origin& source_origin) {
  DCHECK_EQ(source_origin, GetLastCommittedOrigin());
  GetAssociatedLocalMainFrame()->ForwardMessageFromHost(std::move(message),
                                                        source_origin);
}

SiteInstanceImpl* RenderFrameHostImpl::GetSiteInstance() const {
  return site_instance_.get();
}

RenderProcessHost* RenderFrameHostImpl::GetProcess() const {
  return agent_scheduling_group_->GetProcess();
}

AgentSchedulingGroupHost& RenderFrameHostImpl::GetAgentSchedulingGroup() {
  return *agent_scheduling_group_;
}

RenderFrameHostImpl* RenderFrameHostImpl::GetParent() const {
  return parent_;
}

PageImpl& RenderFrameHostImpl::GetPage() {
  return *GetMainFrame()->document_associated_data_->owned_page();
}

const PageImpl& RenderFrameHostImpl::GetPage() const {
  return *GetMainFrame()->document_associated_data_->owned_page();
}

bool RenderFrameHostImpl::IsDescendantOfWithinFrameTree(
    RenderFrameHostImpl* ancestor) {
  if (!ancestor || !ancestor->child_count())
    return false;

  for (RenderFrameHostImpl* current = GetParent(); current;
       current = current->GetParent()) {
    if (current == ancestor)
      return true;
  }
  return false;
}

bool RenderFrameHostImpl::IsFencedFrameRoot() const {
  return fenced_frame_status_ == FencedFrameStatus::kFencedFrameRoot;
}

bool RenderFrameHostImpl::IsNestedWithinFencedFrame() const {
  switch (fenced_frame_status_) {
    case FencedFrameStatus::kNotNestedInFencedFrame:
      return false;
    case FencedFrameStatus::kFencedFrameRoot:
      return true;
    case FencedFrameStatus::kIframeNestedWithinFencedFrame:
      return true;
  }
}

void RenderFrameHostImpl::ForEachRenderFrameHostWithAction(
    base::FunctionRef<FrameIterationAction(RenderFrameHost*)> on_frame) {
  ForEachRenderFrameHostWithAction(
      [on_frame](RenderFrameHostImpl* rfh) { return on_frame(rfh); });
}

void RenderFrameHostImpl::ForEachRenderFrameHost(
    base::FunctionRef<void(RenderFrameHost*)> on_frame) {
  ForEachRenderFrameHost(
      [on_frame](RenderFrameHostImpl* rfh) { on_frame(rfh); });
}

void RenderFrameHostImpl::ForEachRenderFrameHostWithAction(
    base::FunctionRef<FrameIterationAction(RenderFrameHostImpl*)> on_frame) {
  ForEachRenderFrameHostImpl(on_frame, /*include_speculative=*/false);
}

void RenderFrameHostImpl::ForEachRenderFrameHost(
    base::FunctionRef<void(RenderFrameHostImpl*)> on_frame) {
  ForEachRenderFrameHostWithAction([on_frame](RenderFrameHostImpl* rfh) {
    on_frame(rfh);
    return FrameIterationAction::kContinue;
  });
}

void RenderFrameHostImpl::ForEachRenderFrameHostIncludingSpeculativeWithAction(
    base::FunctionRef<FrameIterationAction(RenderFrameHostImpl*)> on_frame) {
  ForEachRenderFrameHostImpl(on_frame, /*include_speculative=*/true);
}

void RenderFrameHostImpl::ForEachRenderFrameHostIncludingSpeculative(
    base::FunctionRef<void(RenderFrameHostImpl*)> on_frame) {
  ForEachRenderFrameHostIncludingSpeculativeWithAction(
      [on_frame](RenderFrameHostImpl* rfh) {
        on_frame(rfh);
        return FrameIterationAction::kContinue;
      });
}

void RenderFrameHostImpl::ForEachRenderFrameHostImpl(
    base::FunctionRef<FrameIterationAction(RenderFrameHostImpl*)> on_frame,
    bool include_speculative) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (!include_speculative &&
      (lifecycle_state() == LifecycleStateImpl::kSpeculative ||
       lifecycle_state() == LifecycleStateImpl::kPendingCommit)) {
    return;
  }

  // Since |this| may not be current in its FrameTree, we can't begin iterating
  // from |frame_tree_node()|, so we special case the first invocation for
  // |this| and then actually start iterating over the subtree starting with
  // our children's FrameTreeNodes.
  bool skip_children_of_starting_frame = false;
  switch (on_frame(this)) {
    case FrameIterationAction::kContinue:
      break;
    case FrameIterationAction::kSkipChildren:
      skip_children_of_starting_frame = true;
      break;
    case FrameIterationAction::kStop:
      return;
  }

  // Potentially include our FrameTreeNode's speculative RenderFrameHost, but
  // only if |this| is current in its FrameTree.
  // TODO(1264145): Avoid having a RenderFrameHost access its FrameTreeNode's
  // speculative RenderFrameHost by moving
  // ForEachRenderFrameHostIncludingSpeculative from RenderFrameHostImpl or
  // possibly removing it entirely.
  if (include_speculative && frame_tree_node()->current_frame_host() == this) {
    RenderFrameHostImpl* speculative_frame_host =
        frame_tree_node()->render_manager()->speculative_frame_host();
    if (speculative_frame_host) {
      DCHECK_EQ(speculative_frame_host->child_count(), 0U);
      switch (on_frame(speculative_frame_host)) {
        case FrameIterationAction::kContinue:
        case FrameIterationAction::kSkipChildren:
          break;
        case FrameIterationAction::kStop:
          return;
      }
    }
  }

  if (skip_children_of_starting_frame)
    return;

  FrameTree::NodeRange ftn_range = FrameTree::SubtreeAndInnerTreeNodes(this);
  FrameTree::NodeIterator it = ftn_range.begin();
  const FrameTree::NodeIterator end = ftn_range.end();

  while (it != end) {
    FrameTreeNode* node = *it;
    RenderFrameHostImpl* frame_host = node->current_frame_host();
    if (frame_host) {
      switch (on_frame(frame_host)) {
        case FrameIterationAction::kContinue:
          ++it;
          break;
        case FrameIterationAction::kSkipChildren:
          it.AdvanceSkippingChildren();
          break;
        case FrameIterationAction::kStop:
          return;
      }
    }

    if (include_speculative) {
      RenderFrameHostImpl* speculative_frame_host =
          node->render_manager()->speculative_frame_host();
      if (speculative_frame_host) {
        DCHECK_EQ(speculative_frame_host->child_count(), 0U);
        switch (on_frame(speculative_frame_host)) {
          case FrameIterationAction::kContinue:
          case FrameIterationAction::kSkipChildren:
            break;
          case FrameIterationAction::kStop:
            return;
        }
      }
    }
  }
}

int RenderFrameHostImpl::GetFrameTreeNodeId() const {
  return frame_tree_node_->frame_tree_node_id();
}

const base::UnguessableToken& RenderFrameHostImpl::GetDevToolsFrameToken() {
  return devtools_frame_token();
}

absl::optional<base::UnguessableToken>
RenderFrameHostImpl::GetEmbeddingToken() {
  return embedding_token_;
}

const std::string& RenderFrameHostImpl::GetFrameName() {
  return browsing_context_state_->frame_name();
}

bool RenderFrameHostImpl::IsFrameDisplayNone() {
  return frame_tree_node()->frame_owner_properties().is_display_none;
}

const absl::optional<gfx::Size>& RenderFrameHostImpl::GetFrameSize() {
  return frame_size_;
}

size_t RenderFrameHostImpl::GetFrameDepth() {
  return depth_;
}

bool RenderFrameHostImpl::IsCrossProcessSubframe() {
  if (is_main_frame() || GetSiteInstance() == parent_->GetSiteInstance())
    return false;
  return GetSiteInstance()->GetProcess() !=
         parent_->GetSiteInstance()->GetProcess();
}

bool RenderFrameHostImpl::RequiresProxyToParent() {
  if (is_main_frame())
    return false;
  return GetSiteInstance() != parent_->GetSiteInstance();
}

WebExposedIsolationLevel RenderFrameHostImpl::GetWebExposedIsolationLevel() {
  DCHECK_EQ(GetSiteInstance()->GetSiteInfo().web_exposed_isolation_info(),
            GetProcess()->GetProcessLock().GetWebExposedIsolationInfo());
  return GetProcess()->GetWebExposedIsolationLevel();
}

const GURL& RenderFrameHostImpl::GetLastCommittedURL() const {
  return last_committed_url_;
}

const url::Origin& RenderFrameHostImpl::GetLastCommittedOrigin() const {
  return last_committed_origin_;
}

const GURL& RenderFrameHostImpl::GetInheritedBaseUrl() const {
  return inherited_base_url_;
}

const net::NetworkIsolationKey& RenderFrameHostImpl::GetNetworkIsolationKey() {
  DCHECK(!isolation_info_.IsEmpty());
  return isolation_info_.network_isolation_key();
}

const net::IsolationInfo&
RenderFrameHostImpl::GetIsolationInfoForSubresources() {
  DCHECK(!isolation_info_.IsEmpty());
  return isolation_info_;
}

net::IsolationInfo
RenderFrameHostImpl::GetPendingIsolationInfoForSubresources() {
  // TODO(https://crbug.com/1211126): Figure out if
  // ForPendingOrLastCommittedNavigation is correct below (it might not be).
  auto config =
      SubresourceLoaderFactoriesConfig::ForPendingOrLastCommittedNavigation(
          *this);
  DCHECK(!config.isolation_info().IsEmpty());
  return config.isolation_info();
}

void RenderFrameHostImpl::GetCanonicalUrl(
    base::OnceCallback<void(const absl::optional<GURL>&)> callback) {
  if (IsRenderFrameLive()) {
    // Validate that the URL returned by the renderer is HTTP(S) only. It is
    // allowed to be cross-origin.
    auto validate_and_forward =
        [](base::OnceCallback<void(const absl::optional<GURL>&)> callback,
           const absl::optional<GURL>& url) {
          if (url && url->is_valid() && url->SchemeIsHTTPOrHTTPS()) {
            std::move(callback).Run(url);
          } else {
            std::move(callback).Run(absl::nullopt);
          }
        };
    GetAssociatedLocalFrame()->GetCanonicalUrlForSharing(
        base::BindOnce(validate_and_forward, std::move(callback)));
  } else {
    std::move(callback).Run(absl::nullopt);
  }
}

void RenderFrameHostImpl::GetOpenGraphMetadata(
    base::OnceCallback<void(blink::mojom::OpenGraphMetadataPtr)> callback) {
  if (IsRenderFrameLive()) {
    GetAssociatedLocalFrame()->GetOpenGraphMetadata(
        base::BindOnce(&ForwardOpenGraphMetadataIfValid, std::move(callback)));
  } else {
    std::move(callback).Run({});
  }
}

bool RenderFrameHostImpl::IsErrorDocument() {
  // This shouldn't be called before committing the document as this value is
  // set during call to RenderFrameHostImpl::DidNavigate which happens after
  // commit.
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kPendingCommit);
  return is_error_document_;
}

DocumentRef RenderFrameHostImpl::GetDocumentRef() {
  return DocumentRef(document_associated_data_->weak_factory().GetSafeRef());
}

WeakDocumentPtr RenderFrameHostImpl::GetWeakDocumentPtr() {
  return WeakDocumentPtr(
      document_associated_data_->weak_factory().GetWeakPtr());
}

void RenderFrameHostImpl::GetSerializedHtmlWithLocalLinks(
    const base::flat_map<GURL, base::FilePath>& url_map,
    const base::flat_map<blink::FrameToken, base::FilePath>& frame_token_map,
    bool save_with_empty_url,
    mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> serializer_handler) {
  if (!IsRenderFrameLive())
    return;
  GetMojomFrameInRenderer()->GetSerializedHtmlWithLocalLinks(
      url_map, frame_token_map, save_with_empty_url,
      std::move(serializer_handler));
}

void RenderFrameHostImpl::SetWantErrorMessageStackTrace() {
  GetMojomFrameInRenderer()->SetWantErrorMessageStackTrace();
}

void RenderFrameHostImpl::ExecuteMediaPlayerActionAtLocation(
    const gfx::Point& location,
    const blink::mojom::MediaPlayerAction& action) {
  auto media_player_action = blink::mojom::MediaPlayerAction::New();
  media_player_action->type = action.type;
  media_player_action->enable = action.enable;
  gfx::PointF point_in_view = GetView()->TransformRootPointToViewCoordSpace(
      gfx::PointF(location.x(), location.y()));
  GetAssociatedLocalFrame()->MediaPlayerActionAt(
      gfx::Point(point_in_view.x(), point_in_view.y()),
      std::move(media_player_action));
}

bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactory(
    mojo::PendingReceiver<network::mojom::URLLoaderFactory>
        default_factory_receiver) {
  // Factory config below is based on the last committed navigation, under the
  // assumptions that the caller wants a factory that acts on behalf of the
  // *currently* committed document.  This assumption is typically valid for
  // callers that are responding to an IPC coming from the renderer process.  If
  // the caller wanted a factory associated with a pending navigation, then the
  // config won't be correct.  For more details, please see the doc comment of
  // ForPendingOrLastCommittedNavigation.
  auto subresource_loader_factories_config =
      SubresourceLoaderFactoriesConfig::ForLastCommittedNavigation(*this);

  return CreateNetworkServiceDefaultFactoryAndObserve(
      CreateURLLoaderFactoryParamsForMainWorld(
          subresource_loader_factories_config,
          "RFHI::CreateNetworkServiceDefaultFactory"),
      subresource_loader_factories_config.ukm_source_id(),
      std::move(default_factory_receiver));
}

std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
RenderFrameHostImpl::CreateSubresourceLoaderFactoriesForInitialEmptyDocument() {
  // This method should only be called (by RenderViewHostImpl::CreateRenderView)
  // when creating a new local main frame in a Renderer process.
  DCHECK(!GetParent());

  // Expecting the frame to be at the initial empty document.
  // Not DCHECK-ing `last_committed_origin_`, because it is not reset by
  // RenderFrameHostImpl::RenderProcessExited for crashed frames.
  DCHECK_EQ(GURL(), last_committed_url_);

  auto subresource_loader_factories =
      std::make_unique<blink::PendingURLLoaderFactoryBundle>();
  switch (lifecycle_state()) {
    case RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache:
    case RenderFrameHostImpl::LifecycleStateImpl::kPendingCommit:
    case RenderFrameHostImpl::LifecycleStateImpl::kReadyToBeDeleted:
    case RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers:
      // A newly-created frame shouldn't be in any of the states above.
      NOTREACHED();
      break;
    case RenderFrameHostImpl::LifecycleStateImpl::kSpeculative:
      // No subresource requests should be initiated in the speculative frame.
      // Serving an empty bundle of `subresource_loader_factories` will
      // desirably lead to a crash in URLLoaderFactoryBundle::GetFactory (see
      // also the DCHECK there) if the speculative frame attempts to start a
      // subresource load.
      break;
    case RenderFrameHostImpl::LifecycleStateImpl::kActive:
    case RenderFrameHostImpl::LifecycleStateImpl::kPrerendering:
      CreateNetworkServiceDefaultFactory(
          subresource_loader_factories->pending_default_factory()
              .InitWithNewPipeAndPassReceiver());

      // The caller will send the returned factory to the renderer process (as
      // the default factory).  Therefore, after a NetworkService crash we need
      // to push to the renderer an updated factory.
      recreate_default_url_loader_factory_after_network_service_crash_ = true;
      break;
  }

  return subresource_loader_factories;
}

void RenderFrameHostImpl::MarkIsolatedWorldsAsRequiringSeparateURLLoaderFactory(
    const base::flat_set<url::Origin>& isolated_world_origins,
    bool push_to_renderer_now) {
  size_t old_size =
      isolated_worlds_requiring_separate_url_loader_factory_.size();
  isolated_worlds_requiring_separate_url_loader_factory_.insert(
      isolated_world_origins.begin(), isolated_world_origins.end());
  size_t new_size =
      isolated_worlds_requiring_separate_url_loader_factory_.size();
  bool insertion_took_place = (old_size != new_size);

  // Push the updated set of factories to the renderer, but only if
  // 1) the caller requested an immediate push (e.g. for content scripts
  //    injected programmatically chrome.tabs.executeCode, but not for content
  //    scripts declared in the manifest - the difference is that the latter
  //    happen at a commit and the factories can just be send in the commit
  //    IPC).
  // 2) an insertion actually took place / the factories have been modified.
  //
  // See also the doc comment of `PendingURLLoaderFactoryBundle::OriginMap`
  // (the type of `pending_isolated_world_factories` that are set below).
  if (push_to_renderer_now && insertion_took_place) {
    // The `config` of the new factories might need to depend on the pending
    // (rather than the last committed) navigation, because we can't predict if
    // an in-flight Commit IPC might be present when an extension injects a
    // content script and MarkIsolatedWorlds... is called.  See also the doc
    // comment for the ForPendingOrLastCommittedNavigation method.
    auto config =
        SubresourceLoaderFactoriesConfig::ForPendingOrLastCommittedNavigation(
            *this);

    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories =
            std::make_unique<blink::PendingURLLoaderFactoryBundle>();
    subresource_loader_factories->pending_isolated_world_factories() =
        CreateURLLoaderFactoriesForIsolatedWorlds(config,
                                                  isolated_world_origins);
    GetMojomFrameInRenderer()->UpdateSubresourceLoaderFactories(
        std::move(subresource_loader_factories));
  }
}

bool RenderFrameHostImpl::IsSandboxed(network::mojom::WebSandboxFlags flags) {
  return static_cast<int>(active_sandbox_flags()) & static_cast<int>(flags);
}

blink::web_pref::WebPreferences
RenderFrameHostImpl::GetOrCreateWebPreferences() {
  return delegate()->GetOrCreateWebPreferences();
}

blink::PendingURLLoaderFactoryBundle::OriginMap
RenderFrameHostImpl::CreateURLLoaderFactoriesForIsolatedWorlds(
    const SubresourceLoaderFactoriesConfig& config,
    const base::flat_set<url::Origin>& isolated_world_origins) {
  blink::PendingURLLoaderFactoryBundle::OriginMap result;
  for (const url::Origin& isolated_world_origin : isolated_world_origins) {
    network::mojom::URLLoaderFactoryParamsPtr factory_params =
        URLLoaderFactoryParamsHelper::CreateForIsolatedWorld(
            this, isolated_world_origin, config.origin(),
            config.isolation_info(), config.GetClientSecurityState(),
            config.trust_token_issuance_policy(),
            config.trust_token_redemption_policy(),
            config.cookie_setting_overrides());

    mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
    CreateNetworkServiceDefaultFactoryAndObserve(
        std::move(factory_params),
        ukm::kInvalidSourceIdObj, /* isolated from page */
        factory_remote.InitWithNewPipeAndPassReceiver());
    result[isolated_world_origin] = std::move(factory_remote);
  }
  return result;
}

gfx::NativeView RenderFrameHostImpl::GetNativeView() {
  RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
  if (!view)
    return nullptr;
  return view->GetNativeView();
}

void RenderFrameHostImpl::AddMessageToConsole(
    blink::mojom::ConsoleMessageLevel level,
    const std::string& message) {
  AddMessageToConsoleImpl(level, message, /*discard_duplicates=*/false);
}

void RenderFrameHostImpl::ExecuteJavaScriptMethod(
    const std::u16string& object_name,
    const std::u16string& method_name,
    base::Value::List arguments,
    JavaScriptResultCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  CHECK(CanExecuteJavaScript());
  AssertNonSpeculativeFrame();

  const bool wants_result = !callback.is_null();
  GetAssociatedLocalFrame()->JavaScriptMethodExecuteRequest(
      object_name, method_name, std::move(arguments), wants_result,
      std::move(callback));
}

void RenderFrameHostImpl::ExecuteJavaScript(const std::u16string& javascript,
                                            JavaScriptResultCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  CHECK(CanExecuteJavaScript());
  AssertNonSpeculativeFrame();

  const bool wants_result = !callback.is_null();
  GetAssociatedLocalFrame()->JavaScriptExecuteRequest(javascript, wants_result,
                                                      std::move(callback));
}

#if BUILDFLAG(IS_OHOS)
void RenderFrameHostImpl::ExecuteJavaScriptExt(const int fd,
                                               const uint64_t scriptLength,
                                               JavaScriptResultCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  CHECK(CanExecuteJavaScript());
  AssertNonSpeculativeFrame();

  const bool wants_result = !callback.is_null();
  MojoPlatformHandle platform_handle;
  platform_handle.struct_size = sizeof(platform_handle);
  platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
  platform_handle.value = static_cast<uint64_t>(fd);
  MojoHandle handle;
  MojoWrapPlatformHandle(&platform_handle, nullptr, &handle);
  GetAssociatedLocalFrame()->JavaScriptExecuteRequestExt(
      mojo::ScopedHandle(mojo::Handle(handle)), scriptLength, wants_result, std::move(callback));
}

void RenderFrameHostImpl::SendAccessibilityEvent(int64_t accessibilityId,
                                                 int32_t eventType,
                                                 const std::string& argument) {
  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
      render_view_host_->GetWidget()->GetView());
  if (view) {
    view->SendAccessibilityEvent(accessibilityId, eventType, argument);
  }
}
#endif

void RenderFrameHostImpl::ExecuteJavaScriptInIsolatedWorld(
    const std::u16string& javascript,
    JavaScriptResultCallback callback,
    int32_t world_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK_GT(world_id, ISOLATED_WORLD_ID_GLOBAL);
  DCHECK_LE(world_id, ISOLATED_WORLD_ID_MAX);
  AssertNonSpeculativeFrame();

  const bool wants_result = !callback.is_null();
  GetAssociatedLocalFrame()->JavaScriptExecuteRequestInIsolatedWorld(
      javascript, wants_result, world_id, std::move(callback));
}

void RenderFrameHostImpl::ExecuteJavaScriptForTests(
    const std::u16string& javascript,
    JavaScriptResultCallback callback,
    int32_t world_id) {
  ExecuteJavaScriptForTests(  // IN-TEST
      javascript, /*has_user_gesture=*/false,
      /*resolve_promises=*/false, world_id,
      CreateJavaScriptExecuteRequestForTestsCallback(std::move(callback)));
}

void RenderFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
    const std::u16string& javascript,
    JavaScriptResultCallback callback,
    int32_t world_id) {
  ExecuteJavaScriptForTests(  // IN-TEST
      javascript, /*has_user_gesture=*/true,
      /*resolve_promises=*/false, world_id,
      CreateJavaScriptExecuteRequestForTestsCallback(std::move(callback)));
}

void RenderFrameHostImpl::ExecuteJavaScriptForTests(
    const std::u16string& javascript,
    bool has_user_gesture,
    bool resolve_promises,
    int32_t world_id,
    JavaScriptResultAndTypeCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  AssertNonSpeculativeFrame();

  if (has_user_gesture && owner_) {
    // TODO(mustaq): The render-to-browser state update caused by the below
    // JavaScriptExecuteRequestsForTests call is redundant with this update. We
    // should determine if the redundancy can be removed.
    owner_->UpdateUserActivationState(
        blink::mojom::UserActivationUpdateType::kNotifyActivation,
        blink::mojom::UserActivationNotificationType::kTest);
  }

  GetAssociatedLocalFrame()->JavaScriptExecuteRequestForTests(  // IN-TEST
      javascript, has_user_gesture, resolve_promises, world_id,
      std::move(callback));
}

void RenderFrameHostImpl::ExecutePluginActionAtLocalLocation(
    const gfx::Point& local_location,
    blink::mojom::PluginActionType plugin_action) {
  GetAssociatedLocalFrame()->PluginActionAt(local_location, plugin_action);
}

void RenderFrameHostImpl::CopyImageAt(int x, int y) {
  gfx::PointF point_in_view =
      GetView()->TransformRootPointToViewCoordSpace(gfx::PointF(x, y));
  GetAssociatedLocalFrame()->CopyImageAt(
      gfx::Point(point_in_view.x(), point_in_view.y()));
}

void RenderFrameHostImpl::SaveImageAt(int x, int y) {
  gfx::PointF point_in_view =
      GetView()->TransformRootPointToViewCoordSpace(gfx::PointF(x, y));
  GetAssociatedLocalFrame()->SaveImageAt(
      gfx::Point(point_in_view.x(), point_in_view.y()));
}

RenderViewHost* RenderFrameHostImpl::GetRenderViewHost() const {
  return render_view_host_.get();
}

service_manager::InterfaceProvider* RenderFrameHostImpl::GetRemoteInterfaces() {
  DCHECK(IsRenderFrameLive());
  return remote_interfaces_.get();
}

blink::AssociatedInterfaceProvider*
RenderFrameHostImpl::GetRemoteAssociatedInterfaces() {
  if (!remote_associated_interfaces_) {
    mojo::AssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
        remote_interfaces;
    if (GetAgentSchedulingGroup().GetChannel()) {
      GetAgentSchedulingGroup().GetRemoteRouteProvider()->GetRoute(
          GetRoutingID(), remote_interfaces.BindNewEndpointAndPassReceiver());
    } else {
      // The channel may not be initialized in some tests environments. In this
      // case we set up a dummy interface provider.
      std::ignore = remote_interfaces.BindNewEndpointAndPassDedicatedReceiver();
    }
    remote_associated_interfaces_ =
        std::make_unique<blink::AssociatedInterfaceProvider>(
            remote_interfaces.Unbind());
  }
  return remote_associated_interfaces_.get();
}

PageVisibilityState RenderFrameHostImpl::GetVisibilityState() {
  // Works around the crashes seen in https://crbug.com/501863, where the
  // active WebContents from a browser iterator may contain a render frame
  // detached from the frame tree. This tries to find a RenderWidgetHost
  // attached to an ancestor frame, and defaults to visibility hidden if
  // it fails.
  // TODO(yfriedman, peter): Ideally this would never be called on an
  // unattached frame and we could omit this check. See
  // https://crbug.com/615867.
  RenderFrameHostImpl* frame = this;
  while (frame) {
    if (frame->GetLocalRenderWidgetHost())
      break;
    frame = frame->GetParent();
  }
  if (!frame)
    return PageVisibilityState::kHidden;

  PageVisibilityState visibility_state = GetRenderWidgetHost()->is_hidden()
                                             ? PageVisibilityState::kHidden
                                             : PageVisibilityState::kVisible;
  GetContentClient()->browser()->OverridePageVisibilityState(this,
                                                             &visibility_state);
  return visibility_state;
}

bool RenderFrameHostImpl::Send(IPC::Message* message) {
  return GetAgentSchedulingGroup().Send(message);
}

bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message& msg) {
  // Only process messages if the RenderFrame is alive.
  if (!is_render_frame_created())
    return false;

  // Crash reports triggered by IPC messages for this frame should be associated
  // with its URL.
  ScopedActiveURL scoped_active_url(this);

  if (delegate_->OnMessageReceived(this, msg))
    return true;

  return false;
}

void RenderFrameHostImpl::OnAssociatedInterfaceRequest(
    const std::string& interface_name,
    mojo::ScopedInterfaceEndpointHandle handle) {
  // `this` is an `IPC::Listener`, but there is no path by which `this` would
  // receive associated interface requests through this method. Associated
  // interface requests come in through `GetAssociatedInterface()`.
  NOTREACHED();
}

std::string RenderFrameHostImpl::ToDebugString() {
  return "RFHI:" +
         render_view_host_->GetDelegate()->GetCreatorLocation().ToString();
}

void RenderFrameHostImpl::AccessibilityPerformAction(
    const ui::AXActionData& action_data) {
  // Don't perform any Accessibility action on an inactive frame.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kAXPerformAction) ||
      !render_accessibility_)
    return;

  if (action_data.action == ax::mojom::Action::kHitTest) {
    AccessibilityHitTest(action_data.target_point,
                         action_data.hit_test_event_to_fire,
                         action_data.request_id, {});
    return;
  }
  // Set the input modality in RenderWidgetHostViewAura to touch so the
  // VK shows up.
  if (action_data.action == ax::mojom::Action::kFocus) {
    RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
        render_view_host_->GetWidget()->GetView());
    if (view)
      view->SetLastPointerType(ui::EventPointerType::kTouch);
  }

  render_accessibility_->PerformAction(action_data);
}

bool RenderFrameHostImpl::AccessibilityViewHasFocus() {
  RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
  if (view)
    return view->HasFocus();
  return false;
}

void RenderFrameHostImpl::AccessibilityViewSetFocus() {
  // Don't update Accessibility for inactive frames.
  if (IsInactiveAndDisallowActivation(DisallowActivationReasonId::kAXSetFocus))
    return;

  RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
  if (view)
    view->Focus();
}

gfx::Rect RenderFrameHostImpl::AccessibilityGetViewBounds() {
  RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
  if (view)
    return view->GetViewBounds();
  return gfx::Rect();
}

float RenderFrameHostImpl::AccessibilityGetDeviceScaleFactor() {
  return GetScaleFactorForView(GetView());
}

void RenderFrameHostImpl::AccessibilityReset() {
  if (!render_accessibility_)
    return;

  accessibility_reset_token_ = g_next_accessibility_reset_token++;
  render_accessibility_->Reset(accessibility_reset_token_);
}

void RenderFrameHostImpl::AccessibilityFatalError() {
  CHECK(!BrowserAccessibilityManager::IsFailFastMode());
  browser_accessibility_manager_.reset();
  if (accessibility_reset_token_ || !render_accessibility_)
    return;

  accessibility_fatal_error_count_++;
  if (accessibility_fatal_error_count_ > max_accessibility_resets_) {
    // This will both create an "Aw Snap..." and generate a second crash report
    // in addition to the DumpWithoutCrashing() for the first reset.
    render_accessibility_->FatalError();
  } else {
    // Crash keys set in BrowserAccessibilityManager::Unserialize().
    if (accessibility_fatal_error_count_ == 1) {
      // Only send crash report first time -- don't skew crash stats too much.
      base::debug::DumpWithoutCrashing();
    }
    AccessibilityReset();
  }
}

gfx::AcceleratedWidget
RenderFrameHostImpl::AccessibilityGetAcceleratedWidget() {
  DCHECK(AccessibilityIsRootFrame());
  // Only the active RenderFrameHost is connected to the native widget tree for
  // accessibility, so return null if this is queried on any other frame.
  if (!IsActive())
    return gfx::kNullAcceleratedWidget;

  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
      render_view_host_->GetWidget()->GetView());
  if (view)
    return view->AccessibilityGetAcceleratedWidget();
  return gfx::kNullAcceleratedWidget;
}

gfx::NativeViewAccessible
RenderFrameHostImpl::AccessibilityGetNativeViewAccessible() {
  if (base::FeatureList::IsEnabled(features::kEvictOnAXEvents) &&
      base::FeatureList::IsEnabled(
          features::kEnableBackForwardCacheForScreenReader) &&
      IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kAXGetNativeView)) {
    // |AccessibilityGetNativeViewAccessible()| should be only accessible when
    // we process AX events. Otherwise this should not be used while in
    // back/forward cache and the document should be evicted.
    return nullptr;
  }
  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
      render_view_host_->GetWidget()->GetView());
  if (view)
    return view->AccessibilityGetNativeViewAccessible();
  return nullptr;
}

gfx::NativeViewAccessible
RenderFrameHostImpl::AccessibilityGetNativeViewAccessibleForWindow() {
  // If this method is called when the frame is in BackForwardCache, evict
  // the frame to avoid ignoring any accessibility related events which are not
  // expected.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kAXGetNativeViewForWindow))
    return nullptr;

  RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
      render_view_host_->GetWidget()->GetView());
  if (view)
    return view->AccessibilityGetNativeViewAccessibleForWindow();
  return nullptr;
}

void RenderFrameHostImpl::AccessibilityHitTest(
    const gfx::Point& point_in_frame_pixels,
    const ax::mojom::Event& opt_event_to_fire,
    int opt_request_id,
    base::OnceCallback<void(ui::AXPlatformTreeManager* hit_manager,
                            ui::AXNodeID hit_node_id)> opt_callback) {
  // This is called by BrowserAccessibilityManager. During teardown it's
  // possible that render_accessibility_ is null but the corresponding
  // BrowserAccessibilityManager still exists and could call this.
  if (IsInactiveAndDisallowActivation(DisallowActivationReasonId::kAXHitTest) ||
      !render_accessibility_) {
    if (opt_callback)
      std::move(opt_callback).Run(nullptr, 0);
    return;
  }

  render_accessibility_->HitTest(
      point_in_frame_pixels, opt_event_to_fire, opt_request_id,
      base::BindOnce(&RenderFrameHostImpl::AccessibilityHitTestCallback,
                     weak_ptr_factory_.GetWeakPtr(), opt_request_id,
                     opt_event_to_fire, std::move(opt_callback)));
}

bool RenderFrameHostImpl::AccessibilityIsRootFrame() const {
  // Do not use is_main_frame() or IsOutermostMainFrame().
  // Frame trees may be nested so it can be the case that is_main_frame() is
  // true, but is not the outermost RenderFrameHost (it only checks for nullity
  // of |parent_|. In particular, !is_main_frame() cannot be used to check if
  // this RenderFrameHost is embedded. In addition, IsOutermostMainFrame()
  // does not escape guest views. Therefore, we must check for any kind of
  // parent document or embedder.
  return !GetParentOrOuterDocumentOrEmbedderExcludingProspectiveOwners();
}

RenderFrameHostImpl* RenderFrameHostImpl::AccessibilityRenderFrameHost() {
  return this;
}

WebContentsAccessibility*
RenderFrameHostImpl::AccessibilityGetWebContentsAccessibility() {
  DCHECK(AccessibilityIsRootFrame());
  auto* view = static_cast<RenderWidgetHostViewBase*>(GetView());
  if (!view)
    return nullptr;
  return view->GetWebContentsAccessibility();
}

void RenderFrameHostImpl::InitializePolicyContainerHost(
    bool renderer_initiated_creation_of_main_frame) {
  // No policy container for speculative frames.
  if (lifecycle_state_ == LifecycleStateImpl::kSpeculative) {
    return;
  }

  // During initialization, a RenderFrameHost always has a non-null `owner_`.
  // This is called from the constructor.
  CHECK(owner_);

  // The initial empty document inherits its policy container from its creator.
  // The creator is either its parent for subframes and embedded frames, or its
  // opener for new windows.
  //
  // Note 1: For normal document created from a navigation, the policy container
  // is computed from the NavigationRequest and assigned in
  // DidCommitNewDocument().
  if (parent_) {
    SetPolicyContainerHost(parent_->policy_container_host()->Clone());
  } else if (GetParentOrOuterDocument()) {
    // In the MPArch implementation of FencedFrame, this RenderFrameHost's
    // SiteInstance has been adjusted to match its parent. During navigations,
    // COOP and COEP are used to determine the SiteInstance. It means that if
    // SiteInstance has been inherited, COOP/COEP must also be inherited to
    // avoid creating inconsistencies. See:
    // https://chromium-review.googlesource.com/c/chromium/src/+/3645368
    //
    // TODO(https://crbug.com/1338603): What makes sense for GuestView?
    // TODO(https://crbug.com/1338601): What makes sense for Portals?
    const PolicyContainerPolicies& parent_policies =
        GetParentOrOuterDocument()->policy_container_host()->policies();

    // Note: the full constructor is used, to force developers to make an
    // explicit decision when adding new fields to the PolicyContainer.
    SetPolicyContainerHost(
        base::MakeRefCounted<PolicyContainerHost>(PolicyContainerPolicies(
            network::mojom::ReferrerPolicy::kDefault,
            network::mojom::IPAddressSpace::kUnknown,
            /*is_web_secure_context=*/false,
            std::vector<network::mojom::ContentSecurityPolicyPtr>(),
            parent_policies.cross_origin_opener_policy,
            parent_policies.cross_origin_embedder_policy,
            network::mojom::WebSandboxFlags::kNone,
            /*is_credentialless=*/false,
            /*can_navigate_top_without_user_gesture=*/true)));
  } else if (owner_->GetOpener()) {
    // During a `window.open(...)` without `noopener`, a new popup is created
    // and always starts from the initial empty document. The opener has
    // synchronous access toward its openee. So they must both share the same
    // policies.
    SetPolicyContainerHost(owner_->GetOpener()
                               ->current_frame_host()
                               ->policy_container_host()
                               ->Clone());
  } else {
    // In all the other cases, there is no environment to inherit policies
    // from. This is "probably" a new top-level about:blank document created by
    // the browser directly (omnibox, bookmarks, ...).
    PolicyContainerPolicies policies;

    // Main frames created by the browser are treated as belonging the
    // `loopback` address space, so that they can make requests to any address
    // space unimpeded. The only way to execute code in such a context is to
    // inject it
    // via DevTools, WebView APIs, or extensions; it is impossible to do so with
    // Web Platform means only.
    //
    // See also https://crbug.com/1191161.
    //
    // We also exclude prerendering from this case manually, since prendering
    // RenderFrameHosts are unconditionally created with the
    // `renderer_initiated_creation_of_main_frame` set to false, even though the
    // frames arguably are renderer-created.
    //
    // TODO(https://crbug.com/1194421): Address the prerendering case.
    DCHECK(IsOutermostMainFrame());
    if (!renderer_initiated_creation_of_main_frame &&
        lifecycle_state_ != LifecycleStateImpl::kPrerendering) {
      policies.ip_address_space = network::mojom::IPAddressSpace::kLoopback;
    }

    SetPolicyContainerHost(
        base::MakeRefCounted<PolicyContainerHost>(std::move(policies)));
  }

  // The initial empty documents sandbox flags is the union from:
  // - The parent or opener document's CSP sandbox inherited by policy
  //   container.
  // - The frame's sandbox flags, contained in browsing_context_state. This
  //   are either:
  //   1. For iframe: the parent + iframe.sandbox attribute.
  //   2. For popups: the opener if "allow-popups-to-escape-sandbox" isn't
  //   set.
  network::mojom::WebSandboxFlags sandbox_flags_to_commit =
      browsing_context_state_->effective_frame_policy().sandbox_flags;
  for (const auto& csp :
       policy_container_host_->policies().content_security_policies) {
    sandbox_flags_to_commit |= csp->sandbox;
  }
  policy_container_host_->set_sandbox_flags(sandbox_flags_to_commit);

  // The initial empty document's credentialless bit was inherited from the
  // parent document. The frame's credentialless bit can also turn it on.
  if (owner_->Credentialless()) {
    policy_container_host_->SetIsCredentialless();
  }
}

void RenderFrameHostImpl::SetPolicyContainerHost(
    scoped_refptr<PolicyContainerHost> policy_container_host) {
  policy_container_host_ = std::move(policy_container_host);
  policy_container_host_->AssociateWithFrameToken(GetFrameToken(),
                                                  GetProcess()->GetID());
  // Top-level document are never credentialless.
  // Note: It is never inherited from the opener, because they are forced to
  // open windows using noopener.
  CHECK(parent_ || !IsCredentialless());
}

void RenderFrameHostImpl::InitializeLocalNetworkRequestPolicy() {
  if (!policy_container_host_) {
    // Only speculative RFHs may lack a policy container.
    DCHECK_EQ(lifecycle_state_, LifecycleStateImpl::kSpeculative);
    return;
  }

  local_network_request_policy_ = DerivePrivateNetworkRequestPolicy(
      policy_container_host_->policies(),
      PrivateNetworkRequestContext::kSubresource);
}

void RenderFrameHostImpl::RenderProcessGone(
    SiteInstanceGroup* site_instance_group,
    const ChildProcessTerminationInfo& info) {
  DCHECK_EQ(site_instance_->group(), site_instance_group);

  if (IsInBackForwardCache()) {
    EvictFromBackForwardCacheWithReason(
        info.status == base::TERMINATION_STATUS_PROCESS_CRASHED
            ? BackForwardCacheMetrics::NotRestoredReason::
                  kRendererProcessCrashed
            : BackForwardCacheMetrics::NotRestoredReason::
                  kRendererProcessKilled);
  }

  CancelPrerendering(PrerenderCancellationReason(
      info.status == base::TERMINATION_STATUS_PROCESS_CRASHED
          ? PrerenderFinalStatus::kRendererProcessCrashed
          : PrerenderFinalStatus::kRendererProcessKilled));

  if (owned_render_widget_host_)
    owned_render_widget_host_->RendererExited();

  // The renderer process is gone, so this frame can no longer be loading.
  ResetOwnedNavigationRequests(NavigationDiscardReason::kRenderProcessGone);
  ResetLoadingState();

// Also, clear any pending navigations that have been blocked while the
  // embedder is processing window.open() requests.  This is consistent
  // with clearing NavigationRequests and loading state above, and it also
  // makes sense because certain parts of `pending_navigate_`, like the
  // NavigationClient remote interface, can no longer be used.
  pending_navigate_.reset();

  // Any future UpdateState or UpdateTitle messages from this or a recreated
  // process should be ignored until the next commit.
  set_nav_entry_id(0);

  if (is_audible_)
    OnAudibleStateChanged(false);

  ++renderer_exit_count_;

  if (base::FeatureList::IsEnabled(features::kCrashReporting))
    MaybeGenerateCrashReport(info.status, info.exit_code);

  // Reporting API: Send any queued reports and mark the reporting source as
  // expired so that the reporting configuration in the network service can be
  // removed. This is done here, rather than in the destructor, as it needs the
  // mojo pipe to the network service.
  GetProcess()
      ->GetStoragePartition()
      ->GetNetworkContext()
      ->SendReportsAndRemoveSource(GetReportingSource());

  // When a frame's process dies, its RenderFrame no longer exists, which means
  // that its child frames must be cleaned up as well.
  ResetChildren();

  // Reset state for the current RenderFrameHost once the FrameTreeNode has been
  // reset.
  RenderFrameDeleted();
  SetLastCommittedUrl(GURL());
  SetInheritedBaseUrl(GURL());
  renderer_url_info_ = RendererURLInfo();

  must_be_replaced_ = true;
  has_committed_any_navigation_ = false;

#if BUILDFLAG(IS_ANDROID)
  // Execute any pending Samsung smart clip callbacks.
  for (base::IDMap<std::unique_ptr<ExtractSmartClipDataCallback>>::iterator
           iter(&smart_clip_callbacks_);
       !iter.IsAtEnd(); iter.Advance()) {
    std::move(*iter.GetCurrentValue())
        .Run(std::u16string(), std::u16string(), gfx::Rect());
  }
  smart_clip_callbacks_.Clear();
#endif  // BUILDFLAG(IS_ANDROID)

  // Ensure that future remote interface requests are associated with the new
  // process's channel.
  remote_associated_interfaces_.reset();

  // Any termination disablers in content loaded by the new process will
  // be sent again.
  has_before_unload_handler_ = false;
  has_unload_handler_ = false;
  has_pagehide_handler_ = false;
  has_visibilitychange_handler_ = false;

  has_navigate_event_handler_ = false;

  if (IsPendingDeletion()) {
    // If the process has died, we don't need to wait for the ACK. Complete the
    // deletion immediately.
    // Note that it is possible for a frame to already be in kReadyToBeDeleted.
    // This happens when this RenderFrameHost is pending deletion and is
    // waiting on one of its children to run its unload handler. While running
    // it, it can request its parent to detach itself.
    if (lifecycle_state() != LifecycleStateImpl::kReadyToBeDeleted)
      SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);

    DCHECK(children_.empty());
    PendingDeletionCheckCompleted();  // Can delete |this|.
    // |this| is deleted. Don't add any more code at this point in the function.
    return;
  }

  // If this was the current pending or speculative RFH dying, cancel and
  // destroy it.
  if (lifecycle_state_ == LifecycleStateImpl::kSpeculative) {
    CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
    owner_->GetRenderFrameHostManager()
        .CleanupSpeculativeRfhForRenderProcessGone();
  }

  // Note: don't add any more code at this point in the function because
  // |this| may be deleted. Any additional cleanup should happen before
  // the last block of code here.
}

void RenderFrameHostImpl::PerformAction(const ui::AXActionData& data) {
  AccessibilityPerformAction(data);
}

bool RenderFrameHostImpl::RequiresPerformActionPointInPixels() const {
  return true;
}

bool RenderFrameHostImpl::CreateRenderFrame(
    const absl::optional<blink::FrameToken>& previous_frame_token,
    const absl::optional<blink::FrameToken>& opener_frame_token,
    const absl::optional<blink::FrameToken>& parent_frame_token,
    const absl::optional<blink::FrameToken>& previous_sibling_frame_token) {
  TRACE_EVENT0("navigation", "RenderFrameHostImpl::CreateRenderFrame");
  DCHECK(!IsRenderFrameLive()) << "Creating frame twice";

  // The process may (if we're sharing a process with another host that already
  // initialized it) or may not (we have our own process or the old process
  // crashed) have been initialized. Calling Init() multiple times will be
  // ignored, so this is safe.
  if (!GetAgentSchedulingGroup().Init())
    return false;

  DCHECK(GetProcess()->IsInitializedAndNotDead());

  mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
  BindBrowserInterfaceBrokerReceiver(
      params->interface_broker.InitWithNewPipeAndPassReceiver());

  params->routing_id = routing_id_;
  params->previous_frame_token = previous_frame_token;
  params->opener_frame_token = opener_frame_token;
  params->parent_frame_token = parent_frame_token;
  params->previous_sibling_frame_token = previous_sibling_frame_token;
  params->tree_scope_type = frame_tree_node()->tree_scope_type();
  params->replication_state =
      browsing_context_state_->current_replication_state().Clone();
  params->frame_token = frame_token_;
  params->devtools_frame_token = devtools_frame_token();
  BindAssociatedInterfaceProviderReceiver(
      params->associated_interface_provider_remote
          .InitWithNewEndpointAndPassReceiver());
  params->document_token = document_associated_data_->token();

  // If this is a new RenderFrameHost for a frame that has already committed a
  // document, we don't have a policy container 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 (policy_container_host())
    params->policy_container =
        policy_container_host()->CreatePolicyContainerForBlink();

  // Normally, the replication state contains effective frame policy, excluding
  // sandbox flags and permissions policy attributes that were updated but have
  // not taken effect. However, a new RenderFrame should use the pending frame
  // policy, since it is being created as part of the navigation that will
  // commit it. (I.e., the RenderFrame needs to know the policy to use when
  // initializing the new document once it commits).
  params->replication_state->frame_policy =
      frame_tree_node()->pending_frame_policy();

  // If we switched BrowsingInstances because of the COOP header, we should
  // clear the frame name. This below informs the renderer at frame creation.
  NavigationRequest* navigation_request =
      frame_tree_node()->navigation_request();

  bool should_clear_browsing_instance_name =
      navigation_request &&
      (navigation_request->browsing_context_group_swap()
           .ShouldClearWindowName() ||
       (navigation_request->commit_params()
            .is_cross_site_cross_browsing_context_group &&
        base::FeatureList::IsEnabled(
            features::kClearCrossSiteCrossBrowsingContextGroupWindowName)));

  if (should_clear_browsing_instance_name) {
    params->replication_state->name = "";
    // The "swaps" only affect main frames, that have an empty unique name.
    DCHECK(params->replication_state->unique_name.empty());
  }

  params->frame_owner_properties =
      frame_tree_node()->frame_owner_properties().Clone();

  params->is_on_initial_empty_document =
      frame_tree_node()->is_on_initial_empty_document();

  // The RenderWidgetHost takes ownership of its view. It is tied to the
  // lifetime of the current RenderProcessHost for this RenderFrameHost.
  // TODO(avi): This will need to change to initialize a
  // RenderWidgetHostViewAura for the main frame once RenderViewHostImpl has-a
  // RenderWidgetHostImpl. https://crbug.com/545684
  if (owned_render_widget_host_) {
    DCHECK(parent_);
    DCHECK(parent_frame_token);
    RenderWidgetHostView* rwhv = RenderWidgetHostViewChildFrame::Create(
        owned_render_widget_host_.get(),
        parent_->GetRenderWidgetHost()->GetScreenInfos());
    // The child frame should be created hidden.
    DCHECK(!rwhv->IsShowing());
  }

  if (auto* rwh = GetLocalRenderWidgetHost()) {
    params->widget_params = rwh->BindAndGenerateCreateFrameWidgetParams();
  }
  mojo::PendingAssociatedRemote<mojom::Frame> pending_frame_remote;
  params->frame = pending_frame_remote.InitWithNewEndpointAndPassReceiver();
  SetMojomFrameRemote(std::move(pending_frame_remote));

  // https://crbug.com/1006814. The renderer needs at least one of these tokens
  // to be able to insert the new frame in the frame tree.
  DCHECK(params->previous_frame_token || params->parent_frame_token);
  GetAgentSchedulingGroup().CreateFrame(std::move(params));

  if (previous_frame_token &&
      previous_frame_token->Is<blink::RemoteFrameToken>()) {
    RenderFrameProxyHost* proxy = RenderFrameProxyHost::FromFrameToken(
        GetProcess()->GetID(),
        previous_frame_token->GetAs<blink::RemoteFrameToken>());
    // We have also created a `blink::RemoteFrame` in CreateFrame above, so
    // remember that.
    CHECK(proxy);
    proxy->SetRenderFrameProxyCreated(true);
  }

  // The renderer now has a RenderFrame for this RenderFrameHost.  Note that
  // this path is only used for out-of-process iframes.  Main frame RenderFrames
  // are created with their RenderView, and same-site iframes are created at the
  // time of OnCreateChildFrame.
  RenderFrameCreated();

  return true;
}

void RenderFrameHostImpl::SetMojomFrameRemote(
    mojo::PendingAssociatedRemote<mojom::Frame> frame_remote) {
  DCHECK(!frame_);
  frame_.Bind(std::move(frame_remote));
}

void RenderFrameHostImpl::DeleteRenderFrame(
    mojom::FrameDeleteIntention intent) {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::DeleteRenderFrame",
              [&](perfetto::EventContext ctx) {
                WriteRenderFrameImplDeletion(ctx, this, intent);
              });
  if (IsPendingDeletion())
    return;

  if (IsRenderFrameLive()) {
    GetMojomFrameInRenderer()->Delete(intent);

    // We change the lifecycle state to kRunningUnloadHandlers at the end of
    // this method to wait until OnUnloadACK() is invoked.
    // For subframes, process shutdown may be delayed for two reasons:
    // (1) to allow the process to be potentially reused by future navigations
    // withjin a short time window, and
    // (2) to give the subframe unload handlers a chance to execute.
    // TODO(crbug.com/1356280): consider delaying process shutdown for fenced
    // frames.
    if (!is_main_frame() && IsActive()) {
      base::TimeDelta subframe_shutdown_timeout =
          frame_tree_->IsBeingDestroyed()
              ? base::TimeDelta()
              : GetSubframeProcessShutdownDelay(
                    GetSiteInstance()->GetBrowserContext());
      // If this document has unload handlers (and is active), ensure that they
      // have a chance to execute by delaying process cleanup. This will prevent
      // the process from shutting down immediately in the case where this is
      // the last active frame in the process. See https://crbug.com/852204.
      // Note that in the majority of cases, this is not necessary now that we
      // keep track of pending delete RenderFrameHost
      // (https://crbug.com/609963), but there are still a few exceptions where
      // this is needed (https://crbug.com/1014550).
      const base::TimeDelta unload_handler_timeout =
          has_unload_handlers() ? subframe_unload_timeout_ : base::TimeDelta();

      if (!subframe_shutdown_timeout.is_zero() ||
          !unload_handler_timeout.is_zero()) {
        GetProcess()->DelayProcessShutdown(subframe_shutdown_timeout,
                                           unload_handler_timeout,
                                           site_instance_->GetSiteInfo());
      }
      // If the subframe takes too long to unload, force its removal from the
      // tree. See https://crbug.com/950625.
      subframe_unload_timer_.Start(FROM_HERE, subframe_unload_timeout_, this,
                                   &RenderFrameHostImpl::OnUnloadTimeout);
    }
  }

  // In case of speculative RenderFrameHosts deletion, we don't run any unload
  // handlers and RenderFrameHost is deleted directly without any lifecycle
  // state transitions.
  if (lifecycle_state() == LifecycleStateImpl::kSpeculative) {
    DCHECK(!has_unload_handlers());
    return;
  }

  // In case of BackForwardCache, page is evicted directly from the cache and
  // deleted immediately, without waiting for unload handlers.
  SetLifecycleState(ShouldWaitForUnloadHandlers()
                        ? LifecycleStateImpl::kRunningUnloadHandlers
                        : LifecycleStateImpl::kReadyToBeDeleted);
}

void RenderFrameHostImpl::RenderFrameCreated() {
  // In https://crbug.com/1146573 a WebContentsObserver was causing the frame to
  // be reinitialized during deletion. It is not valid to re-enter navigation
  // code like that and it led to an invalid state. This is not a DCHECK because
  // the corruption will not be visible until later, making the bug very
  // difficult to understand.
  CHECK_NE(render_frame_state_, RenderFrameState::kDeleting);
  // We should not create new RenderFrames while `frame_tree_` is being
  // destroyed (e.g., via a WebContentsObserver during WebContents shutdown).
  // This seems to have caused crashes in https://crbug.com/717650.
  CHECK(!frame_tree_->IsBeingDestroyed());
  DCHECK_NE(render_frame_state_, RenderFrameState::kCreated);

  const RenderFrameState old_render_frame_state = render_frame_state_;
  render_frame_state_ = RenderFrameState::kCreated;

  if (old_render_frame_state == RenderFrameState::kDeleted) {
    // Dispatch update notification when a Page is recreated after a crash. Only
    // main RenderFrameHosts should ever be reused.
    DCHECK(is_main_frame());
    // Only a current RenderFrameHost should be recreating its RenderFrame
    // here, since speculative and pending deletion RenderFrameHosts get
    // deleted immediately after crash, whereas prerender gets cancelled and
    // bfcache entry gets evicted.
    DCHECK_EQ(lifecycle_state(), LifecycleStateImpl::kActive);
    GetPage().NotifyPageBecameCurrent();
  }

  // Initialize the RenderWidgetHost which marks it and the RenderViewHost as
  // live before calling to the `delegate_`.
  if (GetLocalRenderWidgetHost()) {
#if BUILDFLAG(IS_ANDROID) || defined(OHOS_EX_FORCE_ZOOM)
    GetLocalRenderWidgetHost()->SetForceEnableZoom(
        delegate_->GetOrCreateWebPreferences().force_enable_zoom);
#endif  // BUILDFLAG(IS_ANDROID)
    GetLocalRenderWidgetHost()->RendererWidgetCreated(
        /*for_frame_widget=*/true);
  }

  // Set up mojo connections to the renderer from the `frame_` connection before
  // notifying the delegate.
  SetUpMojoConnection();

  delegate_->RenderFrameCreated(this);

  if (enabled_bindings_)
    GetFrameBindingsControl()->AllowBindings(enabled_bindings_);

  if (web_ui_ && enabled_bindings_ & BINDINGS_POLICY_WEB_UI)
    web_ui_->SetUpMojoConnection();
}

void RenderFrameHostImpl::RenderFrameDeleted() {
  // In https://crbug.com/1146573 a WebContentsObserver was causing the frame to
  // be reinitialized during deletion. It is not valid to re-enter navigation
  // code like that and it led to an invalid state. This is not a DCHECK because
  // the corruption will cause a crash but later, making the bug very
  // difficult to understand.
  CHECK_NE(render_frame_state_, RenderFrameState::kDeleting);
  bool was_created = is_render_frame_created();
  render_frame_state_ = RenderFrameState::kDeleting;
  render_frame_scoped_weak_ptr_factory_.InvalidateWeakPtrs();

  // If the current status is different than the new status, the delegate
  // needs to be notified.
  if (was_created) {
    delegate_->RenderFrameDeleted(this);
  }
  TearDownMojoConnection();

#if !BUILDFLAG(IS_ANDROID)
  HostZoomMap* host_zoom_map = HostZoomMap::Get(GetSiteInstance());
  host_zoom_map->ClearTemporaryZoomLevel(GetGlobalId());
#endif  // !BUILDFLAG(IS_ANDROID)

  if (web_ui_) {
    web_ui_->RenderFrameDeleted();
    web_ui_->TearDownMojoConnection();
  }
  render_frame_state_ = RenderFrameState::kDeleted;
}

void RenderFrameHostImpl::SwapIn() {
  GetAssociatedLocalFrame()->SwapInImmediately();
}

void RenderFrameHostImpl::Init() {
  // This is only called on the main frame, for renderer-created windows. These
  // windows wait for the renderer to signal that we can show them and begin
  // navigations.
  DCHECK(is_main_frame());
  DCHECK(waiting_for_init_);

  waiting_for_init_ = false;

  GetLocalRenderWidgetHost()->Init();

  // TODO(danakj): We only blocked the main frame, so we should only need to
  // resume that?
  ForEachRenderFrameHostIncludingSpeculative(
      [this](RenderFrameHostImpl* render_frame_host) {
        // Inner frame trees shouldn't be possible here.
        DCHECK_EQ(render_frame_host->frame_tree(), frame_tree());

        if (render_frame_host->IsRenderFrameLive())
          render_frame_host->frame_->ResumeBlockedRequests();
      });

  if (pending_navigate_) {
    // Transfer `pending_navigate_` to a local variable, to avoid resetting it
    // after OnBeginNavigation since `this` might already be destroyed (see
    // below).
    //
    // This shouldn't matter for early RFH swaps out of crashed frames, since
    // `pending_navigate_` is cleared when the renderer process dies, but it
    // may matter for other current/future use cases of the early RFH swap.
    std::unique_ptr<PendingNavigation> pending_navigation =
        std::move(pending_navigate_);
    frame_tree_node()->navigator().OnBeginNavigation(
        frame_tree_node(), std::move(pending_navigation->common_params),
        std::move(pending_navigation->begin_navigation_params),
        std::move(pending_navigation->blob_url_loader_factory),
        std::move(pending_navigation->navigation_client),
        EnsurePrefetchedSignedExchangeCache(),
        std::move(pending_navigation->renderer_cancellation_listener));
    // DO NOT ADD CODE after this, as `this` might be deleted if an early
    // RenderFrameHost swap was performed when starting the navigation above.
  }
}

RenderFrameProxyHost* RenderFrameHostImpl::GetProxyToParent() {
  if (is_main_frame())
    return nullptr;

  return browsing_context_state_->GetRenderFrameProxyHost(
      GetParent()->GetSiteInstance()->group());
}

RenderFrameProxyHost* RenderFrameHostImpl::GetProxyToOuterDelegate() {
  // Precondition:RFH in subframe, in pending deletion, speculative, or in the
  // BFCache are not expected to access the outer WebContents.
  CHECK(lifecycle_state_ == LifecycleStateImpl::kActive ||
        lifecycle_state_ == LifecycleStateImpl::kPrerendering);
  DCHECK(is_main_frame());

  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
  return owner_->GetRenderFrameHostManager().GetProxyToOuterDelegate();
}

void RenderFrameHostImpl::DidChangeReferrerPolicy(
    network::mojom::ReferrerPolicy referrer_policy) {
  if (!IsActive())
    return;
  DCHECK(owner_);  // See `owner_` invariants about `IsActive()`.
  owner_->DidChangeReferrerPolicy(referrer_policy);
}

void RenderFrameHostImpl::PropagateEmbeddingTokenToParentFrame() {
  // Protect against calls from WebContentsImpl::AttachInnerWebContents() that
  // happen before RFHI::SetEmbeddingToken() gets called, which is where the
  // token gets set. It's safe to early return in those cases since this method
  // will get called anyway by RFHI::SetEmbeddingToken(), at which time the
  // outer delegate for the inner web contents will have been created already.
  if (!embedding_token_)
    return;

  // We need to propagate the token to the parent frame if it's either remote or
  // part of an outer web contents, therefore we need to figure out the right
  // proxy to send the token to, if any.
  // For local parents the propagation occurs within the renderer process. The
  // token is also present on the main frame for generalization when the main
  // frame in embedded in another context (e.g. browser UI). The main frame is
  // not embedded in the context of the frame tree so it is not propagated here.
  // See RenderFrameHost::GetEmbeddingToken for more details.
  RenderFrameProxyHost* target_render_frame_proxy = nullptr;

  if (RequiresProxyToParent()) {
    // This subframe should have a remote parent frame.
    target_render_frame_proxy = GetProxyToParent();
    DCHECK(target_render_frame_proxy);
  } else if (is_main_frame()) {
    // The main frame in an inner web contents could have a delegate in the
    // outer web contents, so we need to account for that as well.
    target_render_frame_proxy = GetProxyToOuterDelegate();
  }

  // Propagate the token to the right process, if a proxy was found.
  if (target_render_frame_proxy &&
      target_render_frame_proxy->is_render_frame_proxy_live()) {
    target_render_frame_proxy->GetAssociatedRemoteFrame()->SetEmbeddingToken(
        embedding_token_.value());
  }
}

void RenderFrameHostImpl::OnAudibleStateChanged(bool is_audible) {
  DCHECK_NE(is_audible_, is_audible);
  if (is_audible) {
    DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kPrerendering);
    GetProcess()->OnMediaStreamAdded();
  } else {
    GetProcess()->OnMediaStreamRemoved();
  }
  is_audible_ = is_audible;
  delegate_->OnFrameAudioStateChanged(this, is_audible_);
}

void RenderFrameHostImpl::DidAddMessageToConsole(
    blink::mojom::ConsoleMessageLevel log_level,
    const std::u16string& message,
    uint32_t line_no,
    const absl::optional<std::u16string>& source_id,
    const absl::optional<std::u16string>& untrusted_stack_trace) {
  std::u16string updated_source_id;
  if (source_id.has_value())
    updated_source_id = *source_id;
  if (delegate_->DidAddMessageToConsole(this, log_level, message, line_no,
                                        updated_source_id,
                                        untrusted_stack_trace)) {
    return;
  }

  // Pass through log severity only on builtin components pages to limit console
  // spew.
  const bool is_web_ui = HasWebUIScheme(GetMainFrame()->GetLastCommittedURL());
  if (is_web_ui) {
    DCHECK_EQ(GetMainFrame(), GetOutermostMainFrame())
        << "The mainframe and outermost mainframe should be the same in WebUI.";
  }
  const bool is_builtin_component =
      is_web_ui ||
      GetContentClient()->browser()->IsBuiltinComponent(
          GetProcess()->GetBrowserContext(), GetLastCommittedOrigin());
  const bool is_off_the_record =
      GetSiteInstance()->GetBrowserContext()->IsOffTheRecord();

  LogConsoleMessage(log_level, message, line_no, is_builtin_component,
                    is_off_the_record, updated_source_id);
}

void RenderFrameHostImpl::FrameSizeChanged(const gfx::Size& frame_size) {
  frame_size_ = frame_size;
  delegate_->FrameSizeChanged(this, frame_size);
}

void RenderFrameHostImpl::RendererDidActivateForPrerendering() {
  // RendererDidActivateForPrerendering() is called after the renderer has
  // notified that it fired the prerenderingchange event on the documents. The
  // browser now runs any binders that were deferred during prerendering. This
  // corresponds to the following steps of the activate algorithm:
  //
  // https://wicg.github.io/nav-speculation/prerendering.html#prerendering-browsing-context-activate
  // Step 8.3.4. "For each steps in doc's post-prerendering activation steps
  // list:"
  // Step 8.3.4.1. "Run steps."

  // Release Mojo capability control to run the binders. The RenderFrameHostImpl
  // may have been created after activation started, in which case it already
  // does not have Mojo capability control applied.
  if (mojo_binder_policy_applier_) {
    mojo_binder_policy_applier_->GrantAll();

    // As per `ReleaseMojoBinderPolicies` method requirement, the policy applier
    // owner should call the method when destroying the object, so we first
    // need to call this method before resetting the unique pointer.
    broker_.ReleaseMojoBinderPolicies();
    mojo_binder_policy_applier_.reset();
  }
}

void RenderFrameHostImpl::SetCrossOriginOpenerPolicyReporter(
    std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter) {
  coop_access_report_manager_.set_coop_reporter(std::move(coop_reporter));
}

bool RenderFrameHostImpl::IsCredentialless() const {
  return policy_container_host_->policies().is_credentialless;
}

bool RenderFrameHostImpl::IsLastCrossDocumentNavigationStartedByUser() const {
  return last_cross_document_navigation_started_by_user_;
}

void RenderFrameHostImpl::OnCreateChildFrame(
    int new_routing_id,
    mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
        browser_interface_broker_receiver,
    blink::mojom::PolicyContainerBindParamsPtr policy_container_bind_params,
    mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
        associated_interface_provider_receiver,
    blink::mojom::TreeScopeType scope,
    const std::string& frame_name,
    const std::string& frame_unique_name,
    bool is_created_by_script,
    const blink::LocalFrameToken& frame_token,
    const base::UnguessableToken& devtools_frame_token,
    const blink::DocumentToken& document_token,
    const blink::FramePolicy& frame_policy,
    const blink::mojom::FrameOwnerProperties& frame_owner_properties,
    blink::FrameOwnerElementType owner_type,
    ukm::SourceId document_ukm_source_id) {
  // TODO(lukasza): Call ReceivedBadMessage when |frame_unique_name| is empty.
  DCHECK(!frame_unique_name.empty());
  DCHECK(browser_interface_broker_receiver.is_valid());
  DCHECK(policy_container_bind_params->receiver.is_valid());
  DCHECK(associated_interface_provider_receiver.is_valid());
  DCHECK(devtools_frame_token);

  // The RenderFrame corresponding to this host sent an IPC message to create a
  // child, but by the time we get here, it's possible for its process to have
  // disconnected (maybe due to browser shutdown). Ignore such messages.
  if (!is_render_frame_created())
    return;

  // Only active and prerendered documents are allowed to create child
  // frames.
  if (lifecycle_state() != LifecycleStateImpl::kPrerendering) {
    // The RenderFrame corresponding to this host sent an IPC message to create
    // a child, but by the time we get here, it's possible for the
    // RenderFrameHost to become inactive. Ignore such messages.
    if (IsInactiveAndDisallowActivation(
            DisallowActivationReasonId::kCreateChildFrame))
      return;
  }

  // `new_routing_id`, `frame_token`, `devtools_frame_token` and
  // `document_token` were generated on the browser's IO thread and not taken
  // from the renderer process.
  FrameTreeNode* new_frame_tree_node = frame_tree_->AddFrame(
      this, GetProcess()->GetID(), new_routing_id, std::move(frame_remote),
      std::move(browser_interface_broker_receiver),
      std::move(policy_container_bind_params),
      std::move(associated_interface_provider_receiver), scope, frame_name,
      frame_unique_name, is_created_by_script, frame_token,
      devtools_frame_token, document_token, frame_policy,
      frame_owner_properties, was_discarded_, owner_type,
      /*is_dummy_frame_for_inner_tree=*/false);

  // Record the DocumentCreated identifiability metric for initial empty
  // documents in child frames (other cases are taken care of by the
  // NavigationRequest).
  //
  // Note: We do not want to record the corresponding DocumentCreated UKM event
  // here, see https://crbug.com/1326431.
  new_frame_tree_node->current_frame_host()->RecordDocumentCreatedUkmEvent(
      GetLastCommittedOrigin(), document_ukm_source_id, ukm::UkmRecorder::Get(),
      /*only_record_identifiability_metric=*/true);
}

void RenderFrameHostImpl::CreateChildFrame(
    int new_routing_id,
    mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
        browser_interface_broker_receiver,
    blink::mojom::PolicyContainerBindParamsPtr policy_container_bind_params,
    mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
        associated_interface_provider_receiver,
    blink::mojom::TreeScopeType scope,
    const std::string& frame_name,
    const std::string& frame_unique_name,
    bool is_created_by_script,
    const blink::FramePolicy& frame_policy,
    blink::mojom::FrameOwnerPropertiesPtr frame_owner_properties,
    blink::FrameOwnerElementType owner_type,
    ukm::SourceId document_ukm_source_id) {
  blink::LocalFrameToken frame_token;
  base::UnguessableToken devtools_frame_token;
  blink::DocumentToken document_token;
  if (!static_cast<RenderProcessHostImpl*>(GetProcess())
           ->TakeFrameTokensForFrameRoutingID(new_routing_id, frame_token,
                                              devtools_frame_token,
                                              document_token)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_CREATE_CHILD_FRAME_TOKENS_NOT_FOUND);
    return;
  }

  // Documents create iframes, iframes host new documents. Both are associated
  // with sandbox flags. They are required to be stricter or equal to their
  // owner when they are created, as we go down.
  if (frame_policy.sandbox_flags !=
      (frame_policy.sandbox_flags | active_sandbox_flags())) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_CREATE_CHILD_FRAME_SANDBOX_FLAGS);
    return;
  }

  // TODO(crbug.com/1145708). The interface exposed to tests should
  // match the mojo interface.
  OnCreateChildFrame(new_routing_id, std::move(frame_remote),
                     std::move(browser_interface_broker_receiver),
                     std::move(policy_container_bind_params),
                     std::move(associated_interface_provider_receiver), scope,
                     frame_name, frame_unique_name, is_created_by_script,
                     frame_token, devtools_frame_token, document_token,
                     frame_policy, *frame_owner_properties, owner_type,
                     document_ukm_source_id);
}

void RenderFrameHostImpl::DidNavigate(
    const mojom::DidCommitProvisionalLoadParams& params,
    NavigationRequest* navigation_request,
    bool was_within_same_document) {
  // The `url` and `origin` of the current document are stored in the
  // RenderFrameHost, because:
  // - The FrameTreeNode represents the frame.
  // - The RenderFrameHost represents a document hosted inside the frame.
  //
  // The URL is set regardless of whether it's for a net error or not.
  SetLastCommittedUrl(params.url);
  SetLastCommittedOrigin(params.origin);

  // If the navigation was a cross-document navigation and it's not the
  // synchronous about:blank commit, then it committed a document that is not
  // the initial empty document.
  if (!navigation_request->IsSameDocument() &&
      (!navigation_request->is_synchronous_renderer_commit() ||
       !navigation_request->GetURL().IsAboutBlank())) {
    navigation_request->frame_tree_node()->set_not_on_initial_empty_document();
  }

  // For uuid-in-package: resources served from WebBundles, use the Bundle's
  // origin.
  url::Origin origin =
      (params.url.SchemeIs(url::kUuidInPackageScheme) &&
       navigation_request->GetWebBundleURL().is_valid())
          ? url::Origin::Create(navigation_request->GetWebBundleURL())
          : GetLastCommittedOrigin();

  isolation_info_ = ComputeIsolationInfoInternal(
      origin, isolation_info_.request_type(),
      navigation_request->is_credentialless(),
      navigation_request->ComputeFencedFrameNonce());

  // Separately, update the frame's last successful URL except for net error
  // pages, since those do not end up in the correct process after transfers
  // (see https://crbug.com/560511).  Instead, the next cross-process navigation
  // or transfer should decide whether to swap as if the net error had not
  // occurred.
  // TODO(creis): Remove this block and always set the URL.
  // See https://crbug.com/588314.
  if (!navigation_request->DidEncounterError())
    last_successful_url_ = params.url;

  renderer_url_info_.last_document_url = GetLastDocumentURL(
      navigation_request, params, is_error_document_, renderer_url_info_);

  // Set the last committed HTTP method and POST ID. Note that we're setting
  // this here instead of in DidCommitNewDocument because same-document
  // navigations triggered by the History API (history.replaceState/pushState)
  // will reset the method to "GET" (while fragment navigations won't).
  // TODO(arthursonzogni): Stop relying on DidCommitProvisionalLoadParams. Use
  // the NavigationRequest instead. The browser process doesn't need to rely on
  // the renderer process.
  last_http_method_ = params.method;
  last_post_id_ = params.post_id;

  // TODO(arthursonzogni): Stop relying on DidCommitProvisionalLoadParams. Use
  // the NavigationRequest instead. The browser process doesn't need to rely on
  // the renderer process.
  last_http_status_code_ = params.http_status_code;

  // Sets whether the last navigation has user gesture/transient activation or
  // not.
  last_committed_common_params_has_user_gesture_ =
      navigation_request->common_params().has_user_gesture;

  // Sets whether the last cross-document navigation was initiated from the
  // browser (e.g. typing on the location bar) or from the renderer while having
  // transient user activation
  if (!was_within_same_document) {
    last_cross_document_navigation_started_by_user_ =
        !navigation_request->IsRendererInitiated() ||
        (navigation_request->begin_params()
             .initiator_activation_and_ad_status !=
         blink::mojom::NavigationInitiatorActivationAndAdStatus::
             kDidNotStartWithTransientActivation);
  }

  // Navigations that activate an existing bfcached or prerendered document do
  // not create a new document.
  bool did_create_new_document =
      !navigation_request->IsPageActivation() && !was_within_same_document;
  if (did_create_new_document)
    DidCommitNewDocument(params, navigation_request);

  // When the frame hosts a different document, its state must be replicated
  // via its proxies to the other processes where it appears as remote.
  //
  // This includes new documents. It also includes documents restored from the
  // BackForwardCache. This is because the cached state in
  // BrowsingContextState::replication_state_ needs to be refreshed with the
  // actual values.
  if (!navigation_request->IsSameDocument()) {
    // Permissions policy's inheritance from parent frame's permissions policy
    // is through accessing parent frame's security context(either remote or
    // local) when initializing child's security context, so the update to
    // proxies is needed.
    browsing_context_state_->UpdateFramePolicyHeaders(
        active_sandbox_flags(), permissions_policy_header_);
    // Document policy's inheritance from parent frame's required document
    // policy is done at |HTMLFrameOwnerElement::UpdateRequiredPolicy|. Parent
    // frame owns both parent's required document policy and child frame's frame
    // owner element which contains child's required document policy, so there
    // is no need to store required document policy in proxies.
  }
}

void RenderFrameHostImpl::SetLastCommittedOrigin(const url::Origin& origin) {
  last_committed_origin_ = origin;
}

void RenderFrameHostImpl::SetInheritedBaseUrl(const GURL& inherited_base_url) {
  if (blink::features::IsNewBaseUrlInheritanceBehaviorEnabled()) {
    inherited_base_url_ = inherited_base_url;
  }
}

void RenderFrameHostImpl::SetLastCommittedOriginForTesting(
    const url::Origin& origin) {
  SetLastCommittedOrigin(origin);
}

const url::Origin& RenderFrameHostImpl::ComputeTopFrameOrigin(
    const url::Origin& frame_origin) const {
  if (is_main_frame()) {
    return frame_origin;
  }

  DCHECK(parent_);
  // It's important to go through parent_ rather than via
  // frame_free_->root() here in case we're in process of being deleted, as the
  // latter might point to what our ancestor is being replaced with rather than
  // the actual ancestor.
  RenderFrameHostImpl* host = parent_;
  while (host->parent_) {
    host = host->parent_;
  }
  return host->GetLastCommittedOrigin();
}

void RenderFrameHostImpl::SetStorageKey(const blink::StorageKey& storage_key) {
  storage_key_ = storage_key;
}

net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoForNavigation(
    const GURL& destination) {
  return ComputeIsolationInfoForNavigation(
      destination, IsCredentialless(),
      /*fenced_frame_nonce_for_navigation=*/absl::nullopt);
}

net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoForNavigation(
    const GURL& destination,
    bool is_credentialless,
    absl::optional<base::UnguessableToken> fenced_frame_nonce_for_navigation) {
  net::IsolationInfo::RequestType request_type =
      is_main_frame() ? net::IsolationInfo::RequestType::kMainFrame
                      : net::IsolationInfo::RequestType::kSubFrame;
  return ComputeIsolationInfoInternal(url::Origin::Create(destination),
                                      request_type, is_credentialless,
                                      fenced_frame_nonce_for_navigation);
}

net::IsolationInfo
RenderFrameHostImpl::ComputeIsolationInfoForSubresourcesForPendingCommit(
    const url::Origin& main_world_origin,
    bool is_credentialless,
    absl::optional<base::UnguessableToken> fenced_frame_nonce_for_navigation) {
  return ComputeIsolationInfoInternal(
      main_world_origin, net::IsolationInfo::RequestType::kOther,
      is_credentialless, fenced_frame_nonce_for_navigation);
}

net::SiteForCookies RenderFrameHostImpl::ComputeSiteForCookies() {
  return isolation_info_.site_for_cookies();
}

net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal(
    const url::Origin& frame_origin,
    net::IsolationInfo::RequestType request_type,
    bool is_credentialless,
    absl::optional<base::UnguessableToken> fenced_frame_nonce_for_navigation) {
  url::Origin top_frame_origin = ComputeTopFrameOrigin(frame_origin);
  net::SchemefulSite top_frame_site = net::SchemefulSite(top_frame_origin);

  net::SiteForCookies candidate_site_for_cookies(top_frame_site);

  std::set<net::SchemefulSite> party_context;

  // Walk up the frame tree to check SiteForCookies and compute the
  // |party_context|.
  //
  // If |request_type| is kOther, then IsolationInfo is being computed
  // for subresource requests. Check/compute starting from the frame itself.
  // Otherwise, it's OK to skip checking the frame itself since it's stored in
  // IsolationInfo for each request and will be validated later anyway.
  //
  // If origins are not consistent with the candidate SiteForCookies
  // of the main document, SameSite cookies may not be used.
  const RenderFrameHostImpl* initial_rfh = this;
  if (request_type != net::IsolationInfo::RequestType::kOther)
    initial_rfh = this->parent_;

  for (const RenderFrameHostImpl* rfh = initial_rfh; rfh; rfh = rfh->parent_) {
    const url::Origin& cur_origin =
        rfh == this ? frame_origin : rfh->last_committed_origin_;
    net::SchemefulSite cur_site = net::SchemefulSite(cur_origin);

    if (top_frame_site != cur_site) {
      party_context.insert(cur_site);
    }
    candidate_site_for_cookies.CompareWithFrameTreeSiteAndRevise(cur_site);
  }

  // Reset the SiteForCookies if the top frame origin is of a scheme that should
  // always be treated as the SiteForCookies.
  if (GetContentClient()
          ->browser()
          ->ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
              top_frame_origin.scheme(),
              GURL::SchemeIsCryptographic(frame_origin.scheme()))) {
    candidate_site_for_cookies = net::SiteForCookies(top_frame_site);
  }

  absl::optional<base::UnguessableToken> nonce =
      ComputeNonce(is_credentialless, fenced_frame_nonce_for_navigation);
  return net::IsolationInfo::Create(request_type, top_frame_origin,
                                    frame_origin, candidate_site_for_cookies,
                                    std::move(party_context), nonce);
}

absl::optional<base::UnguessableToken> RenderFrameHostImpl::ComputeNonce(
    bool is_credentialless,
    absl::optional<base::UnguessableToken> fenced_frame_nonce_for_navigation) {
  // If it's a credentialless frame tree, use its nonce even if it's within a
  // fenced frame tree to maintain the guarantee that a credentialless frame
  // tree has a unique nonce.
  if (is_credentialless) {
    RenderFrameHostImpl* main_rfh = this;
    while (main_rfh->parent_ && !main_rfh->IsFencedFrameRoot()) {
      main_rfh = main_rfh->parent_;
    }
    return main_rfh->credentialless_iframes_nonce();
  }

  // Otherwise, use the fenced frame nonce for this navigation.
  // If this call is for a pending navigation, the fenced frame nonce should
  // have been computed with `NavigationRequest::ComputeFencedFrameNonce()` and
  // passed in `fenced_frame_nonce_for_navigation`. If there is no navigation
  // associated with this call, then we get the nonce from this RFHI's
  // FrameTreeNode with FrameTreeNode::GetFencedFrameNonce().
  //
  // Note that MPArch will ensure that fenced frame tree within an
  // credentialless iframe does not have `is_credentialless` set to true. The
  // nonce was moved from PageImpl to RenderFrameHostImpl to fix
  // crbug.com/1287458. In the case of a credentialless iframe embedded in a
  // fenced frame, we get the `credentialless_iframes_nonce_` of the fenced
  // frame root to prevent credentialless iframes embedded inside a fenced frame
  // from sharing nonce with credentialless iframes outside the fenced frame.
  if (fenced_frame_nonce_for_navigation.has_value()) {
    return fenced_frame_nonce_for_navigation;
  }
  return frame_tree_node_->GetFencedFrameNonce();
}

bool RenderFrameHostImpl::IsThirdPartyStoragePartitioningEnabled(
    RenderFrameHostImpl* main_frame_for_storage_partitioning) {
  // If we're in the main frame the state of third-party storage partitioning
  // doesn't matter as the StorageKey will be first-party no matter what.
  if (main_frame_for_storage_partitioning == this) {
    return false;
  }

  RuntimeFeatureStateDocumentData* rfs_document_data_for_storage_key =
      RuntimeFeatureStateDocumentData::GetForCurrentDocument(
          main_frame_for_storage_partitioning);

  DCHECK(rfs_document_data_for_storage_key);

  // If the deprecation trial is enabled, we have directive to override the
  // current value of net::features::ThirdPartyStoragePartitioning.
  if (rfs_document_data_for_storage_key->runtime_feature_state_read_context()
          .IsDisableThirdPartyStoragePartitioningEnabled()) {
    return false;
  }
  // If the enterprise policy blocks, we have directive to override the
  // current value of net::features::ThirdPartyStoragePartitioning.
  // We can safely read the last comitted-origin (even during navigation)
  // as we know we are not in the main-frame since that case is filtered above.
  if (!GetContentClient()->browser()->IsThirdPartyStoragePartitioningAllowed(
          GetBrowserContext(),
          main_frame_for_storage_partitioning->GetLastCommittedOrigin())) {
    return false;
  }
  return blink::StorageKey::IsThirdPartyStoragePartitioningEnabled();
}

blink::StorageKey RenderFrameHostImpl::CalculateStorageKey(
    const url::Origin& new_rfh_origin,
    const base::UnguessableToken* nonce) {
  if (nonce) {
    // If the nonce isn't null, we can use the simpler form of the constructor.
    return blink::StorageKey::CreateWithNonce(new_rfh_origin, *nonce);
  }

  if (base::FeatureList::IsEnabled(
          features::kShouldAllowFirstPartyStorageKeyOverrideFromEmbedder) &&
      GetContentClient()->browser()->ShouldUseFirstPartyStorageKey(
          new_rfh_origin)) {
    // Extension subframes should not take their top-level site into account
    // when determining storage access. Thus, we construct all extension frame
    // StorageKeys as first-party using the extension origin.
    return blink::StorageKey::CreateFirstParty(new_rfh_origin);
  }

  std::vector<RenderFrameHostImpl*> ancestor_chain;
  RenderFrameHostImpl* current = this;
  while (current) {
    ancestor_chain.push_back(current);
    current = current->parent_;
  }

  // Make sure to always use the |new_rfh_origin| when referring to the current
  // RenderFrameHost. The origin might differ when the RenderFrameHost is reused
  // when SiteIsolation is off.
  auto origin = [&](RenderFrameHostImpl* rfh) {
    return rfh == this ? new_rfh_origin : rfh->GetLastCommittedOrigin();
  };

  // When the top level RenderFrameHost is a Chrome extension, with host
  // permissions to its child in the ancestor chain, then behave "as-if" the
  // child was the top-level one computing the StorageKey.
  //
  // https://github.com/wanderview/quota-storage-partitioning/blob/main/explainer.md#interaction-with-extension-pages
  //
  // Sites with host permissions are saved in
  // `browser_context->GetSharedCorsOriginAccessList()` because they are also
  // used to bypass CORS restrictions. We can reuse this permissions list here
  // because sites that are explicitly granted access permissions should also be
  // able to access partitioned storage based not partitioned by the top level
  // extension URL. A origin will only have access to another origin via
  // OriginAccessList if the origin is an extension.
  bool ignore_top_level_extension =
      !is_main_frame() &&
      GetBrowserContext()
              ->GetSharedCorsOriginAccessList()
              ->GetOriginAccessList()
              .CheckAccessState(origin(ancestor_chain.end()[-1]),
                                origin(ancestor_chain.end()[-2]).GetURL()) ==
          network::cors::OriginAccessList::AccessState::kAllowed;
  if (ignore_top_level_extension) {
    ancestor_chain.pop_back();
  }
  net::SchemefulSite top_level_site(origin(ancestor_chain.back()));

  // Compute the AncestorChainBit. It represents whether every ancestors are
  // all same-site or not. If `origin` or `top_level_site` is opaque the bit
  // must be kCrossSite as this is the default (which won't be serialized).
  blink::mojom::AncestorChainBit ancestor_chain_bit =
      blink::mojom::AncestorChainBit::kSameSite;
  if (!new_rfh_origin.opaque() && !top_level_site.opaque()) {
    for (auto* ancestor : ancestor_chain) {
      if (top_level_site != net::SchemefulSite(origin(ancestor))) {
        ancestor_chain_bit = blink::mojom::AncestorChainBit::kCrossSite;
        break;
      }
    }
  } else {
    ancestor_chain_bit = blink::mojom::AncestorChainBit::kCrossSite;
  }

  // We want the RuntimeFeatureStateReadContext from the effective main frame
  // (keeping in mind `ignore_top_level_extension`).
  bool is_third_party_storage_partitioning_allowed =
      IsThirdPartyStoragePartitioningEnabled(ancestor_chain.back());

  return blink::StorageKey::Create(new_rfh_origin, top_level_site,
                                   ancestor_chain_bit,
                                   is_third_party_storage_partitioning_allowed);
}

void RenderFrameHostImpl::SetOriginDependentStateOfNewFrame(
    RenderFrameHostImpl* creator_frame) {
  // This method should only be called for *new* frames, that haven't committed
  // a navigation yet.
  DCHECK(!has_committed_any_navigation_);
  DCHECK(GetLastCommittedOrigin().opaque());

  url::Origin creator_origin =
      creator_frame ? creator_frame->GetLastCommittedOrigin() : url::Origin();

  // Calculate and set |new_frame_origin|.
  bool new_frame_should_be_sandboxed =
      network::mojom::WebSandboxFlags::kOrigin ==
      (browsing_context_state_->active_sandbox_flags() &
       network::mojom::WebSandboxFlags::kOrigin);
  url::Origin new_frame_origin = new_frame_should_be_sandboxed
                                     ? creator_origin.DeriveNewOpaqueOrigin()
                                     : creator_origin;
  isolation_info_ = ComputeIsolationInfoInternal(
      new_frame_origin, net::IsolationInfo::RequestType::kOther,
      IsCredentialless(),
      /*fenced_frame_nonce_for_navigation=*/absl::nullopt);
  SetLastCommittedOrigin(new_frame_origin);

  if (creator_frame) {
    // If we're given a parent/opener frame, copy the
    // RuntimeFeatureStateReadContext.
    RuntimeFeatureStateDocumentData* rfs_document_data_from_creator =
        RuntimeFeatureStateDocumentData::GetForCurrentDocument(creator_frame);
    DCHECK(rfs_document_data_from_creator);
    RuntimeFeatureStateDocumentData::CreateForCurrentDocument(
        this,
        rfs_document_data_from_creator->runtime_feature_state_read_context());
  } else {
    // Otherwise create a RuntimeFeatureStateContext. We need to construct a
    // RuntimeFeatureStateContext because its constructor initializes default
    // values while the RuntimeFeatureStateReadContext's doesn't.
    RuntimeFeatureStateDocumentData::CreateForCurrentDocument(
        this, blink::RuntimeFeatureStateContext());
  }

  // For the StorageKey, we want the main frame's
  // RuntimeFeatureStateReadContext.
  SetStorageKey(CalculateStorageKey(
      new_frame_origin, base::OptionalToPtr(isolation_info_.nonce())));

  // Apply private network request policy according to our new origin.
  if (GetContentClient()->browser()->ShouldAllowInsecureLocalNetworkRequests(
          GetBrowserContext(), new_frame_origin)) {
    local_network_request_policy_ =
        network::mojom::LocalNetworkRequestPolicy::kAllow;
  }

  // Construct the frame's permissions policy only once we know its initial
  // committed origin. It's necessary to wait for the origin because the
  // permissions policy's state depends on the origin, so the PermissionsPolicy
  // object could be configured incorrectly if it were initialized before
  // knowing the value of |last_committed_origin_|. More at crbug.com/1112959.
  ResetPermissionsPolicy();
}

FrameTreeNode* RenderFrameHostImpl::AddChild(
    std::unique_ptr<FrameTreeNode> child,
    int frame_routing_id,
    mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
    const blink::LocalFrameToken& frame_token,
    const blink::DocumentToken& document_token,
    base::UnguessableToken devtools_frame_token,
    const blink::FramePolicy& frame_policy,
    std::string frame_name,
    std::string frame_unique_name) {
  DCHECK(lifecycle_state_ == LifecycleStateImpl::kActive ||
         lifecycle_state_ == LifecycleStateImpl::kPrerendering);

  // Initialize the RenderFrameHost for the new node.  We always create child
  // frames in the same SiteInstance as the current frame, and they can swap to
  // a different one if they navigate away.
  child->render_manager()->InitChild(
      GetSiteInstance(), frame_routing_id, std::move(frame_remote), frame_token,
      document_token, devtools_frame_token, frame_policy, frame_name,
      frame_unique_name);

  // Other renderer processes in this BrowsingInstance may need to find out
  // about the new frame.  Create a proxy for the child frame in all
  // SiteInstances that have a proxy for the frame's parent, since all frames
  // in a frame tree should have the same set of proxies.
  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
  owner_->GetRenderFrameHostManager().CreateProxiesForChildFrame(child.get());

  // When the child is added, it hasn't committed any navigation yet - its
  // initial empty document should inherit the origin (the origin may change
  // after the first commit) and other state (such as the
  // RuntimeFeatureStateReadContext) from its parent. See also
  // https://crbug.com/932067.
  child->current_frame_host()->SetOriginDependentStateOfNewFrame(this);

  children_.push_back(std::move(child));
  return children_.back().get();
}

void RenderFrameHostImpl::RemoveChild(FrameTreeNode* child) {
  for (auto iter = children_.begin(); iter != children_.end(); ++iter) {
    if (iter->get() == child) {
      // Subtle: we need to make sure the node is gone from the tree before
      // observers are notified of its deletion.
      std::unique_ptr<FrameTreeNode> node_to_delete(std::move(*iter));
      children_.erase(iter);
      // TODO(dcheng): Removing a subtree is still very piecemeal and somewhat
      // buggy. The entire subtree should be removed as a group, but it can
      // actually happen incrementally. For example, given the frame tree:
      //
      //   A1(A2(B3(A4(B5))))
      //
      //
      // Suppose A1 executes:
      //
      //   window.frames[0].frameElement.remove();
      //
      // What ends up happening is this:
      //
      // 1. Renderer A detaches the subtree beginning at A2.
      // 2. Renderer A starts detaching A2.
      // 3. Renderer A starts detaching B3.
      // 4. Renderer A starts detaching A4.
      // 5. Renderer A starts detaching B5
      // 6. Renderer A reports B5 is complete detaching with the Mojo IPC
      //    `RenderFrameProxyHost::Detach()`, which calls
      //    `RenderFrameHostImpl::DetachFromProxy()`. `DetachFromProxy()`
      //    deletes RenderFrame B5 in renderer B, which means that the unload
      //    handler for B5 runs immediately--before the unload handler for B3.
      //    However, per the spec, the right order to run unload handlers is
      //    top-down (e.g. B3's unload handler should run before B5's in this
      //    scenario).
      node_to_delete->current_frame_host()->DeleteRenderFrame(
          mojom::FrameDeleteIntention::kNotMainFrame);
      RenderFrameHostImpl* speculative_frame_host =
          node_to_delete->render_manager()->speculative_frame_host();
      if (speculative_frame_host) {
        if (speculative_frame_host->lifecycle_state() ==
            LifecycleStateImpl::kPendingCommit) {
          // A speculative RenderFrameHost that has reached `kPendingCommit` has
          // already sent a `CommitNavigation()` to the renderer. Any subsequent
          // IPCs will only be processed after the renderer has already swapped
          // in the provisional RenderFrame and swapped out the provisional
          // frame's reference frame (which is either a RenderFrame or a
          // `blink::RemoteFrame`).
          //
          // Since the swapped out `RenderFrame`/`blink::RemoteFrame` is already
          // gone, a `DeleteRenderFrame()` (routed to the RenderFrame) or a
          // `DetachAndDispose()` (routed to the `blink::RemoteFrame`) won't do
          // anything. The browser must also instruct the already-committed but
          // not-yet-acknowledged speculative RFH to detach itself as well.
          speculative_frame_host->DeleteRenderFrame(
              mojom::FrameDeleteIntention::kNotMainFrame);
        } else {
          // Otherwise, the provisional RenderFrame has not yet been instructed
          // to swap in but is already associated with the RenderFrame or
          // `blink::RemoteFrame` it is expected to replace. The associated
          // `RenderFrame`/`blink::RemoteFrame` (which is still in the frame
          // tree) will be responsible for tearing down any associated
          // provisional RenderFrame, so the browser does not need to take any
          // explicit cleanup actions.
        }
      }
      // No explicit cleanup is needed here for `RenderFrameProxyHost`s.
      // Destroying `FrameTreeNode` destroys the map of `RenderFrameProxyHost`s,
      // and `~RenderFrameProxyHost()` sends a Mojo `DetachAndDispose()` IPC for
      // child frame proxies.
      node_to_delete.reset();
      PendingDeletionCheckCompleted();  // Can delete |this|.
      // |this| is potentially deleted. Do not add code after this.
      return;
    }
  }
}

void RenderFrameHostImpl::ResetChildren() {
  // Remove child nodes from the tree, then delete them. This destruction
  // operation will notify observers. See https://crbug.com/612450 for
  // explanation why we don't just call the std::vector::clear method.
  std::vector<std::unique_ptr<FrameTreeNode>> children;
  children.swap(children_);
  // TODO(dcheng): Ideally, this would be done by messaging all the proxies of
  // this RenderFrameHostImpl to detach the current frame's children, rather
  // than messaging each child's current frame host...
  for (auto& child : children)
    child->current_frame_host()->DeleteRenderFrame(
        mojom::FrameDeleteIntention::kNotMainFrame);
}

void RenderFrameHostImpl::SetLastCommittedUrl(const GURL& url) {
  last_committed_url_ = url;
}

void RenderFrameHostImpl::Detach() {
  // Detach() can be called in both speculative and pending-commit states.
  // - a speculative RenderFrameHost as a result of its associated Frame being
  //   detached (i.e., the Frame in the renderer with a provisional_frame_ field
  //   that points to `this`'s LocalFrame). We don't expect it to self-detach
  //   otherwise.
  // - a pending commit RenderFrameHost might detach itself due to unload events
  //   running that remove it from the tree when swapping it in.
  //
  // In both cases speculative and pending-commit RenderFrameHosts, it's OK to
  // early-return. The logical FrameTreeNode is going to be torn down as well,
  // and the speculative / pending commit RenderFrameHost (which is still
  // strongly owned by the RenderFrameHostManager via unique_ptr) will be torn
  // down then. If we do proceed, this ends up with a use-after-free, since
  // StartPendingDeletionOnSubtree() will call
  // ResetAllNavigationsInSubtreeForFrameDetach(), which deletes `this`.
  if (lifecycle_state() == LifecycleStateImpl::kSpeculative ||
      lifecycle_state() == LifecycleStateImpl::kPendingCommit) {
    return;
  }

  if (!parent_) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_DETACH_MAIN_FRAME);
    return;
  }

  // A frame is removed while replacing this document with the new one. When it
  // happens, delete the frame and both the new and old documents. Unload
  // handlers aren't guaranteed to run here.
  if (is_waiting_for_unload_ack_) {
    parent_->RemoveChild(GetFrameTreeNodeForUnload());
    return;
  }

  // Ignore Detach Mojo API, if the RenderFrameHost should be left in pending
  // deletion state.
  if (do_not_delete_for_testing_)
    return;

  if (IsPendingDeletion()) {
    // The frame is pending deletion. Detach Mojo API is used to confirm its
    // unload handlers ran. Note that it is possible for a frame to already be
    // in kReadyToBeDeleted. This happens when this RenderFrameHost is pending
    // deletion and is waiting on one of its children to run its unload
    // handler. While running it, it can request its parent to detach itself.
    // See test: SitePerProcessBrowserTest.PartialUnloadHandler.
    if (lifecycle_state() != LifecycleStateImpl::kReadyToBeDeleted)
      SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);
    PendingDeletionCheckCompleted();  // Can delete |this|.
    // |this| is potentially deleted. Do not add code after this.
    return;
  }

  // This frame is being removed by the renderer, and it has already executed
  // its unload handler.
  SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);

  // Before completing the removal, we still need to wait for all of its
  // descendant frames to execute unload handlers. Start executing those
  // handlers now.
  StartPendingDeletionOnSubtree(PendingDeletionReason::kFrameDetach);
  frame_tree()->FrameUnloading(GetFrameTreeNodeForUnload());

  // Some children with no unload handler may be eligible for immediate
  // deletion. Cut the dead branches now. This is a performance optimization.
  PendingDeletionCheckCompletedOnSubtree();  // Can delete |this|.
  // |this| is potentially deleted. Do not add code after this.
}

void RenderFrameHostImpl::DidFailLoadWithError(const GURL& url,
                                               int32_t error_code) {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::DidFailLoadWithError",
              ChromeTrackEvent::kRenderFrameHost, *this, "error", error_code);

  // Cancel prerendering if DidFailLoadWithError is called on the outermost main
  // document during prerendering. Don't dispatch the DidFailLoad event in such
  // a case as the embedders are unaware of prerender page yet and shouldn't
  // show any user-visible changes from an inactive RenderFrameHost.
  if (!GetParentOrOuterDocument() &&
      CancelPrerendering(
          PrerenderCancellationReason(PrerenderFinalStatus::kDidFailLoad))) {
    return;
  }

  GURL validated_url(url);
  GetProcess()->FilterURL(false, &validated_url);

  delegate_->DidFailLoadWithError(this, validated_url, error_code);
}

void RenderFrameHostImpl::DidFocusFrame() {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::DidFocusFrame",
              ChromeTrackEvent::kRenderFrameHost, *this,
              ChromeTrackEvent::kSiteInstanceGroup,
              *GetSiteInstance()->group());
  // We don't handle this IPC signal for non-active RenderFrameHost.
  if (!IsActive())
    return;

  DCHECK(owner_);  // See `owner_` invariants about `IsActive()`.
  owner_->SetFocusedFrame(GetSiteInstance()->group());
}

void RenderFrameHostImpl::DidCallFocus() {
  // This should not occur for prerenders but may occur for pages in
  // the BackForwardCache depending on timing.
  if (!IsActive())
    return;
  delegate_->DidCallFocus();
}

void RenderFrameHostImpl::CancelInitialHistoryLoad() {
  // A Javascript navigation interrupted the initial history load.  Check if an
  // initial subframe cross-process navigation needs to be canceled as a result.
  // TODO(creis, clamy): Cancel any cross-process navigation.
  NOTIMPLEMENTED();
}

void RenderFrameHostImpl::DidChangeBackForwardCacheDisablingFeatures(
    BackForwardCacheBlockingDetails details) {
  renderer_reported_bfcache_blocking_details_ = std::move(details);

#if BUILDFLAG(IS_OHOS)
  if (GetBackForwardCacheDisablingFeatures().Has(blink::scheduler::WebSchedulerTrackedFeature::kEnableCacheNativeEmbed)) {
    LOG(INFO) << "NativeEmbed BFCache, render frame host received NativeEmbed feature, render frame host global id = " \
      << GetGlobalId();
  }
#endif

  MaybeEvictFromBackForwardCache();

  if (back_forward_cache_disabling_features_callback_for_testing_) {
    back_forward_cache_disabling_features_callback_for_testing_.Run(
        GetBackForwardCacheDisablingFeatures());
  }
}

using BackForwardCacheDisablingFeatureHandle =
    RenderFrameHostImpl::BackForwardCacheDisablingFeatureHandle;

BackForwardCacheDisablingFeatureHandle::
    BackForwardCacheDisablingFeatureHandle() {
  // |render_frame_host_| will be null, so this value is never used.
  feature_ = BackForwardCacheDisablingFeature::kDummy;
}

BackForwardCacheDisablingFeatureHandle::BackForwardCacheDisablingFeatureHandle(
    BackForwardCacheDisablingFeatureHandle&& other) = default;

BackForwardCacheDisablingFeatureHandle::BackForwardCacheDisablingFeatureHandle(
    RenderFrameHostImpl* render_frame_host,
    BackForwardCacheDisablingFeature feature)
    : render_frame_host_(render_frame_host->GetWeakPtr()), feature_(feature) {
  CHECK(render_frame_host_);
  render_frame_host_->OnBackForwardCacheDisablingFeatureUsed(feature_);
}

void RenderFrameHostImpl::OnBackForwardCacheDisablingFeatureUsed(
    BackForwardCacheDisablingFeature feature) {
  ++browser_reported_bfcache_disabling_features_counts_[feature];

  MaybeEvictFromBackForwardCache();
}

void RenderFrameHostImpl::OnBackForwardCacheDisablingStickyFeatureUsed(
    BackForwardCacheDisablingFeature feature) {
  OnBackForwardCacheDisablingFeatureUsed(feature);
}

void RenderFrameHostImpl::OnBackForwardCacheDisablingFeatureRemoved(
    BackForwardCacheDisablingFeature feature) {
  auto it = browser_reported_bfcache_disabling_features_counts_.find(feature);
  DCHECK(it->second >= 1);
  if (it->second == 1) {
    browser_reported_bfcache_disabling_features_counts_.erase(it);
  } else {
    --it->second;
  }
}

RenderFrameHostImpl::BackForwardCacheDisablingFeatures
RenderFrameHostImpl::GetBackForwardCacheDisablingFeatures() const {
  BackForwardCacheDisablingFeatures features;
  for (const auto& details : GetBackForwardCacheBlockingDetails()) {
    features.Put(static_cast<blink::scheduler::WebSchedulerTrackedFeature>(
        details->feature));
  }
  return features;
}

RenderFrameHostImpl::BackForwardCacheBlockingDetails
RenderFrameHostImpl::GetBackForwardCacheBlockingDetails() const {
  BackForwardCacheBlockingDetails combined_details_list =
      DedicatedWorkerHostsForDocument::GetOrCreateForCurrentDocument(
          const_cast<RenderFrameHostImpl*>(this))
          ->GetBackForwardCacheBlockingDetails();

  for (const auto& details : renderer_reported_bfcache_blocking_details_) {
    combined_details_list.push_back(details.Clone());
  }

  for (const auto& it : browser_reported_bfcache_disabling_features_counts_) {
    // Browser reported features do not have JS location details. Create a
    // blocking details struct with only the feature filled.
    auto details_ptr = blink::mojom::BlockingDetails::New();
    details_ptr->feature = static_cast<uint32_t>(it.first);
    combined_details_list.push_back(std::move(details_ptr));
  }
  return combined_details_list;
}

RenderFrameHostImpl::BackForwardCacheDisablingFeatureHandle
RenderFrameHostImpl::RegisterBackForwardCacheDisablingNonStickyFeature(
    BackForwardCacheDisablingFeature feature) {
  return BackForwardCacheDisablingFeatureHandle(this, feature);
}

bool RenderFrameHostImpl::IsFrozen() {
  // TODO(crbug.com/1081920): Account for non-bfcache freezing here as well.
  return lifecycle_state() == LifecycleStateImpl::kInBackForwardCache;
}

void RenderFrameHostImpl::DidCommitProvisionalLoad(
    mojom::DidCommitProvisionalLoadParamsPtr params,
    mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
  if (!MaybeInterceptCommitCallback(nullptr, &params, &interface_params))
    return;

  DCHECK(params);
  DidCommitNavigation(nullptr, std::move(params), std::move(interface_params));
}

void RenderFrameHostImpl::DidCommitPageActivation(
    NavigationRequest* committing_navigation_request,
    mojom::DidCommitProvisionalLoadParamsPtr params) {
  DCHECK(committing_navigation_request->IsPageActivation());
  DCHECK(is_main_frame());

  auto request = navigation_requests_.find(committing_navigation_request);
  CHECK(request != navigation_requests_.end());

  base::TimeTicks navigation_start =
      committing_navigation_request->NavigationStart();
  bool is_prerender_page_activation =
      committing_navigation_request->IsPrerenderedPageActivation();

  std::unique_ptr<NavigationRequest> owned_request = std::move(request->second);
  navigation_requests_.erase(committing_navigation_request);

  // Copy the prerendering frame replication state to have it available after
  // the navigation commit to be able to check that it didn't change, as
  // NavigationRequest will be destroyed by that point.
  blink::mojom::FrameReplicationState prerender_main_frame_replication_state;
  // Copy the prerendering trigger type and the embbeder histogram suffix for
  // metrics before NavigationRequest is destroyed.
  PrerenderTriggerType prerender_trigger_type;
  std::string prerender_embedder_histogram_suffix;
  if (is_prerender_page_activation) {
    prerender_main_frame_replication_state =
        owned_request->prerender_main_frame_replication_state();
    prerender_trigger_type = owned_request->GetPrerenderTriggerType();
    prerender_embedder_histogram_suffix =
        owned_request->GetPrerenderEmbedderHistogramSuffix();
  }

#if DCHECK_IS_ON()
  // We do not support activating a page while subframes have any ongoing
  // NavigationRequest with a NavigationEntry (this would happen on a navigation
  // to an existing entry; this is called a "history navigation"). This would be
  // tricky to support because the NavigationRequest would change its
  // NavigationController in the course of the activation, and while that may be
  // safe for a normal navigation, it has more implications for a history
  // navigation as it already has an associated NavigationEntry and we do not
  // transfer pending NavigationEntries during activation. Fortunately, for
  // prerendering, we do not expect there to be any ongoing history navigations
  // in a subframe, because we maintain a trivial session history, so check that
  // nav_entry_id() is 0 here. Reloading subframes are considered
  // renderer-initiated navigations and do not create a new navigation entry
  // when NavigationRequest is created.
  //
  // Note that due to PrerenderCommitDeferringCondition, the main frame should
  // have no ongoing NavigationRequest at all, so it is not checked here.
  ForEachRenderFrameHost([](RenderFrameHostImpl* rfh) {
    // Interested only in subframes.
    if (rfh->is_main_frame())
      return;
    for (const auto& pair : rfh->navigation_requests_)
      DCHECK_EQ(pair.first->nav_entry_id(), 0);
  });
#endif

  DidCommitNavigationInternal(std::move(owned_request), std::move(params),
                              /*same_document_params=*/nullptr);

  // If any load events occurred pre-activation and were deferred until
  // activation, dispatch them now. This must happen before DidStopLoading() is
  // called because observers expect them to occur before that.
  if (is_prerender_page_activation)
    GetPage().MaybeDispatchLoadEventsOnPrerenderActivation();

  // Try to dispatch DidStopLoading event (note that
  // RenderFrameHostImpl::DidStopLoading implementation won't dispatch the event
  // if the page is still loading). We dispatch it here as it hasn't been
  // dispatched pre-activation because the back-forward cache page is already
  // loaded, whereas, for initial prerendering navigation, prerendered page
  // might still be loading.
  DidStopLoading();

  if (is_prerender_page_activation) {
    // Record metric to check navigation time with prerender activation.
    base::TimeDelta delta = base::TimeTicks::Now() - navigation_start;
    RecordPrerenderActivationTime(delta, prerender_trigger_type,
                                  prerender_embedder_histogram_suffix);

    // We haven't sent any updates to the proxies during prerendering
    // activation, so we need to ensure that the new frame replication being
    // stored in the browser is the same as the old and consistent with the
    // state we've sent to the renderers.
    // TODO - can we check main frame replication state?
    DCHECK(prerender_main_frame_replication_state ==
           frame_tree()->root()->current_replication_state());
  }
}

void RenderFrameHostImpl::StartLoadingForAsyncNavigationApiCommit() {
  // A same document navigation commit was requested, but was intercepted by
  // the navigation event. This should cause loading UI to appear, because
  // unlike other same-document navigations, this one is asynchronous. Note that
  // the renderer notifies the browser after a short delay, in order to lessen
  // the risk of the loading UI jittering if there are several short
  // asynchronous navigations in a row.
  LoadingState previous_frame_tree_loading_state =
      frame_tree()->LoadingTree()->GetLoadingState();
  loading_state_ = LoadingState::LOADING_UI_REQUESTED;
  frame_tree_node()->DidStartLoading(previous_frame_tree_loading_state);
}

void RenderFrameHostImpl::DidCommitSameDocumentNavigation(
    mojom::DidCommitProvisionalLoadParamsPtr params,
    mojom::DidCommitSameDocumentNavigationParamsPtr same_document_params) {
  TRACE_EVENT("navigation",
              "RenderFrameHostImpl::DidCommitSameDocumentNavigation",
              ChromeTrackEvent::kRenderFrameHost, this, "url",
              params->url.possibly_invalid_spec());

  // TODO(peilinwang): remove after the
  // kReduceToolbarUpdatesForSameDocNavigations experiment is complete.
  SCOPED_UMA_HISTOGRAM_TIMER(
      "Navigation.DidCommitSameDocumentNavigation.Duration");

  ScopedActiveURL scoped_active_url(params->url,
                                    GetMainFrame()->GetLastCommittedOrigin());
  ScopedCommitStateResetter commit_state_resetter(this);

  // When the frame is pending deletion, the browser is waiting for it to unload
  // properly. In the meantime, because of race conditions, it might tries to
  // commit a same-document navigation before unloading. Similarly to what is
  // done with cross-document navigations, such navigation are ignored. The
  // browser already committed to destroying this RenderFrameHost.
  // See https://crbug.com/805705 and https://crbug.com/930132.
  // TODO(ahemery): Investigate to see if this can be removed when the
  // NavigationClient interface is implemented.
  //
  // If this is called when the frame is in BackForwardCache, evict the frame
  // to avoid ignoring the renderer-initiated navigation, which the frame
  // might not expect.
  //
  // If this is called when the frame is in Prerendering, do not cancel
  // Prerendering as prerendered frames can be navigated, including
  // same-document navigations like push/replaceState.
  if (lifecycle_state() != LifecycleStateImpl::kPrerendering &&
      IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kCommitSameDocumentNavigation)) {
    return;
  }

  // Check if the navigation matches a stored same-document NavigationRequest.
  // In that case it is browser-initiated.
  auto request_entry =
      same_document_navigation_requests_.find(params->navigation_token);
  bool is_browser_initiated =
      (request_entry != same_document_navigation_requests_.end());
  std::unique_ptr<NavigationRequest> request =
      is_browser_initiated ? std::move(request_entry->second) : nullptr;
  same_document_navigation_requests_.erase(params->navigation_token);
  if (!MaybeInterceptCommitCallback(request.get(), &params, nullptr)) {
    return;
  }
  if (!DidCommitNavigationInternal(std::move(request), std::move(params),
                                   std::move(same_document_params))) {
    return;
  }

  // Since we didn't early return, it's safe to keep the commit state.
  commit_state_resetter.disable();
}

void RenderFrameHostImpl::DidOpenDocumentInputStream(const GURL& url) {
  // Check if the URL can actually be committed to the current origin. When
  // checking, the restrictions should be the same as a same-document navigation
  // since document.open() can only update the URL to a same-origin URL.
  if (!ValidateURLAndOrigin(url, last_committed_origin_,
                            /*is_same_document_navigation=*/true,
                            /*navigation_request=*/nullptr)) {
    return;
  }
  // Filter the URL, then update `renderer_url_info_`'s `last_document_url`.
  // Note that we won't update `last_committed_url_` because this doesn't really
  // count as committing a real navigation and won't update NavigationEntry etc.
  // See https://crbug.com/1046898 and https://github.com/whatwg/html/pull/6649
  // for more details.
  GURL filtered_url(url);
  GetProcess()->FilterURL(/*empty_allowed=*/false, &filtered_url);
  renderer_url_info_.last_document_url = filtered_url;
  // `owner_` could be null if we get this message asynchronously from the
  // renderer in pending deletion state.
  if (owner_) {
    owner_->DidOpenDocumentInputStream();
  }
}

RenderWidgetHostImpl* RenderFrameHostImpl::GetRenderWidgetHost() {
  RenderFrameHostImpl* frame = this;
  while (frame) {
    if (frame->GetLocalRenderWidgetHost())
      return frame->GetLocalRenderWidgetHost();
    frame = frame->GetParent();
  }

  NOTREACHED();
  return nullptr;
}

RenderWidgetHostView* RenderFrameHostImpl::GetView() {
  return GetRenderWidgetHost()->GetView();
}

GlobalRenderFrameHostId RenderFrameHostImpl::GetGlobalId() const {
  return GlobalRenderFrameHostId(GetProcess()->GetID(), GetRoutingID());
}

bool RenderFrameHostImpl::HasPendingCommitNavigation() const {
  return HasPendingCommitForCrossDocumentNavigation() ||
         !same_document_navigation_requests_.empty();
}

bool RenderFrameHostImpl::HasPendingCommitForCrossDocumentNavigation() const {
  return !navigation_requests_.empty();
}

NavigationRequest* RenderFrameHostImpl::GetSameDocumentNavigationRequest(
    const base::UnguessableToken& token) {
  auto request = same_document_navigation_requests_.find(token);
  return (request == same_document_navigation_requests_.end())
             ? nullptr
             : request->second.get();
}

void RenderFrameHostImpl::ResetOwnedNavigationRequests(
    NavigationDiscardReason reason) {
#ifdef OHOS_LOGGER_REPORT
  LOG_FEEDBACK(INFO) << "current lifecycle state: " << static_cast<int>(lifecycle_state_);
#endif
  if (ShouldQueueNavigationsWhenPendingCommitRFHExists() &&
      lifecycle_state_ == LifecycleStateImpl::kPendingCommit) {
    // With navigation queueing, pending commit navigations shouldn't get
    // canceled, unless the FrameTreeNode or renderer process
    // is gone/will be gone soon.
    CHECK(reason == NavigationDiscardReason::kRenderProcessGone ||
          reason == NavigationDiscardReason::kWillRemoveFrame);
  }
  // Move the NavigationRequests to new maps first before deleting them. This
  // avoids issues if a re-entrant call is made when a NavigationRequest is
  // being deleted (e.g., if the process goes away as the tab is closing).
  std::map<NavigationRequest*, std::unique_ptr<NavigationRequest>>
      navigation_requests;
  navigation_requests_.swap(navigation_requests);

  base::flat_map<base::UnguessableToken, std::unique_ptr<NavigationRequest>>
      same_document_navigation_requests;
  same_document_navigation_requests_.swap(same_document_navigation_requests);
}

void RenderFrameHostImpl::SetNavigationRequest(
    std::unique_ptr<NavigationRequest> navigation_request) {
  DCHECK(navigation_request);

  if (NavigationTypeUtils::IsSameDocument(
          navigation_request->common_params().navigation_type)) {
    loading_state_ = LoadingState::LOADING_WITHOUT_UI;
    same_document_navigation_requests_[navigation_request->commit_params()
                                           .navigation_token] =
        std::move(navigation_request);
    return;
  }
  loading_state_ = LoadingState::LOADING_UI_REQUESTED;
  navigation_requests_[navigation_request.get()] =
      std::move(navigation_request);
}

const scoped_refptr<NavigationOrDocumentHandle>&
RenderFrameHostImpl::GetNavigationOrDocumentHandle() {
  if (!document_associated_data_->navigation_or_document_handle()) {
    document_associated_data_->set_navigation_or_document_handle(
        NavigationOrDocumentHandle::CreateForDocument(GetGlobalId()));
  }
  return document_associated_data_->navigation_or_document_handle();
}

void RenderFrameHostImpl::Unload(RenderFrameProxyHost* proxy, bool is_loading) {
  // The end of this event is in OnUnloadACK when the RenderFrame has completed
  // the operation and sends back an IPC message.
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("navigation", "RenderFrameHostImpl::Unload",
                                    TRACE_ID_LOCAL(this), "render_frame_host",
                                    this);

  // If this RenderFrameHost is already pending deletion, it must have already
  // gone through this, therefore just return.
  if (IsPendingDeletion()) {
    NOTREACHED() << "RFH should be in default state when calling Unload.";
    return;
  }

  if (unload_event_monitor_timeout_ && !do_not_delete_for_testing_) {
    unload_event_monitor_timeout_->Start(kUnloadTimeout);
  }

  // TODO(nasko): If the frame is not live, the RFH should just be deleted by
  // simulating the receipt of unload ack.
  is_waiting_for_unload_ack_ = true;

  if (proxy) {
    SetLifecycleState(LifecycleStateImpl::kRunningUnloadHandlers);
    if (IsRenderFrameLive()) {
      GetMojomFrameInRenderer()->Unload(
          is_loading,
          proxy->frame_tree_node()->current_replication_state().Clone(),
          proxy->GetFrameToken(), proxy->CreateAndBindRemoteFrameInterfaces(),
          proxy->CreateAndBindRemoteMainFrameInterfaces());
      // Remember that a `blink::RemoteFrame` was created as part of processing
      // the Unload message above.
      proxy->SetRenderFrameProxyCreated(true);
    }
  } else {
    // RenderDocument: After a local<->local swap, this function is called with
    // a null |proxy|.
    CHECK(ShouldCreateNewHostForSameSiteSubframe());

    // The unload handlers already ran for this document during the
    // local<->local swap. Hence, there is no need to send
    // mojom::FrameNavigationControl::Unload here. It can be marked at
    // completed.
    SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);
  }

  if (web_ui())
    web_ui()->RenderFrameHostUnloading();

  StartPendingDeletionOnSubtree(PendingDeletionReason::kSwappedOut);
  // Some children with no unload handler may be eligible for deletion. Cut the
  // dead branches now. This is a performance optimization.
  PendingDeletionCheckCompletedOnSubtree();
  // |this| is potentially deleted. Do not add code after this.
}

void RenderFrameHostImpl::UndoCommitNavigation(RenderFrameProxyHost& proxy,
                                               bool is_loading) {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::UndoCommitNavigation",
              "render_frame_host", this);

  DCHECK_EQ(lifecycle_state_, LifecycleStateImpl::kPendingCommit);

  if (IsRenderFrameLive()) {
    // By definition, the browser process has not received the
    // `DidCommitNavgation()`, so the RenderFrameProxyHost endpoints are still
    // bound. Resetting now means any queued IPCs that are still in-flight will
    // be dropped. This is a bit problematic, but it is still less problematic
    // than just crashing the renderer for being in an inconsistent state.
    proxy.TearDownMojoConnection();

    GetMojomFrameInRenderer()->UndoCommitNavigation(
        is_loading,
        proxy.frame_tree_node()->current_replication_state().Clone(),
        proxy.GetFrameToken(), proxy.CreateAndBindRemoteFrameInterfaces(),
        proxy.CreateAndBindRemoteMainFrameInterfaces());
  }

  SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);
}

void RenderFrameHostImpl::MaybeDispatchDidFinishLoadOnPrerenderActivation() {
  // Don't dispatch notification if DidFinishLoad has not yet been invoked for
  // `rfh` i.e., when the url is nullopt.
  if (!document_associated_data_
           ->pending_did_finish_load_url_for_prerendering())
    return;

  delegate_->OnDidFinishLoad(
      this, *document_associated_data_
                 ->pending_did_finish_load_url_for_prerendering());

  // Set to nullopt to avoid calling DidFinishLoad twice.
  document_associated_data_
      ->reset_pending_did_finish_load_url_for_prerendering();
}

void RenderFrameHostImpl::MaybeDispatchDOMContentLoadedOnPrerenderActivation() {
  // Don't send a notification if DOM content is not yet loaded.
  if (!document_associated_data_->dom_content_loaded())
    return;

  delegate_->DOMContentLoaded(this);
}

void RenderFrameHostImpl::SwapOuterDelegateFrame(RenderFrameProxyHost* proxy) {
  GetMojomFrameInRenderer()->Unload(
      /*is_loading=*/false,
      browsing_context_state_->current_replication_state().Clone(),
      proxy->GetFrameToken(), proxy->CreateAndBindRemoteFrameInterfaces(),
      proxy->CreateAndBindRemoteMainFrameInterfaces());
}

void RenderFrameHostImpl::DetachFromProxy() {
  if (IsPendingDeletion())
    return;


  // Start pending deletion on this frame and its children.
  DeleteRenderFrame(mojom::FrameDeleteIntention::kNotMainFrame);
  StartPendingDeletionOnSubtree(PendingDeletionReason::kFrameDetach);
#if BUILDFLAG(IS_OHOS)
  if (!frame_tree()) {
    return;
  }
#endif
  frame_tree()->FrameUnloading(GetFrameTreeNodeForUnload());

  // Some children with no unload handler may be eligible for immediate
  // deletion. Cut the dead branches now. This is a performance optimization.
  PendingDeletionCheckCompletedOnSubtree();  // May delete |this|.
  // |this| is potentially deleted. Do not add code after this.
}

void RenderFrameHostImpl::ProcessBeforeUnloadCompleted(
    bool proceed,
    bool treat_as_final_completion_callback,
    const base::TimeTicks& renderer_before_unload_start_time,
    const base::TimeTicks& renderer_before_unload_end_time,
    bool for_legacy) {
  TRACE_EVENT_NESTABLE_ASYNC_END1(
      "navigation", "RenderFrameHostImpl BeforeUnload", TRACE_ID_LOCAL(this),
      "render_frame_host", this);
  // If this renderer navigated while the beforeunload request was in flight, we
  // may have cleared this state in DidCommitProvisionalLoad, in which case we
  // can ignore this message.
  // However renderer might also be swapped out but we still want to proceed
  // with navigation, otherwise it would block future navigations. This can
  // happen when pending cross-site navigation is canceled by a second one just
  // before DidCommitProvisionalLoad while current RVH is waiting for commit
  // but second navigation is started from the beginning.
  RenderFrameHostImpl* initiator = GetBeforeUnloadInitiator();
  if (!initiator)
    return;

  // Continue processing the ACK in the frame that triggered beforeunload in
  // this frame.  This could be either this frame itself or an ancestor frame.
  initiator->ProcessBeforeUnloadCompletedFromFrame(
      proceed, treat_as_final_completion_callback, this,
      /*is_frame_being_destroyed=*/false, renderer_before_unload_start_time,
      renderer_before_unload_end_time, for_legacy);
}

RenderFrameHostImpl* RenderFrameHostImpl::GetBeforeUnloadInitiator() {
  for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
    if (frame->is_waiting_for_beforeunload_completion_)
      return frame;
  }
  return nullptr;
}

void RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame(
    bool proceed,
    bool treat_as_final_completion_callback,
    RenderFrameHostImpl* frame,
    bool is_frame_being_destroyed,
    const base::TimeTicks& renderer_before_unload_start_time,
    const base::TimeTicks& renderer_before_unload_end_time,
    bool for_legacy) {
  // Check if we need to wait for more beforeunload completion callbacks. If
  // |proceed| is false, we know the navigation or window close will be aborted,
  // so we don't need to wait for beforeunload completion callbacks from any
  // other frames. |treat_as_final_completion_callback| also indicates that we
  // shouldn't wait for any other ACKs (e.g., when a beforeunload timeout
  // fires).
  if (!proceed || treat_as_final_completion_callback) {
    beforeunload_pending_replies_.clear();
  } else {
    beforeunload_pending_replies_.erase(frame);
    if (!beforeunload_pending_replies_.empty())
      return;
  }

  DCHECK(!send_before_unload_start_time_.is_null());

  // Sets a default value for before_unload_end_time so that the browser
  // survives a hacked renderer.
  base::TimeTicks before_unload_end_time = renderer_before_unload_end_time;
  base::TimeDelta browser_to_renderer_ipc_time_delta;
  if (!renderer_before_unload_start_time.is_null() &&
      !renderer_before_unload_end_time.is_null()) {
    base::TimeTicks before_unload_completed_time = base::TimeTicks::Now();

    if (!base::TimeTicks::IsConsistentAcrossProcesses() && !for_legacy) {
      // TimeTicks is not consistent across processes and we are passing
      // TimeTicks across process boundaries so we need to compensate for any
      // skew between the processes. Here we are converting the renderer's
      // notion of before_unload_end_time to TimeTicks in the browser process.
      // See comments in inter_process_time_ticks_converter.h for more.
      blink::InterProcessTimeTicksConverter converter(
          blink::LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_),
          blink::LocalTimeTicks::FromTimeTicks(before_unload_completed_time),
          blink::RemoteTimeTicks::FromTimeTicks(
              renderer_before_unload_start_time),
          blink::RemoteTimeTicks::FromTimeTicks(
              renderer_before_unload_end_time));
      const base::TimeTicks browser_before_unload_start_time =
          converter
              .ToLocalTimeTicks(blink::RemoteTimeTicks::FromTimeTicks(
                  renderer_before_unload_start_time))
              .ToTimeTicks();
      const base::TimeTicks browser_before_unload_end_time =
          converter
              .ToLocalTimeTicks(blink::RemoteTimeTicks::FromTimeTicks(
                  renderer_before_unload_end_time))
              .ToTimeTicks();
      before_unload_end_time = browser_before_unload_end_time;
      browser_to_renderer_ipc_time_delta =
          browser_before_unload_start_time - send_before_unload_start_time_;
    } else {
      browser_to_renderer_ipc_time_delta =
          (renderer_before_unload_start_time - send_before_unload_start_time_);
    }

    if (for_legacy) {
      base::UmaHistogramTimes(
          "Navigation.OnBeforeUnloadLegacyPostTaskTime",
          before_unload_completed_time - renderer_before_unload_end_time);
      // When `for_legacy` is true callers should supply
      // `send_before_unload_start_time_` as the value for
      // `renderer_before_unload_start_time`, which means
      // `browser_to_renderer_ipc_time_delta` should be 0.
      DCHECK(browser_to_renderer_ipc_time_delta.is_zero());
    } else {
      base::UmaHistogramTimes(
          "Navigation.OnBeforeUnloadBrowserToRendererIpcTime",
          browser_to_renderer_ipc_time_delta);
    }

    base::TimeDelta on_before_unload_overhead_time =
        (before_unload_completed_time - send_before_unload_start_time_) -
        (renderer_before_unload_end_time - renderer_before_unload_start_time);
    base::UmaHistogramTimes("Navigation.OnBeforeUnloadOverheadTime",
                            on_before_unload_overhead_time);

    frame_tree_node_->navigator().LogBeforeUnloadTime(
        renderer_before_unload_start_time, renderer_before_unload_end_time,
        send_before_unload_start_time_, for_legacy);
  }

  // Resets beforeunload waiting state.
  is_waiting_for_beforeunload_completion_ = false;
  has_shown_beforeunload_dialog_ = false;
  if (beforeunload_timeout_)
    beforeunload_timeout_->Stop();
  send_before_unload_start_time_ = base::TimeTicks();

  // We could reach this from a subframe destructor for |frame| while we're in
  // the middle of closing the current tab. In that case, dispatch the ACK to
  // prevent re-entrancy and a potential nested attempt to free the current
  // frame. See https://crbug.com/866382 and https://crbug.com/1147567.
  base::OnceClosure task = base::BindOnce(
      [](base::WeakPtr<RenderFrameHostImpl> self,
         const base::TimeTicks& before_unload_end_time, bool proceed,
         bool unload_ack_is_for_navigation) {
        if (!self)
          return;
        FrameTreeNode* frame = self->frame_tree_node();
        // If the ACK is for a navigation, send it to the Navigator to have the
        // current navigation stop/proceed. Otherwise, send it to the
        // RenderFrameHostManager which handles closing.
        if (unload_ack_is_for_navigation) {
          frame->navigator().BeforeUnloadCompleted(frame, proceed,
                                                   before_unload_end_time);
        } else {
          frame->render_manager()->BeforeUnloadCompleted(proceed);
        }
      },
      // The overhead of the browser->renderer IPC may be non trivial. Account
      // for it here. Ideally this would also include the time to execute the
      // JS, but we would need to exclude the time spent waiting for a dialog,
      // which is tricky.
      weak_ptr_factory_.GetWeakPtr(),
      before_unload_end_time - browser_to_renderer_ipc_time_delta, proceed,
      unload_ack_is_for_navigation_);

  if (is_frame_being_destroyed) {
    DCHECK(proceed);
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, std::move(task));
  } else {
    std::move(task).Run();
  }

  // If canceled, notify the delegate to cancel its pending navigation entry.
  // This is usually redundant with the dialog closure code in WebContentsImpl's
  // OnDialogClosed, but there may be some cases that Blink returns !proceed
  // without showing the dialog. We also update the address bar here to be safe.
  if (!proceed)
    frame_tree_->DidCancelLoading();
}

bool RenderFrameHostImpl::IsWaitingForUnloadACK() const {
  return page_close_state_ == PageCloseState::kRunningUnloadHandlers ||
         is_waiting_for_unload_ack_;
}

bool RenderFrameHostImpl::BeforeUnloadTimedOut() const {
  return beforeunload_timeout_ &&
         (send_before_unload_start_time_ != base::TimeTicks()) &&
         (base::TimeTicks::Now() - send_before_unload_start_time_) >
             beforeunload_timeout_delay_;
}

void RenderFrameHostImpl::OnUnloadACK() {
  // Give the tests a chance to override this sequence.
  if (unload_ack_callback_ && unload_ack_callback_.Run()) {
    return;
  }

  // This method should be called in the pending deletion state except the
  // placeholder RFHs for an inner WebContents that use unload without changing
  // lifecycle states. When attaching a GuestView as the inner WebContents,
  // `RFH::SwapOuterDelegateFrame()` is called for the placeholder RFH so that
  // it makes its renderer send this message. `owner_` is non null since this
  // attachment can only happen for subframes and pending deletion is the only
  // case where subframes may have a null `owner_`.
  RenderFrameHostOwner* owner =
      IsPendingDeletion() ? GetFrameTreeNodeForUnload() : owner_;
  if (!is_main_frame() &&
      owner->GetRenderFrameHostManager().is_attaching_inner_delegate()) {
    // This RFH was unloaded while attaching an inner delegate. The RFH
    // will stay around but it will no longer be associated with a RenderFrame.
    RenderFrameDeleted();
    return;
  }

  // Ignore spurious unload ack.
  if (!is_waiting_for_unload_ack_)
    return;

  // Ignore OnUnloadACK if the RenderFrameHost should be left in pending
  // deletion state.
  if (do_not_delete_for_testing_)
    return;

  DCHECK_EQ(LifecycleStateImpl::kRunningUnloadHandlers, lifecycle_state());
  SetLifecycleState(LifecycleStateImpl::kReadyToBeDeleted);
  PendingDeletionCheckCompleted();  // Can delete |this|.
  // |this| is potentially deleted. Do not add code after this.
}

void RenderFrameHostImpl::OnUnloaded() {
  DCHECK(is_waiting_for_unload_ack_);

  TRACE_EVENT_NESTABLE_ASYNC_END0("navigation", "RenderFrameHostImpl::Unload",
                                  TRACE_ID_LOCAL(this));
  if (unload_event_monitor_timeout_)
    unload_event_monitor_timeout_->Stop();

  base::WeakPtr<RenderFrameHostImpl> self = GetWeakPtr();
  ClearWebUI();
  // See https://crbug.com/1308391. Calling `ClearWebUI()` indirectly call
  // content's embedders via a chain of destructors. Some might destroy the
  // whole WebContents.
  if (!self) {
    return;
  }

  bool deleted =
      GetFrameTreeNodeForUnload()->render_manager()->DeleteFromPendingList(
          this);
  CHECK(deleted);
}

void RenderFrameHostImpl::DisableUnloadTimerForTesting() {
  unload_event_monitor_timeout_.reset();
}

bool RenderFrameHostImpl::IsBackForwardCacheEvictionTimeRunningForTesting()
    const {
  return back_forward_cache_eviction_timer_.IsRunning();
}

void RenderFrameHostImpl::SetSubframeUnloadTimeoutForTesting(
    const base::TimeDelta& timeout) {
  subframe_unload_timeout_ = timeout;
}

#if BUILDFLAG(IS_ANDROID)
void RenderFrameHostImpl::RequestSmartClipExtract(
    ExtractSmartClipDataCallback callback,
    gfx::Rect rect) {
  int32_t callback_id = smart_clip_callbacks_.Add(
      std::make_unique<ExtractSmartClipDataCallback>(std::move(callback)));
  GetAssociatedLocalFrame()->ExtractSmartClipData(
      rect, base::BindOnce(&RenderFrameHostImpl::OnSmartClipDataExtracted,
                           base::Unretained(this), callback_id));
}

void RenderFrameHostImpl::OnSmartClipDataExtracted(int32_t callback_id,
                                                   const std::u16string& text,
                                                   const std::u16string& html,
                                                   const gfx::Rect& clip_rect) {
  std::move(*smart_clip_callbacks_.Lookup(callback_id))
      .Run(text, html, clip_rect);
  smart_clip_callbacks_.Remove(callback_id);
}
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_OHOS)
void RenderFrameHostImpl::GetImageFromCache(const std::string& url,
                                            ImageCacheCallback callback) {
  GetAssociatedLocalFrame()->GetImageFromCache(url, std::move(callback));
}
#endif

void RenderFrameHostImpl::RunModalAlertDialog(
    const std::u16string& alert_message,
    bool disable_third_party_subframe_suppresion,
    RunModalAlertDialogCallback response_callback) {
  auto dialog_closed_callback = base::BindOnce(
      [](RunModalAlertDialogCallback response_callback, bool success,
         const std::u16string& response) {
        // The response string is unused but we use a generic mechanism for
        // closing the javascript dialog that returns two arguments.
        std::move(response_callback).Run();
      },
      std::move(response_callback));
  RunJavaScriptDialog(alert_message, std::u16string(),
                      JAVASCRIPT_DIALOG_TYPE_ALERT,
                      disable_third_party_subframe_suppresion,
                      std::move(dialog_closed_callback));
}

void RenderFrameHostImpl::RunModalConfirmDialog(
    const std::u16string& alert_message,
    bool disable_third_party_subframe_suppresion,
    RunModalConfirmDialogCallback response_callback) {
  auto dialog_closed_callback = base::BindOnce(
      [](RunModalConfirmDialogCallback response_callback, bool success,
         const std::u16string& response) {
        // The response string is unused but we use a generic mechanism for
        // closing the javascript dialog that returns two arguments.
        std::move(response_callback).Run(success);
      },
      std::move(response_callback));
  RunJavaScriptDialog(alert_message, std::u16string(),
                      JAVASCRIPT_DIALOG_TYPE_CONFIRM,
                      disable_third_party_subframe_suppresion,
                      std::move(dialog_closed_callback));
}

void RenderFrameHostImpl::RunModalPromptDialog(
    const std::u16string& alert_message,
    const std::u16string& default_value,
    bool disable_third_party_subframe_suppresion,
    RunModalPromptDialogCallback response_callback) {
  RunJavaScriptDialog(
      alert_message, default_value, JAVASCRIPT_DIALOG_TYPE_PROMPT,
      disable_third_party_subframe_suppresion, std::move(response_callback));
}

void RenderFrameHostImpl::RunJavaScriptDialog(
    const std::u16string& message,
    const std::u16string& default_prompt,
    JavaScriptDialogType dialog_type,
    bool disable_third_party_subframe_suppresion,
    JavaScriptDialogCallback ipc_response_callback) {
  // Don't show the dialog if it's triggered on a non-active or non-primary
  // RenderFrameHost. This happens when the RenderFrameHost is pending deletion,
  // or is a non-primary MPArch page (Fenced Frame, in BFCache, etc.)..
  // TODO(https://crbug.com/1262022): Have to check fenced frames explicitly
  // since they are not yet implemented with MPArch. Once the transition from
  // shadow DOM to MPArch is complete, remove the last part of the condition.
  // TODO(crbug.com/1244137): We have to check portals explicitly as they are
  // considered primary. Remove check after we migrate portals to MPArch.
  if (!IsActive() || !GetPage().IsPrimary() || IsNestedWithinFencedFrame() ||
      frame_tree()->delegate()->IsPortal()) {
    std::move(ipc_response_callback).Run(/*success=*/false, std::u16string());
    return;
  }

  // While a JS message dialog is showing, tabs in the same process shouldn't
  // process input events.
  GetProcess()->SetBlocked(true);

  delegate_->RunJavaScriptDialog(
      this, message, default_prompt, dialog_type,
      disable_third_party_subframe_suppresion,
      base::BindOnce(&RenderFrameHostImpl::JavaScriptDialogClosed,
                     weak_ptr_factory_.GetWeakPtr(),
                     std::move(ipc_response_callback)));
}

void RenderFrameHostImpl::RunBeforeUnloadConfirm(
    bool is_reload,
    RunBeforeUnloadConfirmCallback ipc_response_callback) {
  TRACE_EVENT1("navigation", "RenderFrameHostImpl::OnRunBeforeUnloadConfirm",
               "render_frame_host", this);

  // Don't show the dialog if it's triggered on a non-active or non-primary
  // RenderFrameHost. This happens when the RenderFrameHost is pending deletion,
  // or is a non-primary MPArch page (Fenced Frame, in BFCache, etc.)..
  // TODO(https://crbug.com/1262022): Have to check fenced frames explicitly
  // since they are not yet implemented with MPArch. Once the transition from
  // shadow DOM to MPArch is complete, remove the last part of the condition.
  if (!IsActive() || !GetPage().IsPrimary() || IsNestedWithinFencedFrame() ||
      frame_tree()->delegate()->IsPortal()) {
    std::move(ipc_response_callback).Run(/*success=*/false);
    return;
  }

  // Allow at most one attempt to show a beforeunload dialog per navigation.
  RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
  if (beforeunload_initiator) {
    // If the running beforeunload handler wants to display a dialog and the
    // before-unload type wants to ignore it, then short-circuit the request and
    // respond as if the user decided to stay on the page, canceling the unload.
    if (beforeunload_initiator->beforeunload_dialog_request_cancels_unload_) {
      std::move(ipc_response_callback).Run(/*success=*/false);
      return;
    }

    if (beforeunload_initiator->has_shown_beforeunload_dialog_) {
      // TODO(alexmos): Pass enough data back to renderer to record histograms
      // for Document.BeforeUnloadDialog and add the intervention console
      // message to match renderer-side behavior in
      // Document::DispatchBeforeUnloadEvent().
      std::move(ipc_response_callback).Run(/*success=*/true);
      return;
    }
    beforeunload_initiator->has_shown_beforeunload_dialog_ = true;
  } else {
    // TODO(alexmos): If a renderer-initiated beforeunload shows a dialog, it
    // won't find a |beforeunload_initiator|. This can happen for a
    // renderer-initiated navigation or window.close().  We should ensure that
    // when the browser process later kicks off subframe unload handlers (if
    // any), they won't be able to show additional dialogs. However, we can't
    // just set |has_shown_beforeunload_dialog_| because we don't know which
    // frame is navigating/closing here.  Plumb enough information here to fix
    // this.
  }

  // While a JS beforeunload dialog is showing, tabs in the same process
  // shouldn't process input events.
  GetProcess()->SetBlocked(true);

  // The beforeunload dialog for this frame may have been triggered by a
  // browser-side request to this frame or a frame up in the frame hierarchy.
  // Stop any timers that are waiting.
  for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
    if (frame->beforeunload_timeout_)
      frame->beforeunload_timeout_->Stop();
  }

  auto ipc_callback_wrapper = base::BindOnce(
      [](RunBeforeUnloadConfirmCallback response_callback, bool success,
         const std::u16string& response) {
        // The response string is unused but we use a generic mechanism for
        // closing the javascript dialog that returns two arguments.
        std::move(response_callback).Run(success);
      },
      std::move(ipc_response_callback));
  auto dialog_closed_callback = base::BindOnce(
      &RenderFrameHostImpl::JavaScriptDialogClosed,
      weak_ptr_factory_.GetWeakPtr(), std::move(ipc_callback_wrapper));

  delegate_->RunBeforeUnloadConfirm(this, is_reload,
                                    std::move(dialog_closed_callback));
}

void RenderFrameHostImpl::MaybeStartOutermostMainFrameNavigation(
    const std::vector<GURL>& urls) {
  const bool kStartupEnabled =
      base::FeatureList::IsEnabled(kSpeculativeServiceWorkerStartup);
  const bool kWarmUpEnabled =
      base::FeatureList::IsEnabled(
          blink::features::kSpeculativeServiceWorkerWarmUp) &&
      !blink::features::kSpeculativeServiceWorkerWarmUpDryRun.Get();

  if (!kStartupEnabled && !kWarmUpEnabled) {
    return;
  }

  TRACE_EVENT0("navigation",
               "RenderFrameHostImpl::MaybeStartOutermostMainFrameNavigation");

  ServiceWorkerContextWrapper* context =
      GetStoragePartition()->GetServiceWorkerContext();

  if (!context) {
    return;
  }

  for (const auto& url : urls) {
    GURL filtered_url(url);

    GetProcess()->FilterURL(/*empty_allowed=*/false, &filtered_url);

    if (filtered_url.spec() == kBlockedURL) {
      continue;
    }

    if (!OriginCanAccessServiceWorkers(filtered_url)) {
      continue;
    }

    const blink::StorageKey key =
        blink::StorageKey::CreateFirstParty(url::Origin::Create(filtered_url));

    if (!context->MaybeHasRegistrationForStorageKey(key)) {
      continue;
    }

    // Ask the service worker context to speculatively start a service worker
    // for the request URL if necessary for optimization purposes. There are
    // cases where we have already started the service worker (e.g, Prerendering
    // or the previous navigation already started the service worker), but this
    // call does nothing if the service worker already started for the URL.
    if (kStartupEnabled) {
      context->StartServiceWorkerForNavigationHint(filtered_url, key,
                                                   base::DoNothing());
    }

    // Ask the service worker context to speculatively warm-up a service worker
    // for the request URL if necessary for optimization purposes. There are
    // cases where we have already started the service worker (e.g, Prerendering
    // or the previous navigation already started the service worker), but this
    // call does nothing if the service worker already started for the URL.
    if (kWarmUpEnabled) {
      context->WarmUpServiceWorker(filtered_url, key, base::DoNothing());
    }
  }
}

// TODO(crbug.com/1213863): Move this method to content::PageImpl.
void RenderFrameHostImpl::UpdateFaviconURL(
    std::vector<blink::mojom::FaviconURLPtr> favicon_urls) {
  DCHECK(!GetParent());
  GetPage().set_favicon_urls(std::move(favicon_urls));
  delegate_->UpdateFaviconURL(this, GetPage().favicon_urls());
}

float RenderFrameHostImpl::GetPageScaleFactor() const {
  DCHECK(!GetParent());
  return page_scale_factor_;
}

void RenderFrameHostImpl::ScaleFactorChanged(float scale) {
  DCHECK(!GetParent());
  page_scale_factor_ = scale;
  delegate_->OnPageScaleFactorChanged(GetPage());
}

void RenderFrameHostImpl::ContentsPreferredSizeChanged(
    const gfx::Size& pref_size) {
  // Do not try to handle the message in inactive RenderFrameHosts for
  // simplicity. If this RenderFrameHost belongs to a bfcached or prerendered
  // page, the page will be deleted. We predict that it will not significantly
  // impact coverage because renderers only send this message when running in
  // `PreferredSizeChanged` mode.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kContentsPreferredSizeChanged)) {
    return;
  }

  // Ignore the request if we are aren't the outermost main frame.
  if (GetParentOrOuterDocument())
    return;

  delegate_->UpdateWindowPreferredSize(pref_size);
}

void RenderFrameHostImpl::TextAutosizerPageInfoChanged(
    blink::mojom::TextAutosizerPageInfoPtr page_info) {
  GetPage().OnTextAutosizerPageInfoChanged(std::move(page_info));
}

void RenderFrameHostImpl::FocusPage() {
  render_view_host_->OnFocus();
}

void RenderFrameHostImpl::TakeFocus(bool reverse) {
  // TODO(crbug.com/1225366): Consider moving this to PageImpl.
  DCHECK(is_main_frame());

  // Do not update the parent on behalf of the inactive document.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kDispatchLoad)) {
    return;
  }

  // If we are representing an inner frame tree call advance on our outer
  // delegate's parent's RenderFrameHost.
  RenderFrameHostImpl* parent_or_outer_document =
      GetParentOrOuterDocumentOrEmbedder();
  if (parent_or_outer_document) {
    RenderFrameProxyHost* proxy_host = GetProxyToOuterDelegate();
    DCHECK(proxy_host);
    parent_or_outer_document->DidFocusFrame();
    parent_or_outer_document->AdvanceFocus(
        reverse ? blink::mojom::FocusType::kBackward
                : blink::mojom::FocusType::kForward,
        proxy_host);
    return;
  }

  render_view_host_->OnTakeFocus(reverse);
}

void RenderFrameHostImpl::UpdateTargetURL(
    const GURL& url,
    blink::mojom::LocalMainFrameHost::UpdateTargetURLCallback callback) {
  // An inactive document should ignore to update the target url.
  if (!IsActive()) {
    std::move(callback).Run();
    return;
  }

  delegate_->UpdateTargetURL(this, url);
  std::move(callback).Run();
}

void RenderFrameHostImpl::RequestClose() {
  // 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.
  if (!IsOutermostMainFrame()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_WINDOW_CLOSE_ON_NON_OUTERMOST_FRAME);
    return;
  }

  // If the renderer is telling us to close, it has already run the unload
  // events, and we can take the fast path.
  ClosePageIgnoringUnloadEvents(ClosePageSource::kRenderer);
}

bool RenderFrameHostImpl::HasStickyUserActivation() const {
  return user_activation_state_.HasBeenActive();
}

bool RenderFrameHostImpl::IsActiveUserActivation() const {
  return user_activation_state_.IsActive();
}

void RenderFrameHostImpl::ClearUserActivation() {
  user_activation_state_.Clear();
  history_user_activation_state_.Clear();
}

void RenderFrameHostImpl::ConsumeTransientUserActivation() {
  user_activation_state_.ConsumeIfActive();
}

void RenderFrameHostImpl::ActivateUserActivation(
    blink::mojom::UserActivationNotificationType notification_type,
    bool sticky_only) {
  if (sticky_only) {
    user_activation_state_.SetHasBeenActive();
  } else {
    user_activation_state_.Activate(notification_type);
    history_user_activation_state_.Activate();
  }
}

bool RenderFrameHostImpl::IsHistoryUserActivationActive() const {
  return history_user_activation_state_.IsActive();
}

void RenderFrameHostImpl::ConsumeHistoryUserActivation() {
  history_user_activation_state_.Consume();
}

void RenderFrameHostImpl::ClosePage(ClosePageSource source) {
  // This path is taken when tab/window close is initiated by either the
  // browser process or via a window.close() call through a proxy. In both
  // cases, we need to tell the main frame's renderer process to run unload
  // handlers and prepare for page close.
  //
  // This should only be called on outermost main frames. If this
  // RenderFrameHost is no longer a primary main frame (e.g., if it was placed
  // into back-forward cache or became pending deletion just before getting
  // here), we should not close the active tab if the request to close came from
  // the renderer, so return early in that case. We proceed with closing
  // regardless if the request came from the browser so that renderers can't
  // avoid closing via navigation.
  DCHECK(is_main_frame());
  // TODO(crbug.com/1254770): Orphaned portals use this code path. Revisit how
  // portals handle unload when migrating off of inner WebContents.
  DCHECK(IsOutermostMainFrame() || frame_tree()->delegate()->IsPortal());
  if (!IsInPrimaryMainFrame() && source == ClosePageSource::kRenderer) {
    return;
  }

  page_close_state_ = PageCloseState::kRunningUnloadHandlers;

  if (IsRenderFrameLive() && !IsPageReadyToBeClosed()) {
    close_timeout_ = std::make_unique<TimeoutMonitor>(
        base::BindRepeating(&RenderFrameHostImpl::ClosePageTimeout,
                            weak_ptr_factory_.GetWeakPtr(), source));
    close_timeout_->Start(kUnloadTimeout);

    GetAssociatedLocalMainFrame()->ClosePage(
        base::BindOnce(&RenderFrameHostImpl::ClosePageIgnoringUnloadEvents,
                       weak_ptr_factory_.GetWeakPtr(), source));
  } else {
    // This RenderFrameHost doesn't have a live renderer (or has already run
    // unload handlers), so just skip the close event and close the page.
    ClosePageIgnoringUnloadEvents(source);
  }
}

void RenderFrameHostImpl::ClosePageIgnoringUnloadEvents(
    ClosePageSource source) {
  if (close_timeout_) {
    close_timeout_->Stop();
    close_timeout_.reset();
  }

  // If this RenderFrameHost is no longer the primary main frame (e.g., if it
  // was replaced by another frame while waiting for the ClosePage ACK or
  // timeout), there's no need to close the active tab if the request to close
  // came from the renderer, so return early in that case. We proceed with
  // closing regardless if the request came from the browser so that renderers
  // can't avoid closing via navigation.
  if (!IsInPrimaryMainFrame() && source == ClosePageSource::kRenderer) {
    page_close_state_ = PageCloseState::kNotClosing;
    return;
  }

  page_close_state_ = PageCloseState::kReadyToBeClosed;
  delegate_->Close();
}

bool RenderFrameHostImpl::IsPageReadyToBeClosed() {
  DCHECK(IsInPrimaryMainFrame());
  // If there is a JavaScript dialog up, don't bother sending the renderer the
  // close event because it is known unresponsive, waiting for the reply from
  // the dialog.
  return page_close_state_ == PageCloseState::kReadyToBeClosed ||
         delegate_->IsJavaScriptDialogShowing() || BeforeUnloadTimedOut();
}

void RenderFrameHostImpl::ClosePageTimeout(ClosePageSource source) {
  if (delegate_->ShouldIgnoreUnresponsiveRenderer()) {
    return;
  }

  ClosePageIgnoringUnloadEvents(source);
}

void RenderFrameHostImpl::ShowCreatedWindow(
    const blink::LocalFrameToken& opener_frame_token,
    WindowOpenDisposition disposition,
    blink::mojom::WindowFeaturesPtr window_features,
    bool user_gesture,
    ShowCreatedWindowCallback callback) {
  // This needs to be sent to the opener frame's delegate since it stores
  // the handle to this class's associated RenderWidgetHostView.
  RenderFrameHostImpl* opener_frame_host =
      FromFrameToken(GetProcess()->GetID(), opener_frame_token);

  // If |opener_frame_host| has been destroyed just return.
  // TODO(crbug.com/1150976): Get rid of having to look up the opener frame
  // to find the newly created web contents, because it is actually just
  // |delegate_|.
  if (!opener_frame_host) {
    std::move(callback).Run();
    return;
  }
  opener_frame_host->delegate()->ShowCreatedWindow(
      opener_frame_host, GetRenderWidgetHost()->GetRoutingID(), disposition,
      *window_features, user_gesture);
  std::move(callback).Run();
}

void RenderFrameHostImpl::SetWindowRect(const gfx::Rect& bounds,
                                        SetWindowRectCallback callback) {
  // Prerendering pages should not reach this code.
  if (lifecycle_state_ == LifecycleStateImpl::kPrerendering) {
    local_main_frame_host_receiver_.ReportBadMessage(
        "SetWindowRect called during prerendering.");
    return;
  }
  // Throw out SetWindowRects that are not from the outermost document.
  if (GetParentOrOuterDocument()) {
    local_main_frame_host_receiver_.ReportBadMessage(
        "SetWindowRect called from child frame.");
    return;
  }

  delegate_->SetWindowRect(bounds);
  std::move(callback).Run();
}

void RenderFrameHostImpl::DidFirstVisuallyNonEmptyPaint() {
  // TODO(crbug.com/1225366): Consider moving this to PageImpl.
  DCHECK(is_main_frame());
  GetPage().OnFirstVisuallyNonEmptyPaint();
}

void RenderFrameHostImpl::DownloadURL(
    blink::mojom::DownloadURLParamsPtr blink_parameters) {
  // TODO(crbug.com/1205359): We should defer the download until the
  // prerendering page is activated, and it will comply with the prerendering
  // spec.
  if (CancelPrerendering(
          PrerenderCancellationReason(PrerenderFinalStatus::kDownload))) {
    return;
  }

  if (!VerifyDownloadUrlParams(GetProcess(), *blink_parameters)) {
    return;
  }

  net::NetworkTrafficAnnotationTag traffic_annotation =
      net::DefineNetworkTrafficAnnotation("renderer_initiated_download", R"(
        semantics {
          sender: "Download from Renderer"
          description:
            "The frame has either navigated to a URL that was determined to be "
            "a download via one of the renderer's classification mechanisms, "
            "or WebView has requested a <canvas> or <img> element at a "
            "specific location be to downloaded."
          trigger:
            "The user navigated to a destination that was categorized as a "
            "download, or WebView triggered saving a <canvas> or <img> tag."
          data: "Only the URL we are attempting to download."
          destination: WEBSITE
        }
        policy {
          cookies_allowed: YES
          cookies_store: "user"
          setting: "This feature cannot be disabled by settings."
          chrome_policy {
            DownloadRestrictions {
              DownloadRestrictions: 3
            }
          }
        })");
  std::unique_ptr<download::DownloadUrlParameters> parameters(
      new download::DownloadUrlParameters(blink_parameters->url,
                                          GetProcess()->GetID(), GetRoutingID(),
                                          traffic_annotation));
  parameters->set_content_initiated(!blink_parameters->is_context_menu_save);
  parameters->set_has_user_gesture(blink_parameters->has_user_gesture);
  parameters->set_suggested_name(
      blink_parameters->suggested_name.value_or(std::u16string()));
  parameters->set_prompt(blink_parameters->is_context_menu_save);
  parameters->set_cross_origin_redirects(
      blink_parameters->cross_origin_redirects);
  parameters->set_referrer(
      blink_parameters->referrer ? blink_parameters->referrer->url : GURL());
  parameters->set_referrer_policy(Referrer::ReferrerPolicyForUrlRequest(
      blink_parameters->referrer ? blink_parameters->referrer->policy
                                 : network::mojom::ReferrerPolicy::kDefault));
  parameters->set_initiator(
      blink_parameters->initiator_origin.value_or(url::Origin()));
  parameters->set_download_source(download::DownloadSource::FROM_RENDERER);

  if (blink_parameters->data_url_blob) {
    DataURLBlobReader::ReadDataURLFromBlob(
        std::move(blink_parameters->data_url_blob),
        base::BindOnce(&OnDataURLRetrieved, std::move(parameters)));
    return;
  }

  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  if (blink_parameters->blob_url_token) {
    blob_url_loader_factory =
        ChromeBlobStorageContext::URLLoaderFactoryForToken(
            GetStoragePartition(), std::move(blink_parameters->blob_url_token));
  }

  StartDownload(std::move(parameters), std::move(blob_url_loader_factory));
}

void RenderFrameHostImpl::ReportNoBinderForInterface(const std::string& error) {
  broker_receiver_.ReportBadMessage(error + " for the frame/document scope");
}

ukm::SourceId RenderFrameHostImpl::GetPageUkmSourceId() {
  // This id for all subframes or fenced frames is the same as the id for the
  // outermost main frame. For portals, this id for frames inside a portal is
  // the same as the id for the main frame for the portal.
  RenderFrameHostImpl* main_frame =
      IsNestedWithinFencedFrame() ? GetOutermostMainFrame() : GetMainFrame();
  int64_t navigation_id =
      main_frame->last_committed_cross_document_navigation_id_;
  if (navigation_id == -1)
    return ukm::kInvalidSourceId;
  return ukm::ConvertToSourceId(navigation_id,
                                ukm::SourceIdType::NAVIGATION_ID);
}

BrowserContext* RenderFrameHostImpl::GetBrowserContext() {
  return GetProcess()->GetBrowserContext();
}

// TODO(crbug.com/1091720): Would be better to do this directly in the chrome
// layer.  See referenced bug for further details.
void RenderFrameHostImpl::ReportInspectorIssue(
    blink::mojom::InspectorIssueInfoPtr info) {
  devtools_instrumentation::BuildAndReportBrowserInitiatedIssue(
      this, std::move(info));
}

void RenderFrameHostImpl::WriteIntoTrace(
    perfetto::TracedProto<TraceProto> proto) const {
  proto.Set(TraceProto::kRenderFrameHostId, GetGlobalId());
  proto->set_frame_tree_node_id(GetFrameTreeNodeId());
  proto->set_lifecycle_state(LifecycleStateToProto());
  proto->set_frame_type(GetFrameTypeProto());
  proto->set_origin(GetLastCommittedOrigin().GetDebugString());
  proto->set_url(GetLastCommittedURL().possibly_invalid_spec());
  proto.Set(TraceProto::kProcess, GetProcess());
  proto.Set(TraceProto::kSiteInstance, GetSiteInstance());
  if (auto* parent = GetParent()) {
    proto.Set(TraceProto::kParent, parent);
  } else if (auto* outer_document = GetParentOrOuterDocument()) {
    proto.Set(TraceProto::kOuterDocument, outer_document);
    outer_document->WriteIntoTrace(proto.WriteNestedMessage(
        perfetto::protos::pbzero::RenderFrameHost::kOuterDocument));
  } else if (auto* embedder = GetParentOrOuterDocumentOrEmbedder()) {
    proto.Set(TraceProto::kEmbedder, embedder);
    embedder->WriteIntoTrace(proto.WriteNestedMessage(
        perfetto::protos::pbzero::RenderFrameHost::kEmbedder));
  }
  proto.Set(TraceProto::kBrowsingContextState, browsing_context_state_);
}

perfetto::protos::pbzero::RenderFrameHost::LifecycleState
RenderFrameHostImpl::LifecycleStateToProto() const {
  using RFHProto = perfetto::protos::pbzero::RenderFrameHost;
  switch (lifecycle_state()) {
    case LifecycleStateImpl::kSpeculative:
      return RFHProto::SPECULATIVE;
    case LifecycleStateImpl::kPendingCommit:
      return RFHProto::PENDING_COMMIT;
    case LifecycleStateImpl::kPrerendering:
      return RFHProto::PRERENDERING;
    case LifecycleStateImpl::kActive:
      return RFHProto::ACTIVE;
    case LifecycleStateImpl::kInBackForwardCache:
      return RFHProto::IN_BACK_FORWARD_CACHE;
    case LifecycleStateImpl::kRunningUnloadHandlers:
      return RFHProto::RUNNING_UNLOAD_HANDLERS;
    case LifecycleStateImpl::kReadyToBeDeleted:
      return RFHProto::READY_TO_BE_DELETED;
  }

  return RFHProto::UNSPECIFIED;
}

perfetto::protos::pbzero::FrameTreeNodeInfo::FrameType
RenderFrameHostImpl::GetFrameTypeProto() const {
  using RFHProto = perfetto::protos::pbzero::FrameTreeNodeInfo;

  if (GetParent()) {
    return RFHProto::SUBFRAME;
  }
  if (GetPage().IsPrimary()) {
    return RFHProto::PRIMARY_MAIN_FRAME;
  }
  if (lifecycle_state() == LifecycleStateImpl::kPrerendering) {
    return RFHProto::PRERENDER_MAIN_FRAME;
  }
  if (IsFencedFrameRoot()) {
    return RFHProto::FENCED_FRAME_ROOT;
  }

  // It returns a different value from FrameTreeNode::GetFrameType() when
  // - `this` is a speculative RFH or
  // - `this` is not in a frame tree (e.g., IsInBackForwardCache() or
  // IsPendingDeletion()).
  return RFHProto::UNSPECIFIED_FRAME_TYPE;
}

StoragePartitionImpl* RenderFrameHostImpl::GetStoragePartition() {
  // Both RenderProcessHostImpl and MockRenderProcessHost obtain the
  // StoragePartition instance through BrowserContext::GetStoragePartition()
  // call. That method does not support creating TestStoragePartition
  // instances and always vends StoragePartitionImpl objects. It is therefore
  // safe to static cast the result here.
  return static_cast<StoragePartitionImpl*>(
      GetProcess()->GetStoragePartition());
}

void RenderFrameHostImpl::RequestTextSurroundingSelection(
    blink::mojom::LocalFrame::GetTextSurroundingSelectionCallback callback,
    int max_length) {
  DCHECK(!callback.is_null());
  GetAssociatedLocalFrame()->GetTextSurroundingSelection(max_length,
                                                         std::move(callback));
}

bool RenderFrameHostImpl::HasCommittingNavigationRequestForOrigin(
    const url::Origin& origin,
    NavigationRequest* navigation_request_to_exclude) {
  for (const auto& it : navigation_requests_) {
    NavigationRequest* request = it.first;
    if (request != navigation_request_to_exclude &&
        request->HasCommittingOrigin(origin)) {
      return true;
    }
  }

  // Note: this function excludes |same_document_navigation_requests_|, which
  // should be ok since these cannot change the origin.
  return false;
}

void RenderFrameHostImpl::SendInterventionReport(const std::string& id,
                                                 const std::string& message) {
  GetAssociatedLocalFrame()->SendInterventionReport(id, message);
}

WebUI* RenderFrameHostImpl::GetWebUI() {
  return web_ui();
}

void RenderFrameHostImpl::AllowBindings(int bindings_flags) {
  // Never grant any bindings to browser plugin guests.
  if (GetProcess()->IsForGuestsOnly()) {
    NOTREACHED() << "Never grant bindings to a guest process.";
    return;
  }
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::AllowBindings",
               "render_frame_host", this, "bindings_flags", bindings_flags);

  int webui_bindings = bindings_flags & kWebUIBindingsPolicyMask;

  // Ensure callers that specify non-zero WebUI bindings are doing so on a
  // RenderFrameHost that has WebUI associated with it. If we run the renderer
  // code in-process, the security invariant cannot be enforced, therefore it
  // should be skipped in that case.
  if (webui_bindings != BINDINGS_POLICY_NONE &&
      !RenderProcessHost::run_renderer_in_process() &&
      base::FeatureList::IsEnabled(kEnsureAllowBindingsIsAlwaysForWebUI)) {
    ProcessLock process_lock = GetProcess()->GetProcessLock();
    if (!process_lock.is_locked_to_site() ||
        !base::Contains(URLDataManagerBackend::GetWebUISchemes(),
                        process_lock.lock_url().scheme())) {
      // TODO(nasko): Convert this to CHECK once it has had enough time to
      // ensure this branch is not hit in production.
      SCOPED_CRASH_KEY_STRING256("AllowBindings", "process_lock",
                                 process_lock.ToString());
      NOTREACHED() << "Calling AllowBindings for a process not locked to WebUI:"
                   << process_lock;
      base::debug::DumpWithoutCrashing();
    }
  }

  // The bindings being granted here should not differ from the bindings that
  // the associated WebUI requires.
  if (web_ui_)
    CHECK_EQ(web_ui_->GetBindings(), webui_bindings);

  // Ensure we aren't granting WebUI bindings to a process that has already
  // been used for non-privileged views.
  if (webui_bindings && GetProcess()->IsInitializedAndNotDead() &&
      !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
          GetProcess()->GetID())) {
    // This process has no bindings yet. Make sure it does not have more
    // than this single active view.
    // --single-process only has one renderer.
    if (GetProcess()->GetActiveViewCount() > 1 &&
        !base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kSingleProcess))
      return;
  }

  if (webui_bindings) {
    ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
        GetProcess()->GetID(), webui_bindings);
  }

  enabled_bindings_ |= bindings_flags;

  if (is_render_frame_created()) {
    GetFrameBindingsControl()->AllowBindings(enabled_bindings_);
    if (web_ui_ && enabled_bindings_ & BINDINGS_POLICY_WEB_UI)
      web_ui_->SetUpMojoConnection();
  }
}

int RenderFrameHostImpl::GetEnabledBindings() {
  return enabled_bindings_;
}

void RenderFrameHostImpl::SetWebUIProperty(const std::string& name,
                                           const std::string& value) {
  // WebUI allows to register SetProperties only for the outermost main frame.
  if (GetParentOrOuterDocument())
    return;

  // This is a sanity check before telling the renderer to enable the property.
  // It could lie and send the corresponding IPC messages anyway, but we will
  // not act on them if enabled_bindings_ doesn't agree. If we get here without
  // WebUI bindings, terminate the renderer process.
  if (enabled_bindings_ & BINDINGS_POLICY_WEB_UI)
    web_ui_->SetProperty(name, value);
  else
    ReceivedBadMessage(GetProcess(), bad_message::RVH_WEB_UI_BINDINGS_MISMATCH);
}

void RenderFrameHostImpl::DisableBeforeUnloadHangMonitorForTesting() {
  beforeunload_timeout_.reset();
}

bool RenderFrameHostImpl::IsBeforeUnloadHangMonitorDisabledForTesting() {
  return !beforeunload_timeout_;
}

void RenderFrameHostImpl::DoNotDeleteForTesting() {
  do_not_delete_for_testing_ = true;
}

void RenderFrameHostImpl::ResumeDeletionForTesting() {
  do_not_delete_for_testing_ = false;
}

void RenderFrameHostImpl::DetachForTesting() {
  do_not_delete_for_testing_ = false;
  RenderFrameHostImpl::Detach();
}

bool RenderFrameHostImpl::IsFeatureEnabled(
    blink::mojom::PermissionsPolicyFeature feature) {
  return permissions_policy_ && permissions_policy_->IsFeatureEnabledForOrigin(
                                    feature, GetLastCommittedOrigin());
}

const blink::PermissionsPolicy* RenderFrameHostImpl::GetPermissionsPolicy() {
  return permissions_policy_.get();
}

const blink::ParsedPermissionsPolicy&
RenderFrameHostImpl::GetPermissionsPolicyHeader() {
  return permissions_policy_header_;
}

void RenderFrameHostImpl::ViewSource() {
  delegate_->ViewSource(this);
}

void RenderFrameHostImpl::FlushNetworkAndNavigationInterfacesForTesting(
    bool do_nothing_if_no_network_service_connection) {
  if (do_nothing_if_no_network_service_connection &&
      !network_service_disconnect_handler_holder_) {
    return;
  }
  DCHECK(network_service_disconnect_handler_holder_);
  network_service_disconnect_handler_holder_.FlushForTesting();  // IN-TEST

  DCHECK(IsRenderFrameLive());
  DCHECK(frame_);
  frame_.FlushForTesting();  // IN-TEST
}

void RenderFrameHostImpl::PrepareForInnerWebContentsAttach(
    PrepareForInnerWebContentsAttachCallback callback) {
  // TODO(https://crbug.com/1405759) Explain why `owner_` exists.
  CHECK(owner_);
  owner_->GetRenderFrameHostManager().PrepareForInnerDelegateAttach(
      std::move(callback));
}

// UpdateSubresourceLoaderFactories may be called (internally/privately), when
// RenderFrameHostImpl detects a NetworkService crash after pushing a
// NetworkService-based factory to the renderer process.  It may also be called
// when DevTools wants to send to the renderer process a fresh factory bundle
// (e.g. after injecting DevToolsURLLoaderInterceptor) - the latter scenario may
// happen even if `this` RenderFrameHostImpl has not pushed any NetworkService
// factories to the renderer process (DevTools is agnostic to this).
void RenderFrameHostImpl::UpdateSubresourceLoaderFactories() {
  // Disregard this if frame is being destroyed.
  if (!frame_)
    return;

  // Disregard if the IPC payload would have been empty.
  //
  // Examples of when this can happen:
  // 1. DevTools tries to inject its proxies by calling this method on *all*
  //    frames, regardless of whether a frame has actually used one or more
  //    NetworkService-based URLLoaderFactory.  See also
  //    RenderFrameDevToolsAgentHost::UpdateResourceLoaderFactories.
  // 2. The RenderFrameHostImpl::CreateNetworkServiceDefaultFactory method
  //    (exposed via //content/public) is called on a frame that doesn't
  //    actually use a NetworkService-backed URLLoaderFactory.  This means
  //    that CreateNetworkServiceDefaultFactoryAndObserve sets up
  //    `network_service_disconnect_handler_holder_`, but
  //    `recreate_default_url_loader_factory_after_network_service_crash_` is
  //    false.
  if (!recreate_default_url_loader_factory_after_network_service_crash_ &&
      isolated_worlds_requiring_separate_url_loader_factory_.empty()) {
    return;
  }

  // TODO(https://crbug.com/1382971): Remove the crash key logging after the
  // ad-hoc bug investigation is no longer needed.
  SCOPED_CRASH_KEY_STRING256("rfhi-uslf", "frame->ToDebugString",
                             ToDebugString());

  // The `subresource_loader_factories_config` of the new factories might need
  // to depend on the pending (rather than the last committed) navigation,
  // because we can't predict if an in-flight Commit IPC might be present when
  // an extension injects a content script and MarkIsolatedWorlds... is called.
  // See also the doc comment for the ForPendingOrLastCommittedNavigation
  // method.
  auto subresource_loader_factories_config =
      SubresourceLoaderFactoriesConfig::ForPendingOrLastCommittedNavigation(
          *this);

  mojo::PendingRemote<network::mojom::URLLoaderFactory> default_factory_remote;
  bool bypass_redirect_checks = false;
  if (recreate_default_url_loader_factory_after_network_service_crash_) {
    DCHECK(!IsOutOfProcessNetworkService() ||
           network_service_disconnect_handler_holder_.is_bound());
    bypass_redirect_checks = CreateNetworkServiceDefaultFactoryAndObserve(
        CreateURLLoaderFactoryParamsForMainWorld(
            subresource_loader_factories_config,
            "RFHI::UpdateSubresourceLoaderFactories"),
        subresource_loader_factories_config.ukm_source_id(),
        default_factory_remote.InitWithNewPipeAndPassReceiver());
  }

  std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
      subresource_loader_factories =
          std::make_unique<blink::PendingURLLoaderFactoryBundle>(
              std::move(default_factory_remote),
              blink::PendingURLLoaderFactoryBundle::SchemeMap(),
              CreateURLLoaderFactoriesForIsolatedWorlds(
                  subresource_loader_factories_config,
                  isolated_worlds_requiring_separate_url_loader_factory_),
              bypass_redirect_checks);

  GetMojomFrameInRenderer()->UpdateSubresourceLoaderFactories(
      std::move(subresource_loader_factories));
}

blink::FrameOwnerElementType RenderFrameHostImpl::GetFrameOwnerElementType() {
  return frame_owner_element_type_;
}

bool RenderFrameHostImpl::HasTransientUserActivation() {
  return user_activation_state_.IsActive();
}

void RenderFrameHostImpl::NotifyUserActivation(
    blink::mojom::UserActivationNotificationType notification_type) {
  GetAssociatedLocalFrame()->NotifyUserActivation(notification_type);
}

void RenderFrameHostImpl::DidAccessInitialMainDocument() {
  frame_tree_->DidAccessInitialMainDocument();
}

void RenderFrameHostImpl::DidChangeName(const std::string& name,
                                        const std::string& unique_name) {
  // Frame name updates used to occur in the FrameTreeNode; however, as they
  // now occur in RenderFrameHostImpl (and by extension, BrowsingContextState),
  // ensure that invalid updates (i.e. when in the BackForwardCache or in a
  // pending deletion state) are not applied.
  if ((IsInBackForwardCache() || IsPendingDeletion()) &&
      base::FeatureList::IsEnabled(
          features::kDisableFrameNameUpdateOnNonCurrentRenderFrameHost)) {
    return;
  }
  if (GetParent() != nullptr) {
    // TODO(lukasza): Call ReceivedBadMessage when |unique_name| is empty.
    DCHECK(!unique_name.empty());
  }
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::OnDidChangeName",
               "render_frame_host", this, "name", name);

  std::string old_name = browsing_context_state_->frame_name();
  browsing_context_state_->SetFrameName(name, unique_name);
  if (old_name.empty() && !name.empty())
    frame_tree_node_->render_manager()->CreateProxiesForNewNamedFrame(
        browsing_context_state_);
  delegate_->DidChangeName(this, name);
}

void RenderFrameHostImpl::EnforceInsecureRequestPolicy(
    blink::mojom::InsecureRequestPolicy policy) {
  browsing_context_state_->SetInsecureRequestPolicy(policy);
}

void RenderFrameHostImpl::EnforceInsecureNavigationsSet(
    const std::vector<uint32_t>& set) {
  browsing_context_state_->SetInsecureNavigationsSet(set);
}

void RenderFrameHostImpl::AddDocumentService(
    internal::DocumentServiceBase* document_service,
    base::PassKey<internal::DocumentServiceBase>) {
  document_associated_data_->services().push_back(document_service);
}

void RenderFrameHostImpl::RemoveDocumentService(
    internal::DocumentServiceBase* document_service,
    base::PassKey<internal::DocumentServiceBase>) {
  if (document_service == last_web_bluetooth_service_for_testing_) {
    last_web_bluetooth_service_for_testing_ = nullptr;
  }
  base::Erase(document_associated_data_->services(), document_service);
}

FrameTreeNode* RenderFrameHostImpl::FindAndVerifyChild(
    int32_t child_frame_routing_id,
    bad_message::BadMessageReason reason) {
  auto child_frame_or_proxy = LookupRenderFrameHostOrProxy(
      GetProcess()->GetID(), child_frame_routing_id);
  return FindAndVerifyChildInternal(child_frame_or_proxy, reason);
}

FrameTreeNode* RenderFrameHostImpl::FindAndVerifyChild(
    const blink::FrameToken& child_frame_token,
    bad_message::BadMessageReason reason) {
  auto child_frame_or_proxy =
      LookupRenderFrameHostOrProxy(GetProcess()->GetID(), child_frame_token);
  return FindAndVerifyChildInternal(child_frame_or_proxy, reason);
}

FrameTreeNode* RenderFrameHostImpl::FindAndVerifyChildInternal(
    RenderFrameHostOrProxy child_frame_or_proxy,
    bad_message::BadMessageReason reason) {
  // A race can result in |child| to be nullptr. Avoid killing the renderer in
  // that case.
  if (!child_frame_or_proxy)
    return nullptr;

  if (&child_frame_or_proxy.GetFrameTreeNode()->frame_tree() != frame_tree()) {
    // Ignore the cases when the child lives in a different frame tree.
    // This is possible when we create a proxy for inner WebContents (e.g.
    // for portals) so the |child_frame_or_proxy| points to the root frame
    // of the nested WebContents, which is in a different tree.
    // TODO(altimin, lfg): Reconsider what the correct behaviour here should be.
    return nullptr;
  }

  if (child_frame_or_proxy.GetFrameTreeNode()->parent() != this) {
    bad_message::ReceivedBadMessage(GetProcess(), reason);
    return nullptr;
  }
  return child_frame_or_proxy.GetFrameTreeNode();
}

void RenderFrameHostImpl::UpdateTitle(
    const absl::optional<::std::u16string>& title,
    base::i18n::TextDirection title_direction) {
  // This message should only be sent for top-level frames.
  if (!is_main_frame())
    return;

  std::u16string received_title;
  if (title.has_value())
    received_title = title.value();

  if (received_title.length() > blink::mojom::kMaxTitleChars) {
    mojo::ReportBadMessage("Renderer sent too many characters in title.");
    return;
  }

  delegate_->UpdateTitle(this, received_title, title_direction);
}

void RenderFrameHostImpl::DidInferColorScheme(
    blink::mojom::PreferredColorScheme color_scheme) {
  if (is_main_frame()) {
    GetPage().DidInferColorScheme(color_scheme);
  }
}

void RenderFrameHostImpl::UpdateEncoding(const std::string& encoding_name) {
  if (!is_main_frame()) {
    mojo::ReportBadMessage("Renderer sent updated encoding for a subframe.");
    return;
  }

  GetPage().UpdateEncoding(encoding_name);
}

void RenderFrameHostImpl::FullscreenStateChanged(
    bool is_fullscreen,
    blink::mojom::FullscreenOptionsPtr options) {
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kFullScreenStateChanged))
    return;
  delegate_->FullscreenStateChanged(this, is_fullscreen, std::move(options));
}

void RenderFrameHostImpl::RegisterProtocolHandler(const std::string& scheme,
                                                  const GURL& url,
                                                  bool user_gesture) {
  delegate_->RegisterProtocolHandler(this, scheme, url, user_gesture);
}

void RenderFrameHostImpl::UnregisterProtocolHandler(const std::string& scheme,
                                                    const GURL& url,
                                                    bool user_gesture) {
  delegate_->UnregisterProtocolHandler(this, scheme, url, user_gesture);
}

void RenderFrameHostImpl::DidDisplayInsecureContent() {
  frame_tree_->controller().ssl_manager()->DidDisplayMixedContent();
}

void RenderFrameHostImpl::DidContainInsecureFormAction() {
  frame_tree_->controller().ssl_manager()->DidContainInsecureFormAction();
}

void RenderFrameHostImpl::MainDocumentElementAvailable(
    bool uses_temporary_zoom_level) {
  if (!is_main_frame()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_INVALID_CALL_FROM_NOT_MAIN_FRAME);
    return;
  }

  GetPage().set_is_main_document_element_available(true);
  GetPage().set_uses_temporary_zoom_level(uses_temporary_zoom_level);

  // Don't dispatch PrimaryMainDocumentElementAvailable for non-primary
  // RenderFrameHosts. As most of the observers are interested only in taking
  // into account and can interact with or send IPCs to only the current
  // document in the primary main frame. Since the WebContents could be hosting
  // more than one main frame (e.g., fenced frame, prerender pages or pending
  // delete RFHs), return early for other cases.
  if (!IsInPrimaryMainFrame())
    return;

  delegate_->PrimaryMainDocumentElementAvailable();

  if (base::FeatureList::IsEnabled(blink::features::kFullscreenPopupWindows) &&
      fullscreen_document_on_document_element_ready_ &&
      (*fullscreen_document_on_document_element_ready_) == GetDocumentToken()) {
    fullscreen_document_on_document_element_ready_ = absl::nullopt;
    // Create a fullscreen request token to waive the user activation
    // requirement when the request is received back to the browser in
    // `EnterFullscreen`.
    fullscreen_request_token_.Activate();
    GetAssociatedLocalFrame()->RequestFullscreenDocumentElement();
  }

  if (!uses_temporary_zoom_level)
    return;

#if !BUILDFLAG(IS_ANDROID)
  HostZoomMapImpl* host_zoom_map =
      static_cast<HostZoomMapImpl*>(HostZoomMap::Get(GetSiteInstance()));
  host_zoom_map->SetTemporaryZoomLevel(GetGlobalId(),
                                       host_zoom_map->GetDefaultZoomLevel());
#endif  // !BUILDFLAG(IS_ANDROID)
}

void RenderFrameHostImpl::SetNeedsOcclusionTracking(bool needs_tracking) {
  // Do not update the parent on behalf of inactive RenderFrameHost. See also
  // https://crbug.com/972566.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kSetNeedsOcclusionTracking))
    return;

  RenderFrameProxyHost* proxy = GetProxyToParent();
  if (!proxy) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_NO_PROXY_TO_PARENT);
    return;
  }

  if (proxy->is_render_frame_proxy_live()) {
    proxy->GetAssociatedRemoteFrame()->SetNeedsOcclusionTracking(
        needs_tracking);
  }
}

void RenderFrameHostImpl::SetVirtualKeyboardMode(
    ui::mojom::VirtualKeyboardMode mode) {
  // TODO(crbug.com/1225366): Consider moving this to PageImpl.
  if (GetOutermostMainFrame() != this) {
    bad_message::ReceivedBadMessage(
        GetProcess(),
        bad_message::RFHI_SET_OVERLAYS_CONTENT_NOT_OUTERMOST_FRAME);
    return;
  }
  GetPage().SetVirtualKeyboardMode(mode);
}

#if BUILDFLAG(IS_ANDROID)
void RenderFrameHostImpl::UpdateUserGestureCarryoverInfo() {
  // This should not occur for prerenders but may occur for pages in
  // the BackForwardCache depending on timing.
  if (!IsActive())
    return;
  delegate_->UpdateUserGestureCarryoverInfo();
}
#endif

void RenderFrameHostImpl::VisibilityChanged(
    blink::mojom::FrameVisibility visibility) {
  visibility_ = visibility;
}

void RenderFrameHostImpl::DidChangeThemeColor(
    absl::optional<SkColor> theme_color) {
  // TODO(crbug.com/1225366): Consider moving this to PageImpl.
  DCHECK(is_main_frame());
  GetPage().OnThemeColorChanged(theme_color);
}

void RenderFrameHostImpl::DidChangeBackgroundColor(
    const SkColor4f& background_color,
    bool color_adjust) {
  // TODO(crbug.com/1225366): Consider moving this to PageImpl.
  DCHECK(is_main_frame());
  GetPage().DidChangeBackgroundColor(background_color, color_adjust);
}

#if defined(OHOS_EX_FREE_COPY)
void RenderFrameHostImpl::NotifyContextMenuWillShow() {
  delegate_->NotifyContextMenuWillShow();
}
#endif
void RenderFrameHostImpl::SetCommitCallbackInterceptorForTesting(
    CommitCallbackInterceptor* interceptor) {
  // This DCHECK's aims to avoid unexpected replacement of an interceptor.
  // If this becomes a legitimate use case, feel free to remove.
  DCHECK(!commit_callback_interceptor_ || !interceptor);
  commit_callback_interceptor_ = interceptor;
}

void RenderFrameHostImpl::SetCreateNewPopupCallbackForTesting(
    const CreateNewPopupWidgetCallbackForTesting& callback) {
  // This DCHECK aims to avoid unexpected replacement of a callback.
  DCHECK(!create_new_popup_widget_callback_ || !callback);
  create_new_popup_widget_callback_ = callback;
}

void RenderFrameHostImpl::SetUnloadACKCallbackForTesting(
    const UnloadACKCallbackForTesting& callback) {
  // This DCHECK aims to avoid unexpected replacement of a callback.
  DCHECK(!unload_ack_callback_ || !callback);
  unload_ack_callback_ = callback;
}

const net::HttpResponseHeaders* RenderFrameHostImpl::GetLastResponseHeaders() {
  // This shouldn't be called before committing the document as this value is
  // set during call to RenderFrameHostImpl::DidNavigate which happens after
  // commit.
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kPendingCommit);
  return last_response_head_ ? last_response_head_->headers.get() : nullptr;
}

void RenderFrameHostImpl::DidBlockNavigation(
    const GURL& blocked_url,
    const GURL& initiator_url,
    blink::mojom::NavigationBlockedReason reason) {
  // Do not allow renderers to show off-limits URLs in the blocked dialog.
  GURL validated_blocked_url = blocked_url;
  GURL validated_initiator_url = initiator_url;
  RenderProcessHost* process = GetProcess();
  process->FilterURL(/*empty_allowed=*/false, &validated_blocked_url);
  process->FilterURL(/*empty_allowed=*/false, &validated_initiator_url);

  // Cross-origin navigations are not allowed in prerendering so we can not
  // reach here while prerendering.
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kPrerendering);
  delegate_->OnDidBlockNavigation(validated_blocked_url,
                                  validated_initiator_url, reason);
}

void RenderFrameHostImpl::DidChangeLoadProgress(double load_progress) {
  if (!is_main_frame())
    return;

  if (load_progress < GetPage().load_progress())
    return;

  GetPage().set_load_progress(load_progress);

  // Only dispatch LoadProgressChanged for the primary main frame.
  if (IsInPrimaryMainFrame())
    delegate_->DidChangeLoadProgressForPrimaryMainFrame();
}

void RenderFrameHostImpl::DidFinishLoad(const GURL& validated_url) {
  // In case of prerendering, we dispatch DidFinishLoad on activation. This is
  // done to avoid notifying observers about a load event triggered from a
  // inactive RenderFrameHost.
  if (lifecycle_state() == LifecycleStateImpl::kPrerendering) {
    document_associated_data_->set_pending_did_finish_load_url_for_prerendering(
        validated_url);
    return;
  }

  delegate_->OnDidFinishLoad(this, validated_url);
}

void RenderFrameHostImpl::DispatchLoad() {
  TRACE_EVENT1("navigation", "RenderFrameHostImpl::DispatchLoad",
               "render_frame_host", this);

  // Only active and prerendered documents are allowed to dispatch load events
  // to the parent.
  if (lifecycle_state() != LifecycleStateImpl::kPrerendering) {
    // Don't forward the load event to the parent on behalf of inactive
    // RenderFrameHost. This can happen in a race where this inactive
    // RenderFrameHost finishes loading just after the frame navigates away.
    // See https://crbug.com/626802.
    if (IsInactiveAndDisallowActivation(
            DisallowActivationReasonId::kDispatchLoad))
      return;
  }

  DCHECK(lifecycle_state() == LifecycleStateImpl::kActive ||
         lifecycle_state() == LifecycleStateImpl::kPrerendering);

  // Only frames with an out-of-process parent frame should be sending this
  // message.
  RenderFrameProxyHost* proxy = GetProxyToParent();
  if (!proxy) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_NO_PROXY_TO_PARENT);
    return;
  }

  if (proxy->is_render_frame_proxy_live())
    proxy->GetAssociatedRemoteFrame()->DispatchLoadEventForFrameOwner();
}

void RenderFrameHostImpl::GoToEntryAtOffset(
    int32_t offset,
    bool has_user_gesture,
    absl::optional<blink::scheduler::TaskAttributionId>
        soft_navigation_heuristics_task_id) {
  OPTIONAL_TRACE_EVENT2("content", "RenderFrameHostImpl::GoToEntryAtOffset",
                        "render_frame_host", this, "offset", offset);

  // Non-user initiated navigations coming from the renderer should be ignored
  // if there is an ongoing browser-initiated navigation.
  // See https://crbug.com/879965.
  // TODO(arthursonzogni): See if this should check for ongoing navigations in
  // the frame(s) affected by the session history navigation, rather than just
  // the main frame.
  if (Navigator::ShouldIgnoreIncomingRendererRequest(
          frame_tree_->root()->navigation_request(), has_user_gesture)) {
    return;
  }

  // All frames are allowed to navigate the global history.
  if (delegate_->IsAllowedToGoToEntryAtOffset(offset)) {
    frame_tree_->controller().GoToOffsetFromRenderer(
        offset, this, soft_navigation_heuristics_task_id);
  }
}

void RenderFrameHostImpl::NavigateToNavigationApiKey(
    const std::string& key,
    bool has_user_gesture,
    absl::optional<blink::scheduler::TaskAttributionId> task_id) {
  // Non-user initiated navigations coming from the renderer should be ignored
  // if there is an ongoing browser-initiated navigation.
  // See https://crbug.com/879965.
  // TODO(arthursonzogni): See if this should check for ongoing navigations in
  // the frame(s) affected by the session history navigation, rather than just
  // the main frame.
  if (Navigator::ShouldIgnoreIncomingRendererRequest(
          frame_tree_->root()->navigation_request(), has_user_gesture)) {
    return;
  }
  frame_tree_->controller().NavigateToNavigationApiKey(this, task_id, key);
}

void RenderFrameHostImpl::NavigateEventHandlerPresenceChanged(bool present) {
  DCHECK_NE(has_navigate_event_handler_, present);
  has_navigate_event_handler_ = present;
}

void RenderFrameHostImpl::HandleAccessibilityFindInPageResult(
    blink::mojom::FindInPageResultAXParamsPtr params) {
  // Only update FindInPageResult on active RenderFrameHost. Note that, it is
  // safe to ignore this call for BackForwardCache, as we terminate the
  // FindInPage session once the page enters BackForwardCache.
  if (lifecycle_state() != LifecycleStateImpl::kActive)
    return;

  BrowserAccessibilityManager* manager =
      GetOrCreateBrowserAccessibilityManager();
  if (manager) {
    manager->OnFindInPageResult(params->request_id, params->match_index,
                                params->start_id, params->start_offset,
                                params->end_id, params->end_offset);
  }
}

void RenderFrameHostImpl::HandleAccessibilityFindInPageTermination() {
  // Only update FindInPageTermination on active RenderFrameHost. Note that, it
  // is safe to ignore this call for BackForwardCache, as we terminate the
  // FindInPage session once the page enters BackForwardCache.
  if (lifecycle_state() != LifecycleStateImpl::kActive)
    return;

  BrowserAccessibilityManager* manager =
      GetOrCreateBrowserAccessibilityManager();
  if (manager)
    manager->OnFindInPageTermination();
}

// TODO(crbug.com/1213863): Move this method to content::PageImpl.
void RenderFrameHostImpl::DocumentOnLoadCompleted() {
  if (!is_main_frame()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_INVALID_CALL_FROM_NOT_MAIN_FRAME);
    return;
  }

  GetPage().set_is_on_load_completed_in_main_document(true);

  // Don't dispatch DocumentOnLoadCompletedInPrimaryMainFrame for non-primary
  // main frames. As most of the observers are interested only in the onload
  // completion of the current document in the primary main frame. Since the
  // WebContents could be hosting more than one main frame (e.g., fenced frames,
  // prerender pages or pending delete RFHs), return early for other cases. In
  // case of prerendering, we dispatch DocumentOnLoadCompletedInPrimaryMainFrame
  // on activation.
  if (!IsInPrimaryMainFrame())
    return;

  // This message is only sent for top-level frames.
  //
  // TODO(avi): when frame tree mirroring works correctly, add a check here
  // to enforce it.
  delegate_->DocumentOnLoadCompleted(this);
}

void RenderFrameHostImpl::ForwardResourceTimingToParent(
    blink::mojom::ResourceTimingInfoPtr timing) {
  // Only active and prerendered documents are allowed to forward the resource
  // timing information to the parent.
  if (lifecycle_state() != LifecycleStateImpl::kPrerendering) {
    // Don't forward the resource timing of the parent on behalf of inactive
    // RenderFrameHost. This can happen in a race where this RenderFrameHost
    // finishes loading just after the frame navigates away. See
    // https://crbug.com/626802.
    if (IsInactiveAndDisallowActivation(
            DisallowActivationReasonId::kForwardResourceTimingToParent))
      return;
  }

  DCHECK(lifecycle_state() == LifecycleStateImpl::kActive ||
         lifecycle_state() == LifecycleStateImpl::kPrerendering);

  RenderFrameProxyHost* proxy = GetProxyToParent();
  if (!proxy) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_NO_PROXY_TO_PARENT);
    return;
  }
  if (proxy->is_render_frame_proxy_live()) {
    proxy->GetAssociatedRemoteFrame()->AddResourceTimingFromChild(
        std::move(timing));
  }
}

bool RenderFrameHostImpl::Reload() {
  // Reloading the document frame will delete the currently active one.
  // We expected this function to be used only when this RenderFrameHost
  // is the currently active one. RenderFrameHost pending deletion, or
  // in the BackForwardCache are not expected to have side effect on the
  // current page.
  CHECK(IsActive());

  return owner_->Reload();
}

void RenderFrameHostImpl::SendAccessibilityEventsToManager(
    const AXEventNotificationDetails& details) {
  if (!browser_accessibility_manager_)
    return;

  DCHECK(delegate_->GetAccessibilityMode().has_mode(ui::AXMode::kNativeAPIs));
  if (!browser_accessibility_manager_->OnAccessibilityEvents(details)) {
    // OnAccessibilityEvents returns false in IPC error conditions.
    AccessibilityFatalError();
  }
}

bool RenderFrameHostImpl::IsInactiveAndDisallowActivation(uint64_t reason) {
  TRACE_EVENT1("navigation",
               "RenderFrameHostImpl::IsInactiveAndDisallowActivation",
               "render_frame_host", this);
  switch (lifecycle_state_) {
    case LifecycleStateImpl::kRunningUnloadHandlers:
    case LifecycleStateImpl::kReadyToBeDeleted:
      return true;
    case LifecycleStateImpl::kInBackForwardCache: {
      // This function should not be called with kAXEvent when the page is in
      // back/forward cache, because |HandleAXevents()| will continue to process
      // accessibility events without evicting unless the kEvictOnAXEvents flag
      // is on.
      if (!base::FeatureList::IsEnabled(features::kEvictOnAXEvents))
        DCHECK_NE(reason, kAXEvent);
      BackForwardCacheCanStoreDocumentResult can_store_flat;
      can_store_flat.NoDueToDisallowActivation(reason);
      EvictFromBackForwardCacheWithFlattenedReasons(can_store_flat);
    }
      return true;
    case LifecycleStateImpl::kPrerendering:
      // Since the page in prerendering state is able to handle IndexedDB event,
      // the prerendering should not be cancelled because of that.
      DCHECK_NE(reason, DisallowActivationReasonId::kIndexedDBEvent);
      CancelPrerendering(
          PrerenderCancellationReason::BuildForDisallowActivationState(reason));
      return true;
    case LifecycleStateImpl::kSpeculative:
      // We do not expect speculative or pending commit RenderFrameHosts to
      // generate events that require an active/inactive check. Don't crash the
      // browser process in case it comes from a compromised renderer, but kill
      // the renderer to avoid further confusion.
      bad_message::ReceivedBadMessage(
          GetProcess(), bad_message::RFH_INACTIVE_CHECK_FROM_SPECULATIVE_RFH);
      return false;
    case LifecycleStateImpl::kPendingCommit:
      // TODO(https://crbug.com/1191469): Understand the expected behaviour to
      // disallow activation for kPendingCommit RenderFrameHosts and update
      // accordingly.
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFH_INACTIVE_CHECK_FROM_PENDING_COMMIT_RFH);
      return false;
    case LifecycleStateImpl::kActive:
      return false;
  }
}

bool RenderFrameHostImpl::IsInactiveAndDisallowActivationForAXEvents(
    const std::vector<ui::AXEvent>& events) {
  DCHECK(base::FeatureList::IsEnabled(features::kEvictOnAXEvents));
  if (lifecycle_state_ != LifecycleStateImpl::kInBackForwardCache) {
    return IsInactiveAndDisallowActivation(
        DisallowActivationReasonId::kAXEvent);
  }
  // If the lifecycle state is |LifecycleStateImpl::kInBackForwardCache|, we
  // cannot handle accessibility events any more. We should evict the entry.
  BackForwardCacheCanStoreDocumentResult can_store_flat;
  can_store_flat.NoDueToAXEvents(events);
  EvictFromBackForwardCacheWithFlattenedReasons(can_store_flat);
  return true;
}

void RenderFrameHostImpl::EvictFromBackForwardCache(
    blink::mojom::RendererEvictionReason reason) {
  EvictFromBackForwardCacheWithReason(
      RendererEvictionReasonToNotRestoredReason(reason));
}

void RenderFrameHostImpl::EvictFromBackForwardCacheWithReason(
    BackForwardCacheMetrics::NotRestoredReason reason) {
  // kIgnoreEventAndEvict should never be a reason on its own without further
  // details.
  DCHECK_NE(reason,
            BackForwardCacheMetrics::NotRestoredReason::kIgnoreEventAndEvict);

  BackForwardCacheCanStoreDocumentResult flattened_reasons;
  flattened_reasons.No(reason);
  EvictFromBackForwardCacheWithFlattenedReasons(flattened_reasons);
}

void RenderFrameHostImpl::EvictFromBackForwardCacheWithFlattenedReasons(
    BackForwardCacheCanStoreDocumentResult can_store_flat) {
  // Create a NotRestoredReasons tree that has |can_store_flat| as a reason
  // for |this| RenderFrameHost.
  auto can_store =
      BackForwardCacheImpl::CreateEvictionBackForwardCacheCanStoreTreeResult(
          *this, can_store_flat);
  EvictFromBackForwardCacheWithFlattenedAndTreeReasons(can_store);
}

void RenderFrameHostImpl::EvictFromBackForwardCacheWithFlattenedAndTreeReasons(
    BackForwardCacheCanStoreDocumentResultWithTree& can_store) {
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::EvictFromBackForwardCache",
               "can_store", can_store.flattened_reasons.ToString(), "rfh",
               static_cast<void*>(this));
  TRACE_EVENT("navigation",
              "RenderFrameHostImpl::"
              "EvictFromBackForwardCacheWithFlattenedAndTreeReasons",
              ChromeTrackEvent::kBackForwardCacheCanStoreDocumentResult,
              can_store.flattened_reasons);
  LOG(INFO) << "RenderFrameHostImpl::" << __func__ << " the value of can_stored flattened_reasons is: "
            << can_store.flattened_reasons.ToString();
  DCHECK(IsBackForwardCacheEnabled());

  RenderFrameHostImpl* top_document = GetOutermostMainFrame();

  if (top_document->is_evicted_from_back_forward_cache_)
    return;

  bool in_back_forward_cache = IsInBackForwardCache();

  // TODO(hajimehoshi): Record the 'race condition' by JavaScript execution when
  // |in_back_forward_cache| is false.
  BackForwardCacheMetrics* metrics = top_document->GetBackForwardCacheMetrics();
  if (in_back_forward_cache && metrics) {
    metrics->SetNotRestoredReasons(can_store);
  }

  if (!in_back_forward_cache) {
    TRACE_EVENT0("navigation", "BackForwardCache_EvictAfterDocumentRestored");
    // TODO(crbug.com/1163843): We should no longer get into this branch thanks
    // to https://crrev.com/c/2563674 but we do. We should eventually replace
    // this with a CHECK.
    BackForwardCacheMetrics::RecordEvictedAfterDocumentRestored(
        BackForwardCacheMetrics::EvictedAfterDocumentRestoredReason::
            kByJavaScript);
    CaptureTraceForNavigationDebugScenario(
        DebugScenario::kDebugBackForwardCacheEvictRestoreRace);

    // A document is evicted from the BackForwardCache, but it has already been
    // restored. The current document should be reloaded, because it is not
    // salvageable.
    top_document->frame_tree()->controller().Reload(ReloadType::NORMAL, false);
    return;
  }

  // Check if there is an in-flight navigation restoring the frame that
  // is being evicted.
  NavigationRequest* in_flight_navigation_request =
      top_document->frame_tree_node()->navigation_request();
  bool is_navigation_to_evicted_frame_in_flight =
      (in_flight_navigation_request &&
       in_flight_navigation_request->rfh_restored_from_back_forward_cache() ==
           top_document);

  if (is_navigation_to_evicted_frame_in_flight) {
    // If we are currently navigating to the frame that was just evicted, we
    // must restart the navigation. This is important because restarting the
    // navigation deletes the NavigationRequest associated with the evicted
    // frame (preventing use-after-free).
    // This should also happen asynchronously as eviction might happen in the
    // middle of another navigation — we should not try to restart the
    // navigation in that case.
    // NOTE: Here we rely on the PostTask inside this function running before
    // the task posted to destroy the evicted frames below.
    in_flight_navigation_request->RestartBackForwardCachedNavigation();
  }

  // Evict the frame and schedule it to be destroyed. Eviction happens
  // immediately, but destruction is delayed, so that callers don't have to
  // worry about use-after-free of |this|.
  top_document->is_evicted_from_back_forward_cache_ = true;
  GetBackForwardCache().PostTaskToDestroyEvictedFrames();
}

void RenderFrameHostImpl::
    UseDummyStickyBackForwardCacheDisablingFeatureForTesting() {
  OnBackForwardCacheDisablingFeatureUsed(
      BackForwardCacheDisablingFeature::kDummy);
}

bool RenderFrameHostImpl::HasSeenRecentXrOverlaySetup() {
  static constexpr base::TimeDelta kMaxInterval = base::Seconds(1);
  base::TimeDelta delta = base::TimeTicks::Now() - last_xr_overlay_setup_time_;
  DVLOG(2) << __func__ << ": return " << (delta <= kMaxInterval);
  return delta <= kMaxInterval;
}

void RenderFrameHostImpl::SetIsXrOverlaySetup() {
  DVLOG(2) << __func__;
  last_xr_overlay_setup_time_ = base::TimeTicks::Now();
}

// TODO(alexmos): When the allowFullscreen flag is known in the browser
// process, use it to double-check that fullscreen can be entered here.
void RenderFrameHostImpl::EnterFullscreen(
    blink::mojom::FullscreenOptionsPtr options,
    EnterFullscreenCallback callback) {
  const bool had_fullscreen_token = fullscreen_request_token_.IsActive();

  // Frames (possibly a subframe) that are not active nor belonging to a primary
  // page should not enter fullscreen.
  if (!IsActive() || !GetPage().IsPrimary()) {
    std::move(callback).Run(/*granted=*/false);
    return;
  }

  // Entering fullscreen requires a transient user activation, a fullscreen
  // capability delegation token, a user-generated screen orientation change, or
  // another feature-specific transient allowance.
  // CanEnterFullscreenWithoutUserActivation is only ever true in tests, to
  // allow fullscreen when mocking screen orientation changes.
  if (!delegate_->HasSeenRecentScreenOrientationChange() &&
      !WindowManagementAllowsFullscreen() && !HasSeenRecentXrOverlaySetup() &&
#if defined(OHOS_CUSTOM_VIDEO_PLAYER)
      !options->is_custom_media_player &&
#endif // OHOS_CUSTOM_VIDEO_PLAYER
      !GetContentClient()
           ->browser()
           ->CanEnterFullscreenWithoutUserActivation()) {
    // Consume any transient user activation and delegated fullscreen token.
    // Reject requests made without transient user activation or a token.
    // TODO(lanwei): Investigate whether we can terminate the renderer when
    // transient user activation and the delegated token are both inactive.
    CHECK(owner_);  // See `owner_` invariants about `IsActive()`.
    const bool consumed_activation = owner_->UpdateUserActivationState(
        blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
        blink::mojom::UserActivationNotificationType::kNone);
    const bool consumed_token = fullscreen_request_token_.ConsumeIfActive();
    if (!consumed_activation && !consumed_token) {
      DLOG(ERROR) << "Cannot enter fullscreen because there is no transient "
                  << "user activation, orientation change, XR overlay, nor "
                  << "capability delegation.";
      std::move(callback).Run(/*granted=*/false);
      return;
    }
  }

  if (!delegate_->CanEnterFullscreenMode(this, *options)) {
    std::move(callback).Run(/*granted=*/false);
    return;
  }

  // Allow sites with the Window Management permission to open a popup window
  // after requesting fullscreen on a specific screen of a multi-screen device.
  // This enables multi-screen content experiences from a single user gesture.
  const display::Screen* screen = display::Screen::GetScreen();
  display::Display display;
  if (screen && screen->GetNumDisplays() > 1 &&
      screen->GetDisplayWithDisplayId(options->display_id, &display) &&
      IsWindowManagementGranted(this)) {
    transient_allow_popup_.Activate();
  }

  std::move(callback).Run(/*granted=*/true);

  // Entering fullscreen from a cross-process subframe also affects all
  // renderers for ancestor frames, which will need to apply fullscreen CSS to
  // appropriate ancestor <iframe> elements, fire fullscreenchange events, etc.
  // Thus, walk through the ancestor chain of this frame and for each (parent,
  // child) pair, send a message about the pending fullscreen change to the
  // child's proxy in parent's SiteInstanceGroup. The renderer process will use
  // this to find the <iframe> element in the parent frame that will need
  // fullscreen styles. This is done at most once per SiteInstanceGroup: for
  // example, with a A-B-A-B hierarchy, if the bottom frame goes fullscreen,
  // this only needs to notify its parent, and Blink-side logic will take care
  // of applying necessary changes to the other two ancestors.
  std::set<SiteInstance*> notified_instances;
  notified_instances.insert(GetSiteInstance());
  for (RenderFrameHostImpl* rfh = this; rfh->GetParent();
       rfh = rfh->GetParent()) {
    SiteInstance* parent_site_instance = rfh->GetParent()->GetSiteInstance();
    if (base::Contains(notified_instances, parent_site_instance))
      continue;

    RenderFrameProxyHost* child_proxy =
        rfh->browsing_context_state()->GetRenderFrameProxyHost(
            static_cast<SiteInstanceImpl*>(parent_site_instance)->group());
    if (child_proxy->is_render_frame_proxy_live()) {
      child_proxy->GetAssociatedRemoteFrame()->WillEnterFullscreen(
          options.Clone());
      notified_instances.insert(parent_site_instance);
    }
  }

  // Focus the window if another frame may have delegated the capability.
  if (had_fullscreen_token && !GetView()->HasFocus())
    GetView()->Focus();
  delegate_->EnterFullscreenMode(this, *options);
  delegate_->FullscreenStateChanged(this, /*is_fullscreen=*/true,
                                    std::move(options));

  // The previous call might change the fullscreen state. We need to make sure
  // the renderer is aware of that, which is done via the resize message.
  // Typically, this will be sent as part of the call on the |delegate_| above
  // when resizing the native windows, but sometimes fullscreen can be entered
  // without causing a resize, so we need to ensure that the resize message is
  // sent in that case. We always send this to the main frame's widget, and if
  // there are any OOPIF widgets, this will also trigger them to resize via
  // frameRectsChanged.
  render_view_host_->GetWidget()->SynchronizeVisualProperties();
}

// TODO(alexmos): When the allowFullscreen flag is known in the browser
// process, use it to double-check that fullscreen can be entered here.
void RenderFrameHostImpl::ExitFullscreen() {
  delegate_->ExitFullscreenMode(/*will_cause_resize=*/true);

  // The previous call might change the fullscreen state. We need to make sure
  // the renderer is aware of that, which is done via the resize message.
  // Typically, this will be sent as part of the call on the |delegate_| above
  // when resizing the native windows, but sometimes fullscreen can be entered
  // without causing a resize, so we need to ensure that the resize message is
  // sent in that case. We always send this to the main frame's widget, and if
  // there are any OOPIF widgets, this will also trigger them to resize via
  // frameRectsChanged.
  render_view_host_->GetWidget()->SynchronizeVisualProperties();
}

void RenderFrameHostImpl::SuddenTerminationDisablerChanged(
    bool present,
    blink::mojom::SuddenTerminationDisablerType disabler_type) {
  switch (disabler_type) {
    case blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler:
      DCHECK_NE(has_before_unload_handler_, present);
      if (IsNestedWithinFencedFrame()) {
        bad_message::ReceivedBadMessage(
            GetProcess(),
            bad_message::RFH_BEFOREUNLOAD_HANDLER_NOT_ALLOWED_IN_FENCED_FRAME);
        return;
      }
      has_before_unload_handler_ = present;
      break;
    case blink::mojom::SuddenTerminationDisablerType::kPageHideHandler:
      DCHECK_NE(has_pagehide_handler_, present);
      has_pagehide_handler_ = present;
      break;
    case blink::mojom::SuddenTerminationDisablerType::kUnloadHandler:
      DCHECK_NE(has_unload_handler_, present);
      if (IsNestedWithinFencedFrame()) {
        bad_message::ReceivedBadMessage(
            GetProcess(),
            bad_message::RFH_UNLOAD_HANDLER_NOT_ALLOWED_IN_FENCED_FRAME);
        return;
      }
      has_unload_handler_ = present;
      break;
    case blink::mojom::SuddenTerminationDisablerType::kVisibilityChangeHandler:
      DCHECK_NE(has_visibilitychange_handler_, present);
      has_visibilitychange_handler_ = present;
      break;
  }
}

bool RenderFrameHostImpl::GetSuddenTerminationDisablerState(
    blink::mojom::SuddenTerminationDisablerType disabler_type) {
  if (do_not_delete_for_testing_ &&
      disabler_type !=
          blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler) {
    return true;
  }
  switch (disabler_type) {
    case blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler:
      return has_before_unload_handler_;
    case blink::mojom::SuddenTerminationDisablerType::kPageHideHandler:
      return has_pagehide_handler_;
    case blink::mojom::SuddenTerminationDisablerType::kUnloadHandler:
      return has_unload_handler_;
    case blink::mojom::SuddenTerminationDisablerType::kVisibilityChangeHandler:
      return has_visibilitychange_handler_;
  }
}

void RenderFrameHostImpl::DidDispatchDOMContentLoadedEvent() {
  document_associated_data_->MarkDomContentLoaded();

  // In case of prerendering, we dispatch DOMContentLoaded on activation. This
  // is done to avoid notifying observers about a load event triggered from a
  // inactive RenderFrameHost.
  if (lifecycle_state() == LifecycleStateImpl::kPrerendering)
    return;

  delegate_->DOMContentLoaded(this);
}

void RenderFrameHostImpl::FocusedElementChanged(
    bool is_editable_element,
    bool is_richly_editable_element,
    const gfx::Rect& bounds_in_frame_widget,
    blink::mojom::FocusType focus_type) {
  if (!GetView())
    return;

  has_focused_editable_element_ = is_editable_element;
  has_focused_richly_editable_element_ = is_richly_editable_element;

  // First convert the bounds to root view.
  delegate_->OnFocusedElementChangedInFrame(
      this,
      gfx::Rect(GetView()->TransformPointToRootCoordSpace(
                    bounds_in_frame_widget.origin()),
                bounds_in_frame_widget.size()),
      focus_type);
}

void RenderFrameHostImpl::TextSelectionChanged(const std::u16string& text,
                                               uint32_t offset,
                                               const gfx::Range& range) {
  RecordAction(base::UserMetricsAction("TextSelectionChanged"));
  has_selection_ = !text.empty();
  GetRenderWidgetHost()->SelectionChanged(text, offset, range);
}

void RenderFrameHostImpl::DidReceiveUserActivation() {
  delegate_->DidReceiveUserActivation(this);
}

void RenderFrameHostImpl::MaybeIsolateForUserActivation() {
  // If user activation occurs in a frame that previously triggered a site
  // isolation hint based on the Cross-Origin-Opener-Policy header, isolate the
  // corresponding site for all future BrowsingInstances.  We also do this for
  // user activation on any same-origin subframe of such COOP main frames.
  //
  // Note that without user activation, COOP-triggered site isolation is scoped
  // only to the current BrowsingInstance.  This prevents malicious sites from
  // silently loading COOP sites to put them on the isolation list and later
  // querying that state.
  if (GetMainFrame()
          ->GetSiteInstance()
          ->GetSiteInfo()
          .does_site_request_dedicated_process_for_coop()) {
    // The SiteInfo flag above should guarantee that we've already passed all
    // the isolation eligibility checks, such as having the corresponding
    // feature enabled or satisfying memory requirements.
    DCHECK(base::FeatureList::IsEnabled(
        features::kSiteIsolationForCrossOriginOpenerPolicy));

    bool is_same_origin_activation =
        GetParent() ? GetMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
                          GetLastCommittedOrigin())
                    : true;
    if (is_same_origin_activation) {
      SiteInstance::StartIsolatingSite(
          GetSiteInstance()->GetBrowserContext(),
          GetMainFrame()->GetLastCommittedURL(),
          ChildProcessSecurityPolicy::IsolatedOriginSource::WEB_TRIGGERED,
          SiteIsolationPolicy::ShouldPersistIsolatedCOOPSites());
    }
  }
}

void RenderFrameHostImpl::UpdateUserActivationState(
    blink::mojom::UserActivationUpdateType update_type,
    blink::mojom::UserActivationNotificationType notification_type) {
  // Don't update UserActivationState for non-active RenderFrameHost. In case
  // of BackForwardCache, this is only called for tests and it is safe to ignore
  // such requests.
  if (lifecycle_state() != LifecycleStateImpl::kActive)
    return;

  CHECK(owner_);  // See `owner_` invariants about `lifecycle_state_`.
  owner_->UpdateUserActivationState(update_type, notification_type);
}

void RenderFrameHostImpl::DidConsumeHistoryUserActivation() {
  // owner_ may be null for IsPendingDeletion() or IsInBackForwardCache(), in
  // which case the history user activation is managed by a different active
  // RenderFrameHost.
  if (owner_) {
    owner_->DidConsumeHistoryUserActivation();
  }
}

void RenderFrameHostImpl::HadStickyUserActivationBeforeNavigationChanged(
    bool value) {
  browsing_context_state_->OnSetHadStickyUserActivationBeforeNavigation(value);
}

void RenderFrameHostImpl::ScrollRectToVisibleInParentFrame(
    const gfx::RectF& rect_to_scroll,
    blink::mojom::ScrollIntoViewParamsPtr params) {
  // Do not update the parent on behalf of inactive RenderFrameHost.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kDispatchLoad)) {
    return;
  }

  RenderFrameProxyHost* proxy = nullptr;

  if (IsFencedFrameRoot()) {
    // TODO(bokan): This is overly trusting of the renderer. We'll need some way
    // to verify that the browser is the one that initiated this
    // ScrollFocusedEditable process.
    // https://crbug.com/1123606. I&S tracker row 191.
    if (!params->for_focused_editable) {
      local_frame_host_receiver_.ReportBadMessage(
          "ScrollRectToVisibleInParentFrame can only be used for "
          "is_for_editable from a fenced frame");
      return;
    }

    proxy = GetProxyToOuterDelegate();
  } else {
    proxy = GetProxyToParent();
  }

  if (!proxy)
    return;

  proxy->ScrollRectToVisible(rect_to_scroll, std::move(params));
}

void RenderFrameHostImpl::BubbleLogicalScrollInParentFrame(
    blink::mojom::ScrollDirection direction,
    ui::ScrollGranularity granularity) {
  // Do not update the parent on behalf of inactive RenderFrameHost.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kDispatchLoad)) {
    return;
  }

  // TODO(bokan): This is overly trusting of the renderer. Ideally we'd check
  // that a keyboard event was recently sent. https://crbug.com/1123606. I&S
  // tracker row 191.
  RenderFrameProxyHost* proxy =
      IsFencedFrameRoot() ? GetProxyToOuterDelegate() : GetProxyToParent();

  if (!proxy) {
    // Only frames with an out-of-process parent frame should be sending this
    // message.
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_NO_PROXY_TO_PARENT);
    return;
  }

  if (proxy->is_render_frame_proxy_live()) {
    proxy->GetAssociatedRemoteFrame()->BubbleLogicalScroll(direction,
                                                           granularity);
  }
}

void RenderFrameHostImpl::ShowPopupMenu(
    mojo::PendingRemote<blink::mojom::PopupMenuClient> popup_client,
    const gfx::Rect& bounds,
    int32_t item_height,
    double font_size,
    int32_t selected_item,
    std::vector<blink::mojom::MenuItemPtr> menu_items,
    bool right_aligned,
    bool allow_multiple_selection) {
#if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
  // Do not open popups in an inactive document.
  if (!IsActive()) {
    // Sending this message requires user activation, which is impossible
    // for a prerendering document, so the renderer process should be
    // terminated. See
    // https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation.
    if (lifecycle_state() == LifecycleStateImpl::kPrerendering) {
      bad_message::ReceivedBadMessage(
          GetProcess(), bad_message::RFH_POPUP_REQUEST_WHILE_PRERENDERING);
    }
    return;
  }

  if (delegate()->ShowPopupMenu(this, bounds)) {
    return;
  }

  auto* view = render_view_host()->delegate_->GetDelegateView();
  if (!view)
    return;

  gfx::Point original_point(bounds.x(), bounds.y());
  gfx::Point transformed_point =
      static_cast<RenderWidgetHostViewBase*>(GetView())
          ->TransformPointToRootCoordSpace(original_point);
  gfx::Rect transformed_bounds(transformed_point.x(), transformed_point.y(),
                               bounds.width(), bounds.height());
  view->ShowPopupMenu(this, std::move(popup_client), transformed_bounds,
                      item_height, font_size, selected_item,
                      std::move(menu_items), right_aligned,
                      allow_multiple_selection);
#endif
}

#if defined(OHOS_CLIPBOARD)
void RenderFrameHostImpl::MouseSelectMenuShow(bool show) {
  if (delegate_) {
    delegate_->MouseSelectMenuShow(show);
  }
}

void RenderFrameHostImpl::ChangeVisibilityOfQuickMenu() {
  if (delegate_) {
    delegate_->ChangeVisibilityOfQuickMenu();
  }
}
#endif

#ifdef OHOS_AI
void RenderFrameHostImpl::CloseImageOverlaySelection() {
  if (delegate_) {
    delegate_->CloseImageOverlaySelection();
  }
}
#endif

void RenderFrameHostImpl::ShowContextMenu(
    mojo::PendingAssociatedRemote<blink::mojom::ContextMenuClient>
        context_menu_client,
    const blink::UntrustworthyContextMenuParams& params) {
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kShowContextMenu))
    return;

  // Validate the URLs in |params|.  If the renderer can't request the URLs
  // directly, don't show them in the context menu.
  ContextMenuParams validated_params(params);
  // Freshly constructed ContextMenuParams have empty `page_url` and `frame_url`
  // - populate them based on trustworthy, browser-side data.
  validated_params.page_url = GetOutermostMainFrame()->GetLastCommittedURL();
  validated_params.frame_url = GetLastCommittedURL();
  validated_params.frame_origin = GetLastCommittedOrigin();
  validated_params.is_subframe = !!GetParentOrOuterDocument();

  // We don't validate |unfiltered_link_url| so that this field can be used
  // when users want to copy the original link URL.
  RenderProcessHost* process = GetProcess();
  process->FilterURL(true, &validated_params.link_url);
  process->FilterURL(true, &validated_params.src_url);
  // In theory `page_url` and `frame_url` come from a trustworthy data source
  // (from the browser process) and therefore don't need to be validated via
  // FilterURL below.  In practice, some scenarios depend on clearing of the
  // `page_url` - see https://crbug.com/1285312.
  process->FilterURL(false, &validated_params.page_url);
  process->FilterURL(true, &validated_params.frame_url);

  // It is necessary to transform the coordinates to account for nested
  // RenderWidgetHosts, such as with out-of-process iframes.
  gfx::Point original_point(validated_params.x, validated_params.y);
  gfx::Point transformed_point =
      static_cast<RenderWidgetHostViewBase*>(GetView())
          ->TransformPointToRootCoordSpace(original_point);
  validated_params.x = transformed_point.x();
  validated_params.y = transformed_point.y();

#ifdef OHOS_EX_TOPCONTROLS
  if (GetView()) {
    validated_params.y += GetView()->GetTopControlsOffset();
  }
#endif

  if (validated_params.selection_start_offset < 0) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_NEGATIVE_SELECTION_START_OFFSET);
    return;
  }

  delegate_->ShowContextMenu(*this, std::move(context_menu_client),
                             validated_params);
}

void RenderFrameHostImpl::DidLoadResourceFromMemoryCache(
    const GURL& url,
    const std::string& http_method,
    const std::string& mime_type,
    network::mojom::RequestDestination request_destination,
    bool include_credentials) {
  delegate_->DidLoadResourceFromMemoryCache(this, url, http_method, mime_type,
                                            request_destination,
                                            include_credentials);
}

void RenderFrameHostImpl::DidChangeFrameOwnerProperties(
    const blink::FrameToken& child_frame_token,
    blink::mojom::FrameOwnerPropertiesPtr properties) {
  auto* child =
      FindAndVerifyChild(child_frame_token, bad_message::RFH_OWNER_PROPERTY);
  if (!child)
    return;

  bool has_display_none_property_changed =
      properties->is_display_none !=
      child->frame_owner_properties().is_display_none;

  child->set_frame_owner_properties(*properties);

  child->render_manager()->OnDidUpdateFrameOwnerProperties(*properties);
  if (has_display_none_property_changed) {
    delegate_->DidChangeDisplayState(child->current_frame_host(),
                                     properties->is_display_none);
  }
}

void RenderFrameHostImpl::DidChangeOpener(
    const absl::optional<blink::LocalFrameToken>& opener_frame_token) {
  // `owner_` could be null when we get this message asynchronously from the
  // renderer in pending deletion state.
  if (!owner_)
    return;

  owner_->GetRenderFrameHostManager().DidChangeOpener(
      opener_frame_token, GetSiteInstance()->group());
}

void RenderFrameHostImpl::DidChangeIframeAttributes(
    const blink::FrameToken& child_frame_token,
    blink::mojom::IframeAttributesPtr attributes) {
  if (attributes->parsed_csp_attribute &&
      !ValidateCSPAttribute(
          attributes->parsed_csp_attribute->header->header_value)) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::RFH_CSP_ATTRIBUTE);
    return;
  }

  if (attributes->browsing_topics &&
      !base::FeatureList::IsEnabled(blink::features::kBrowsingTopics)) {
    bad_message::ReceivedBadMessage(
        GetProcess(),
        bad_message::RFH_RECEIVED_INVALID_BROWSING_TOPICS_ATTRIBUTE);
    return;
  }

  auto* child = FindAndVerifyChild(
      child_frame_token, bad_message::RFH_DID_CHANGE_IFRAME_ATTRIBUTE);
  if (!child)
    return;

  child->SetAttributes(std::move(attributes));
}

void RenderFrameHostImpl::DidChangeFramePolicy(
    const blink::FrameToken& child_frame_token,
    const blink::FramePolicy& frame_policy) {
  // Ensure that a frame can only update sandbox flags or permissions policy for
  // its immediate children.  If this is not the case, the renderer is
  // considered malicious and is killed.
  FrameTreeNode* child = FindAndVerifyChild(
      // TODO(iclelland): Rename this message
      child_frame_token, bad_message::RFH_SANDBOX_FLAGS);
  if (!child)
    return;

  child->SetPendingFramePolicy(frame_policy);

  // Notify the RenderFrame if it lives in a different process from its parent.
  // The frame's proxies in other processes also need to learn about the updated
  // flags and policy, but these notifications are sent later in
  // RenderFrameHostManager::CommitPendingFramePolicy(), when the frame
  // navigates and the new policies take effect.
  if (child->current_frame_host()->GetSiteInstance() != GetSiteInstance()) {
    child->current_frame_host()
        ->GetAssociatedLocalFrame()
        ->DidUpdateFramePolicy(frame_policy);
  }
}

void RenderFrameHostImpl::CapturePaintPreviewOfSubframe(
    const gfx::Rect& clip_rect,
    const base::UnguessableToken& guid) {
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kCapturePaintPreview))
    return;
  // This should only be called on a subframe.
  if (IsOutermostMainFrame()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_SUBFRAME_CAPTURE_ON_MAIN_FRAME);
    return;
  }

  delegate()->CapturePaintPreviewOfCrossProcessSubframe(clip_rect, guid, this);
}

void RenderFrameHostImpl::SetCloseListener(
    mojo::PendingRemote<blink::mojom::CloseListener> listener) {
  CloseListenerHost::GetOrCreateForCurrentDocument(this)->SetListener(
      std::move(listener));
}

void RenderFrameHostImpl::BindBrowserInterfaceBrokerReceiver(
    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver) {
  DCHECK(receiver.is_valid());
  if (frame_tree()->is_prerendering()) {
    // RenderFrameHostImpl will rebind the receiver end of
    // BrowserInterfaceBroker if it receives a new one sent from renderer
    // processes. It happens when renderer processes navigate to a new document,
    // see RenderFrameImpl::DidCommitNavigation() and
    // RenderFrameHostImpl::DidCommitNavigation(). So before binding a new
    // receiver end of BrowserInterfaceBroker, RenderFrameHostImpl should drop
    // all deferred binders to avoid connecting Mojo pipes with old documents.
    DCHECK(mojo_binder_policy_applier_)
        << "prerendering pages should have a policy applier";
    mojo_binder_policy_applier_->DropDeferredBinders();
  }
  broker_receiver_.Bind(std::move(receiver));
  broker_receiver_.SetFilter(
      std::make_unique<internal::ActiveUrlMessageFilter>(this));
}

void RenderFrameHostImpl::BindAssociatedInterfaceProviderReceiver(
    mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
        receiver) {
  DCHECK(receiver.is_valid());
  associated_interface_provider_receiver_.Bind(std::move(receiver));
}

void RenderFrameHostImpl::BindDomOperationControllerHostReceiver(
    mojo::PendingAssociatedReceiver<mojom::DomAutomationControllerHost>
        receiver) {
  DCHECK(receiver.is_valid());
  // In the renderer side, the remote is document-associated so the receiver on
  // the browser side can be reused after a cross-document navigation.
  // TODO(dcheng): Make this document-associated?
  dom_automation_controller_receiver_.reset();
  dom_automation_controller_receiver_.Bind(std::move(receiver));
  dom_automation_controller_receiver_.SetFilter(
      CreateMessageFilterForAssociatedReceiver(
          mojom::DomAutomationControllerHost::Name_));
}

void RenderFrameHostImpl::SetKeepAliveTimeoutForTesting(
    base::TimeDelta timeout) {
  keep_alive_handle_factory_.set_timeout(timeout);
}

void RenderFrameHostImpl::UpdateState(const blink::PageState& state) {
  OPTIONAL_TRACE_EVENT1("content", "RenderFrameHostImpl::UpdateState",
                        "render_frame_host", this);
  // TODO(creis): Verify the state's ISN matches the last committed FNE.

  // Without this check, the renderer can trick the browser into using
  // filenames it can't access in a future session restore.
  if (!CanAccessFilesOfPageState(state)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_CAN_ACCESS_FILES_OF_PAGE_STATE);
    return;
  }

  frame_tree_->controller().UpdateStateForFrame(this, state);
}

void RenderFrameHostImpl::OpenURL(blink::mojom::OpenURLParamsPtr params) {
  // Verify and unpack the Mojo payload.
  GURL validated_url;
  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  if (!VerifyOpenURLParams(this, GetProcess(), params, &validated_url,
                           &blob_url_loader_factory)) {
    return;
  }

  // If the flag `is_unfenced_top_navigation` is set, this is a special code
  // path for MPArch fenced frames. The target frame doesn't have a handle
  // inside the MPArch renderer process, so we need to set it here.
  // TODO(crbug.com/1315802): Refactor _unfencedTop handling.
  if (params->is_unfenced_top_navigation) {
    GURL validated_params_url = params->url;

    // Check that the IPC parameters are valid and that the navigation
    // is allowed.
    // TODO(crbug.com/1315802): When this handling is refactored into a separate
    // IPC, make sure that the checks from VerifyOpenURLParams above are not
    // unintentionally weakened.
    if (!ValidateUnfencedTopNavigation(this, validated_params_url,
                                       GetProcess()->GetID(), params->post_body,
                                       params->user_gesture)) {
      return;
    }

    TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL(_unfencedTop)",
                 "url", validated_params_url.possibly_invalid_spec());

    // There are some relevant parameter changes to note:
    // Change the navigation target to the outermost frame.
    // This escapes Portals but not GuestViews.
    // - We don't especially care about Portals, because the frame won't be
    //   user activated until it's no longer in a portal
    // - We don't want _unfencedTop navigations to escape a GuestView
    //   (<webview>) and affect their embedder.
    RenderFrameHostImpl* target_frame = GetOutermostMainFrame();

    // Change `should_replace_current_entry` to false.
    // Fenced frames are enforced to have a history of length 1. Because the
    // renderer thinks this navigation is to the fenced frame root, it sets
    // `should_replace_current_entry` to true, but we do not want this
    // restriction for navigations outside the fenced frame.
    // TODO(crbug.com/1315802): Make sure that the browser doesn't rely on
    // whether the renderer says we should replace the current entry, i.e.
    // make sure there are no situations where we should actually replace the
    // current entry but don't, due to this line.
    bool should_replace_current_entry = false;

    // TODO(crbug.com/1315802): Null out the initiator origin, frame token, and
    // site instance.
    // We use an opaque `initiator_origin` in order to avoid leaking
    // information from the fenced frame to its embedder. (The navigation will
    // be treated as cross-origin unconditionally.) We don't need to provide a
    // `source_site_instance`.
    // url::Origin initiator_origin;

    // TODO(crbug.com/1237552): Resolve the discussion of download policy.
    blink::NavigationDownloadPolicy download_policy;

    target_frame->frame_tree_node()->navigator().NavigateFromFrameProxy(
        target_frame, validated_params_url,
        base::OptionalToPtr(params->initiator_frame_token),
        GetProcess()->GetID(), params->initiator_origin,
        params->initiator_base_url, GetSiteInstance(), content::Referrer(),
        ui::PAGE_TRANSITION_LINK, should_replace_current_entry, download_policy,
        "GET",
        /*post_body=*/nullptr, params->extra_headers,
        /*blob_url_loader_factory=*/nullptr,
        network::mojom::SourceLocation::New(), /*has_user_gesture=*/false,
        params->is_form_submission, params->impression,
        params->initiator_activation_and_ad_status, base::TimeTicks::Now(),
        /*is_embedder_initiated_fenced_frame_navigation=*/false,
        /*is_unfenced_top_navigation=*/true,
        /*force_new_browsing_instance=*/true);
    return;
  }

  TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL", "url",
               validated_url.possibly_invalid_spec());

  RenderFrameHostOwner* owner = owner_;
  // Inactive documents are not allowed to initiate navigations.
  // Also, see a similar check in RenderFrameHostImpl::BeginNavigation at
  // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.cc;l=7761-7769;drc=6dc39d60fea45c003424272efdb4c366119a9d7f
  if (!owner) {
    return;
  }
  owner->GetCurrentNavigator().RequestOpenURL(
      this, validated_url, base::OptionalToPtr(params->initiator_frame_token),
      GetProcess()->GetID(), params->initiator_origin,
      params->initiator_base_url, params->post_body, params->extra_headers,
      params->referrer.To<content::Referrer>(), params->disposition,
      params->should_replace_current_entry, params->user_gesture,
      params->triggering_event_info, params->href_translate,
      std::move(blob_url_loader_factory), params->impression);
}

void RenderFrameHostImpl::GetAssociatedInterface(
    const std::string& name,
    mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface>
        receiver) {
  // `associated_interface_provider_receiver_` and `associated_registry_` are
  // both reset at the same time, so we should never get a request for an
  // associated interface when `associated_registry_` is not valid.
  DCHECK(associated_registry_);

  // Perform Mojo capability control if `mojo_binder_policy_applier_` exists.
  if (mojo_binder_policy_applier_ &&
      !mojo_binder_policy_applier_->ApplyPolicyToAssociatedBinder(name)) {
    return;
  }

  mojo::ScopedInterfaceEndpointHandle handle = receiver.PassHandle();
  if (associated_registry_->TryBindInterface(name, &handle))
    return;
}

void RenderFrameHostImpl::DidStopLoading() {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::DidStopLoading",
              ChromeTrackEvent::kRenderFrameHost, this);
  if (did_stop_loading_callback_)
    std::move(did_stop_loading_callback_).Run();

  // This may be called for newly created frames when the frame is not loading
  // that navigate to about:blank, as well as history navigations during
  // BeforeUnload or Unload events.
  // TODO(fdegans): Change this to a DCHECK after LoadEventProgress has been
  // refactored in Blink. See crbug.com/466089
  if (!is_loading()) {
    return;
  }

  was_discarded_ = false;
  loading_state_ = LoadingState::NONE;

  // If we have a PeakGpuMemoryTrack, close it as loading as stopped. It will
  // asynchronously receive the statistics from the GPU process, and update
  // UMA stats.
  if (loading_mem_tracker_)
    loading_mem_tracker_.reset();

  // Only inform the FrameTreeNode of a change in load state if the load state
  // of this RenderFrameHost is being tracked.
  // This async Mojo method can be called from the renderer before entering
  // BFCache but the message can arrive here after it.
  if (!IsPendingDeletion() && !IsInBackForwardCache()) {
    DCHECK(owner_);  // See `owner_` invariants about IsPendingDeletion() and
                     // IsInBackForwardCache().
    owner_->DidStopLoading();
  }
}

void RenderFrameHostImpl::GetSavableResourceLinksCallback(
    blink::mojom::GetSavableResourceLinksReplyPtr reply) {
  if (!reply) {
    delegate_->SavableResourceLinksError(this);
    return;
  }

  delegate_->SavableResourceLinksResponse(this, reply->resources_list,
                                          std::move(reply->referrer),
                                          reply->subframes);
}

void RenderFrameHostImpl::DomOperationResponse(const std::string& json_string) {
  delegate_->DomOperationResponse(this, json_string);
}

std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
RenderFrameHostImpl::CreateCrossOriginPrefetchLoaderFactoryBundle() {
  // IPCs coming from the renderer talk on behalf of the last-committed
  // navigation.  This also applies to IPCs asking for a prefetch factory
  // bundle.
  auto subresource_loader_factories_config =
      SubresourceLoaderFactoriesConfig::ForLastCommittedNavigation(*this);
  network::mojom::URLLoaderFactoryParamsPtr factory_params =
      URLLoaderFactoryParamsHelper::CreateForPrefetch(
          this, subresource_loader_factories_config.GetClientSecurityState(),
          subresource_loader_factories_config.cookie_setting_overrides());

  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_default_factory;
  bool bypass_redirect_checks = false;
  // Passing an empty IsolationInfo ensures the factory is not initialized with
  // a IsolationInfo. This is necessary for a cross-origin prefetch factory
  // because the factory must use the value provided by requests going through
  // it.
  bypass_redirect_checks = CreateNetworkServiceDefaultFactoryAndObserve(
      std::move(factory_params),
      subresource_loader_factories_config.ukm_source_id(),
      pending_default_factory.InitWithNewPipeAndPassReceiver());

  return std::make_unique<blink::PendingURLLoaderFactoryBundle>(
      std::move(pending_default_factory),
      blink::PendingURLLoaderFactoryBundle::SchemeMap(),
      CreateURLLoaderFactoriesForIsolatedWorlds(
          subresource_loader_factories_config,
          isolated_worlds_requiring_separate_url_loader_factory_),
      bypass_redirect_checks);
}

base::WeakPtr<RenderFrameHostImpl> RenderFrameHostImpl::GetWeakPtr() {
  return weak_ptr_factory_.GetWeakPtr();
}

base::SafeRef<RenderFrameHostImpl> RenderFrameHostImpl::GetSafeRef() const {
  return weak_ptr_factory_.GetSafeRef();
}

#if BUILDFLAG(IS_OHOS)
void RenderFrameHostImpl::GetCreateNewWindow(
    const GURL& target_url,
    WindowOpenDisposition disposition,
    bool allow_popup,
    GetCreateNewWindowCallback callback) {
#if defined(OHOS_MULTI_WINDOW)
  bool effective_transient_activation_state =
      allow_popup || frame_tree_node_->HasTransientUserActivation();
  GetContentClient()->browser()->CanCreateWindow(
      this, target_url, disposition, effective_transient_activation_state,
      std::move(callback));
#endif  // defined(OHOS_MULTI_WINDOW)
}

void RenderFrameHostImpl::GenerateCodeCache(const std::string& url,
                                            const std::string& script,
                                            const std::shared_ptr<oh_code_cache::CacheOptions>& cacheOptions,
                                            CodeCacheCallback callback) {
  auto options = blink::mojom::CacheOptions::New();

  for (auto header : cacheOptions->response_headers_) {
    options->response_headers.insert(std::make_pair(header.first, header.second));
  }

  GetAssociatedLocalFrame()->GenerateCodeCache(url, script, std::move(options), std::move(callback));
}
#endif

void RenderFrameHostImpl::CreateNewWindow(
    mojom::CreateNewWindowParamsPtr params,
    CreateNewWindowCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::CreateNewWindow",
               "render_frame_host", this, "url", params->target_url);

  // Only top-most frames can open picture-in-picture windows.
  if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE &&
      GetParentOrOuterDocumentOrEmbedder()) {
    frame_host_associated_receiver_.ReportBadMessage(
        "Only top-most frames can open picture-in-picture windows.");
    return;
  }

  bool no_javascript_access = false;

  // Filter out URLs to which navigation is disallowed from this context.
  //
  // Note that currently, "javascript:" URLs and empty strings (both of which
  // are legal arguments to window.open) make it here; FilterURL rewrites them
  // to "about:blank" -- they shouldn't be cancelled.
  GetProcess()->FilterURL(false, &params->target_url);

  bool effective_transient_activation_state =
      params->allow_popup || HasTransientUserActivation() ||
      (transient_allow_popup_.IsActive() &&
       params->disposition == WindowOpenDisposition::NEW_POPUP);

  // Ignore window creation when sent from a frame that's not active or
  // created.
  bool can_create_window =
      IsActive() && is_render_frame_created() &&
      GetContentClient()->browser()->CanCreateWindow(
          this, GetLastCommittedURL(),
          GetOutermostMainFrame()->GetLastCommittedURL(),
          last_committed_origin_, params->window_container_type,
          params->target_url, params->referrer.To<Referrer>(),
          params->frame_name, params->disposition, *params->features,
          effective_transient_activation_state, params->opener_suppressed,
          &no_javascript_access);

  // If this frame isn't allowed to create a window, return early (before we
  // consume transient user activation).
  if (!can_create_window) {
    std::move(callback).Run(mojom::CreateNewWindowStatus::kBlocked, nullptr);
    return;
  }

  // Otherwise, consume user activation before we proceed. In particular, it is
  // important to do this before we return from the |opener_suppressed| case
  // below.
  // NB: This call will consume activations in the browser and the remote frame
  // proxies for this frame. The initiating renderer will consume its view of
  // the activations after we return.

  // See `owner_` invariants about `IsActive()`, which is implied by
  // `can_create_window`.
  CHECK(owner_);
  bool was_consumed = owner_->UpdateUserActivationState(
      blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
      blink::mojom::UserActivationNotificationType::kNone);

  // For Android WebView, we support a pop-up like behavior for window.open()
  // even if the embedding app doesn't support multiple windows. In this case,
  // window.open() will return "window" and navigate it to whatever URL was
  // passed.
  if (!GetOrCreateWebPreferences().supports_multiple_windows) {
    // See crbug.com/1083819, we should ignore if the URL is javascript: scheme,
    // previously we already filtered out javascript: scheme and replace the
    // URL with |kBlockedURL|, so we check against |kBlockedURL| here.
    if (params->target_url == GURL(kBlockedURL)) {
      std::move(callback).Run(mojom::CreateNewWindowStatus::kIgnore, nullptr);
    } else {
      std::move(callback).Run(mojom::CreateNewWindowStatus::kReuse, nullptr);
    }
    return;
  }

  // This will clone the sessionStorage for namespace_id_to_clone.
  StoragePartition* storage_partition = GetStoragePartition();
  DOMStorageContextWrapper* dom_storage_context =
      static_cast<DOMStorageContextWrapper*>(
          storage_partition->GetDOMStorageContext());

  scoped_refptr<SessionStorageNamespaceImpl> cloned_namespace;
  if (!params->clone_from_session_storage_namespace_id.empty()) {
    cloned_namespace = SessionStorageNamespaceImpl::CloneFrom(
        dom_storage_context, params->session_storage_namespace_id,
        params->clone_from_session_storage_namespace_id);
  } else {
    cloned_namespace = SessionStorageNamespaceImpl::Create(
        dom_storage_context, params->session_storage_namespace_id);
  }

  if (IsCredentialless() || IsNestedWithinFencedFrame() ||
      CoopSuppressOpener(/*opener=*/this)) {
    params->opener_suppressed = true;
    // TODO(https://crbug.com/1060691) This should be applied to all
    // popups opened with noopener.
    params->frame_name.clear();
  }

  RenderFrameHostImpl* top_level_opener = GetMainFrame();
  int popup_virtual_browsing_context_group =
      params->opener_suppressed
          ? CrossOriginOpenerPolicyAccessReportManager::
                NextVirtualBrowsingContextGroup()
          : top_level_opener->virtual_browsing_context_group();
  int popup_soap_by_default_virtual_browsing_context_group =
      params->opener_suppressed
          ? CrossOriginOpenerPolicyAccessReportManager::
                NextVirtualBrowsingContextGroup()
          : top_level_opener->soap_by_default_virtual_browsing_context_group();

  // If the opener is suppressed or script access is disallowed, we should
  // open the window in a new BrowsingInstance, and thus a new process. That
  // means the current renderer process will not be able to route messages to
  // it. Because of this, we will immediately show and navigate the window
  // in OnCreateNewWindowOnUI, using the params provided here.
  bool is_new_browsing_instance =
      params->opener_suppressed || no_javascript_access;

  DCHECK(IsRenderFrameLive());

  // The non-owning pointer |new_frame_tree| is valid in this stack frame since
  // nothing can delete it until this thread is freed up again.
  FrameTree* new_frame_tree =
      delegate_->CreateNewWindow(this, *params, is_new_browsing_instance,
                                 was_consumed, cloned_namespace.get());

  transient_allow_popup_.Deactivate();

  if (is_new_browsing_instance || !new_frame_tree) {
    // Opener suppressed, Javascript access disabled, or delegate did not
    // provide a handle to any windows it created. In these cases, never tell
    // the renderer about the new window.
    std::move(callback).Run(mojom::CreateNewWindowStatus::kIgnore, nullptr);
    return;
  }

  DCHECK(!params->opener_suppressed);

  RenderFrameHostImpl* new_main_rfh =
      new_frame_tree->root()->current_frame_host();

  new_main_rfh->virtual_browsing_context_group_ =
      popup_virtual_browsing_context_group;
  new_main_rfh->soap_by_default_virtual_browsing_context_group_ =
      popup_soap_by_default_virtual_browsing_context_group;

  // COOP and COOP reporter are inherited from the opener to the popup's initial
  // empty document.
  if (PopupInheritCOOP(/*opener=*/this) &&
      GetMainFrame()->coop_access_report_manager()->coop_reporter()) {
    new_main_rfh->SetCrossOriginOpenerPolicyReporter(
        std::make_unique<CrossOriginOpenerPolicyReporter>(
            GetProcess()->GetStoragePartition(), GetLastCommittedURL(),
            params->referrer->url, new_main_rfh->cross_origin_opener_policy(),
            GetReportingSource(), isolation_info_.network_anonymization_key()));
  }

  mojo::PendingAssociatedRemote<mojom::Frame> pending_frame_remote;
  mojo::PendingAssociatedReceiver<mojom::Frame> pending_frame_receiver =
      pending_frame_remote.InitWithNewEndpointAndPassReceiver();
  new_main_rfh->SetMojomFrameRemote(std::move(pending_frame_remote));

  mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
      browser_interface_broker;
  new_main_rfh->BindBrowserInterfaceBrokerReceiver(
      browser_interface_broker.InitWithNewPipeAndPassReceiver());

  mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
      pending_associated_interface_provider;
  new_main_rfh->BindAssociatedInterfaceProviderReceiver(
      pending_associated_interface_provider
          .InitWithNewEndpointAndPassReceiver());

  // With this path, RenderViewHostImpl::CreateRenderView is never called
  // because `blink::WebView` is already created on the renderer side. Thus we
  // need to establish the connection here.
  mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> page_broadcast;
  mojo::PendingAssociatedReceiver<blink::mojom::PageBroadcast>
      page_broadcast_receiver =
          page_broadcast.InitWithNewEndpointAndPassReceiver();

  auto widget_params =
      new_main_rfh->GetLocalRenderWidgetHost()
          ->BindAndGenerateCreateFrameWidgetParamsForNewWindow();

  new_main_rfh->render_view_host()->BindPageBroadcast(
      std::move(page_broadcast));

  bool wait_for_debugger =
      devtools_instrumentation::ShouldWaitForDebuggerInWindowOpen();
  mojom::CreateNewWindowReplyPtr reply = mojom::CreateNewWindowReply::New(
      new_main_rfh->GetFrameToken(), new_main_rfh->GetRoutingID(),
      std::move(pending_frame_receiver), std::move(widget_params),
      std::move(page_broadcast_receiver), std::move(browser_interface_broker),
      std::move(pending_associated_interface_provider), cloned_namespace->id(),
      new_main_rfh->GetDevToolsFrameToken(), wait_for_debugger,
      new_main_rfh->GetDocumentToken(),
      new_main_rfh->policy_container_host()->CreatePolicyContainerForBlink());

  std::move(callback).Run(mojom::CreateNewWindowStatus::kSuccess,
                          std::move(reply));

  // When `waiting_for_init_` is true, the browser waits for the renderer to
  // request to show the window (which becomes a call to Init() on the new
  // window's `new_main_rfh`) before servicing subresource requests. We ensure
  // this is the first message received by the remote frame (instead of plumbing
  // it with the CreateNewWindow IPC).
  if (new_main_rfh->waiting_for_init_)
    new_main_rfh->GetMojomFrameInRenderer()->BlockRequests();

  // The mojom reply callback with kSuccess causes the renderer to create the
  // renderer-side objects.
  new_main_rfh->render_view_host()->RenderViewCreated(new_main_rfh);
}

void RenderFrameHostImpl::SendPrivateAggregationRequestsForFencedFrameEvent(
    const std::string& event_type) {
  if (!base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi) ||
      !blink::features::kPrivateAggregationApiEnabledInFledge.Get() ||
      (!blink::features::kPrivateAggregationApiFledgeExtensionsEnabled.Get() &&
       !base::FeatureList::IsEnabled(
           blink::features::
               kPrivateAggregationApiFledgeExtensionsLocalTestingOverride))) {
    mojo::ReportBadMessage(
        "FLEDGE extensions must be enabled to use reportEvent() for private "
        "aggregation events.");
    return;
  }
  // We only care if the event type starts with "reserved." - We allow event
  // types like "myevent.reserved.name".
  if (base::StartsWith(event_type, blink::kFencedFrameReservedPAEventPrefix)) {
    mojo::ReportBadMessage("Reserved events cannot be triggered manually.");
    return;
  }
  // Get the reporting metadata associated with the fenced frame.
  const absl::optional<FencedFrameProperties>& fenced_frame_properties =
      frame_tree_node_->GetFencedFrameProperties();
  if (!fenced_frame_properties.has_value() ||
      !fenced_frame_properties->fenced_frame_reporter_) {
    // No associated fenced frame reporter. This should have been captured
    // in the renderer process at `Fence::reportEvent`.
    // This implies there is an inconsistency between the browser and the
    // renderer.
    mojo::ReportBadMessage(
        "This frame had reporting metadata registered in its renderer process"
        "but not in its browser process. The reporting metadata should be"
        "consistent between the two.");
    return;
  }
  if (!fenced_frame_properties->mapped_url_.has_value() ||
      !GetLastCommittedOrigin().IsSameOriginWith(
          url::Origin::Create(fenced_frame_properties->mapped_url_
                                  ->GetValueIgnoringVisibility()))) {
    mojo::ReportBadMessage(
        "This frame is cross-origin to the mapped url of its fenced frame "
        "config, so the renderer should not be able to call reportEvent.");
    return;
  }

  fenced_frame_properties->fenced_frame_reporter_
      ->SendPrivateAggregationRequestsForEvent(event_type);
}

void RenderFrameHostImpl::CreatePortal(
    mojo::PendingAssociatedReceiver<blink::mojom::Portal> pending_receiver,
    mojo::PendingAssociatedRemote<blink::mojom::PortalClient> client,
    blink::mojom::RemoteFrameInterfacesFromRendererPtr remote_frame_interfaces,
    CreatePortalCallback callback) {
  if (!Portal::IsEnabled()) {
    frame_host_associated_receiver_.ReportBadMessage(
        "blink.mojom.Portal can only be used if the Portals feature is "
        "enabled.");
    return;
  }

  // If a Portal is nested inside a prerendered page, its owning RFH is also
  // |kPrerendering|. In this case the creation of the Portal is deferred from
  // the renderer.
  if (GetLifecycleState() == RenderFrameHost::LifecycleState::kPrerendering) {
    frame_host_associated_receiver_.ReportBadMessage(
        "RenderFrameHostImpl::CreatePortal cannot be called when the RFH is "
        "being prerendered.");
    return;
  }

  // We don't support attaching a portal inside a nested browsing context or
  // inside a fenced frame.
  if (!is_main_frame() || IsFencedFrameRoot()) {
    frame_host_associated_receiver_.ReportBadMessage(
        "RFHI::CreatePortal called in a nested browsing context");
    return;
  }

  // TODO(crbug.com/1051639): We need to find a long term solution to when/how
  // portals should work in sandboxed documents.
  if (active_sandbox_flags() != network::mojom::WebSandboxFlags::kNone) {
    frame_host_associated_receiver_.ReportBadMessage(
        "RFHI::CreatePortal called in a sandboxed browsing context");
    return;
  }

  // Note that we don't check |GetLastCommittedOrigin|, since that is inherited
  // by the initial empty document of a new frame.
  // TODO(1008989): Once issue 1008989 is fixed we could move this check into
  // |Portal::Create|.
  if (!GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
    frame_host_associated_receiver_.ReportBadMessage(
        "Portal creation is restricted to the HTTP family.");
    return;
  }

  auto portal = std::make_unique<Portal>(this);
  portal->Bind(std::move(pending_receiver), std::move(client));
  auto it = portals_.insert(std::move(portal)).first;

  RenderFrameProxyHost* proxy_host =
      (*it)->CreateProxyAndAttachPortal(std::move(remote_frame_interfaces));

  if (!proxy_host) {
    frame_host_associated_receiver_.ReportBadMessage(
        "Trying to attach a portal that has already been attached.");
    return;
  }

  // Since the portal is newly created and has yet to commit a navigation, this
  // state is default-constructed.
  const blink::mojom::FrameReplicationState& initial_replicated_state =
      proxy_host->frame_tree_node()->current_replication_state();
  DCHECK(initial_replicated_state.origin.opaque());

  std::move(callback).Run(initial_replicated_state.Clone(),
                          (*it)->portal_token(), proxy_host->GetFrameToken(),
                          (*it)->GetDevToolsFrameToken());
}

void RenderFrameHostImpl::AdoptPortal(
    const blink::PortalToken& portal_token,
    blink::mojom::RemoteFrameInterfacesFromRendererPtr remote_frame_interfaces,
    AdoptPortalCallback callback) {
  Portal* portal = FindPortalByToken(portal_token);
  if (!portal) {
    frame_host_associated_receiver_.ReportBadMessage(
        "Unknown portal_token when adopting portal.");
    return;
  }
  DCHECK_EQ(portal->owner_render_frame_host(), this);
  RenderFrameProxyHost* proxy_host =
      portal->CreateProxyAndAttachPortal(std::move(remote_frame_interfaces));

  if (!proxy_host) {
    frame_host_associated_receiver_.ReportBadMessage(
        "Trying to attach a portal that has already been attached.");
    return;
  }

  // |frame_sink_id| should be set to the associated frame. See
  // https://crbug.com/966119 for details.
  viz::FrameSinkId frame_sink_id = proxy_host->frame_tree_node()
                                       ->render_manager()
                                       ->GetRenderWidgetHostView()
                                       ->GetFrameSinkId();
  proxy_host->GetAssociatedRemoteFrame()->SetFrameSinkId(frame_sink_id);

  std::move(callback).Run(
      proxy_host->frame_tree_node()->current_replication_state().Clone(),
      proxy_host->GetFrameToken(), portal->GetDevToolsFrameToken());
}

std::vector<FencedFrame*> RenderFrameHostImpl::GetFencedFrames() const {
  std::vector<FencedFrame*> result;
  for (const std::unique_ptr<FencedFrame>& fenced_frame : fenced_frames_)
    result.push_back(fenced_frame.get());
  return result;
}

void RenderFrameHostImpl::DestroyFencedFrame(FencedFrame& fenced_frame) {
  auto it = base::ranges::find_if(fenced_frames_,
                                  base::MatchesUniquePtr(&fenced_frame));
  CHECK(it != fenced_frames_.end());
  fenced_frames_.erase(it);
}

void RenderFrameHostImpl::CreateFencedFrame(
    mojo::PendingAssociatedReceiver<blink::mojom::FencedFrameOwnerHost>
        pending_receiver,
    blink::mojom::RemoteFrameInterfacesFromRendererPtr remote_frame_interfaces,
    const blink::RemoteFrameToken& frame_token,
    const base::UnguessableToken& devtools_frame_token) {
  // We should defer fenced frame creation during prerendering, so creation at
  // this point is an error.
  if (GetLifecycleState() == RenderFrameHost::LifecycleState::kPrerendering) {
    bad_message::ReceivedBadMessage(GetProcess(),
                                    bad_message::FF_CREATE_WHILE_PRERENDERING);
    return;
  }
  if (!blink::features::IsFencedFramesEnabled()) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_FENCED_FRAME_MOJO_WHEN_DISABLED);
    return;
  }
  // Cannot create a fenced frame in a sandbox iframe which doesn't allow
  // features that need to be allowed in the fenced frame.
  if (IsSandboxed(blink::kFencedFrameMandatoryUnsandboxedFlags)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFH_CREATE_FENCED_FRAME_IN_SANDBOXED_FRAME);
    return;
  }

  // Check that we have a unique `frame_token`.
  if (RenderFrameProxyHost::IsFrameTokenInUse(frame_token)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::RFHI_CREATE_FENCED_FRAME_BAD_FRAME_TOKEN);
    return;
  }

  // Ensure the devtools frame token doesn't exist in the FrameTree for
  // this tab.
  for (FrameTreeNode* node :
       GetOutermostMainFrame()->frame_tree()->NodesIncludingInnerTreeNodes()) {
    if (node->current_frame_host()->devtools_frame_token() ==
        devtools_frame_token) {
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFHI_CREATE_FENCED_FRAME_BAD_DEVTOOLS_FRAME_TOKEN);
      return;
    }
  }

  // Inactive pages cannot create fenced frames. If the page is in the BFCache,
  // it will be evicted.
  if (IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kCreateFencedFrame)) {
    return;
  }

  fenced_frames_.push_back(std::make_unique<FencedFrame>(
      weak_ptr_factory_.GetSafeRef(), was_discarded_));
  FencedFrame* fenced_frame = fenced_frames_.back().get();
  RenderFrameProxyHost* proxy_host =
      fenced_frame->InitInnerFrameTreeAndReturnProxyToOuterFrameTree(
          std::move(remote_frame_interfaces), frame_token,
          devtools_frame_token);
  fenced_frame->Bind(std::move(pending_receiver));

  // Since the fenced frame is newly created and has yet to commit a navigation,
  // this state is default-constructed.
  const blink::mojom::FrameReplicationState& initial_replicated_state =
      proxy_host->frame_tree_node()->current_replication_state();
  // Note that a default-constructed `FrameReplicationState` always has an
  // opaque origin, simply because the frame hasn't had any navigations yet.
  // Fenced frames (after their first navigation) do not have opaque origins,
  // and this default-constructed FRS does not impact that.
  DCHECK(initial_replicated_state.origin.opaque());
}

// TODO(crbug.com/1400992): Move SendFencedFrameReportingBeacon into a separate
// refcounted class.
void RenderFrameHostImpl::SendFencedFrameReportingBeacon(
    const std::string& event_data,
    const std::string& event_type,
    const std::vector<blink::FencedFrame::ReportingDestination>& destinations) {
  for (const blink::FencedFrame::ReportingDestination& destination :
       destinations) {
    SendFencedFrameReportingBeaconInternal(event_data, event_type, destination,
                                           /*from_renderer=*/true);
  }
}

void RenderFrameHostImpl::MaybeSendFencedFrameReportingBeacon(
    NavigationRequest& navigation_request) {
  // The fenced frame "reserved.top_navigation" automatic beacon only cares
  // about top-frame navigations.
  if (!IsOutermostMainFrame()) {
    return;
  }

  // Automatic beacons can only be sent if the initiating frame had transient
  // user activation when it navigated.
  if (navigation_request.GetNavigationInitiatorActivationAndAdStatus() ==
      blink::mojom::NavigationInitiatorActivationAndAdStatus::
          kDidNotStartWithTransientActivation) {
    return;
  }

  if (!navigation_request.GetInitiatorFrameToken().has_value()) {
    return;
  }

  // Treat the automatic beacon as if it's being sent by the document that
  // initiated the top-level navigation. (You can think of it like a
  // reportEvent call from that document.)
  RenderFrameHostImpl* initiator_rfh = RenderFrameHostImpl::FromFrameToken(
      navigation_request.GetInitiatorProcessID(),
      navigation_request.GetInitiatorFrameToken().value());
  if (!initiator_rfh) {
    return;
  }

  // Beacons can only be sent from inside a fenced frame/urn iframe tree, where
  // there is a fenced frame reporter.
  const absl::optional<FencedFrameProperties>& properties =
      initiator_rfh->frame_tree_node()->GetFencedFrameProperties();
  if (!properties.has_value() || !properties->fenced_frame_reporter_) {
    return;
  }

  // If there is no automatic beacon declared, there is nothing to send.
  absl::optional<AutomaticBeaconInfo> info =
      properties->automatic_beacon_info();
  if (!info) {
    return;
  }

  // Beacons can only be sent when the initiator frame is same-origin with the
  // fenced frame config's mapped url.
  if (!properties->mapped_url_.has_value() ||
      !initiator_rfh->GetLastCommittedOrigin().IsSameOriginWith(
          url::Origin::Create(
              properties->mapped_url_->GetValueIgnoringVisibility()))) {
    return;
  }

  for (blink::FencedFrame::ReportingDestination destination :
       info->destinations) {
    initiator_rfh->SendFencedFrameReportingBeaconInternal(
        info->data, blink::kFencedFrameTopNavigationBeaconType, destination,
        /*from_renderer=*/false, navigation_request.GetNavigationId());
  }
}

void RenderFrameHostImpl::SendFencedFrameReportingBeaconInternal(
    const std::string& event_data,
    const std::string& event_type,
    blink::FencedFrame::ReportingDestination destination,
    bool from_renderer,
    absl::optional<int64_t> navigation_id) {
  if (!IsActive()) {
    // reportEvent is not allowed when this RenderFrameHost or one of its
    // ancestors is not active.
    return;
  }

  // Get the reporting metadata associated with the fenced frame.
  const absl::optional<FencedFrameProperties>& fenced_frame_properties =
      frame_tree_node_->GetFencedFrameProperties();
  if (fenced_frame_properties.has_value() &&
      fenced_frame_properties->is_ad_component_) {
    if (from_renderer) {
      // Direct invocation of fence.reportEvent from an ad component is
      // disallowed.
      AddMessageToConsole(
          blink::mojom::ConsoleMessageLevel::kError,
          "This frame is an ad component. It is not allowed to call "
          "fence.reportEvent.");
      return;
    }
    // The only allowed event type from an ad component is
    // `reserved.top_navigation`.
    CHECK_EQ(event_type, blink::kFencedFrameTopNavigationBeaconType);
  }

  if (!fenced_frame_properties.has_value() ||
      !fenced_frame_properties->fenced_frame_reporter_) {
    // No associated fenced frame reporter. This should have been captured
    // in the renderer process at `Fence::reportEvent`.
    // This implies there is an inconsistency between the browser and the
    // renderer.
    mojo::ReportBadMessage(
        "This frame has no fenced frame reporter registered in the browser.");
    return;
  }

  if (!fenced_frame_properties->mapped_url_.has_value() ||
      !GetLastCommittedOrigin().IsSameOriginWith(
          url::Origin::Create(fenced_frame_properties->mapped_url_
                                  ->GetValueIgnoringVisibility()))) {
    mojo::ReportBadMessage(
        "This frame is cross-origin to the mapped url of its fenced frame "
        "config, so the renderer should not be able to call reportEvent.");
    return;
  }

  if (event_data.length() > blink::kFencedFrameMaxBeaconLength) {
    mojo::ReportBadMessage(
        "The data provided to SendFencedFrameReportingBeacon() exceeds the "
        "maximum length, which is 64KB.");
    return;
  }

  std::string error_message;
  if (!fenced_frame_properties->fenced_frame_reporter_->SendReport(
          event_type, event_data, destination,
          /*request_initiator_frame=*/this, error_message, navigation_id)) {
    AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kError,
                        error_message);
  }
}

void RenderFrameHostImpl::SetFencedFrameAutomaticBeaconReportEventData(
    const std::string& event_data,
    const std::vector<blink::FencedFrame::ReportingDestination>& destinations) {
  if (event_data.length() > blink::kFencedFrameMaxBeaconLength) {
    mojo::ReportBadMessage(
        "The data provided to SetFencedFrameAutomaticBeaconReportEventData() "
        "exceeds the maximum length, which is 64KB.");
    return;
  }

  // The call is ignored if the RenderFrameHost is not the currently active one
  // in the FrameTreeNode. For instance, this is ignored when it is pending
  // deletion or if it entered the BackForwardCache.
  //
  // Note: The renderer process already tests the document is not detached from
  // the frame tree before sending the IPC, but this might race with frame
  // deletion IPC sent from other processes.
  if (!IsActive()) {
    return;
  }
  CHECK(owner_);  // See `owner_` invariants about `IsActive()`.

  owner_->SetFencedFrameAutomaticBeaconReportEventData(event_data,
                                                       destinations);
}

void RenderFrameHostImpl::OnViewTransitionOptInChanged(
    blink::mojom::ViewTransitionSameOriginOptIn view_transition_opt_in) {
  ViewTransitionOptInState::GetOrCreateForCurrentDocument(this)
      ->set_same_origin_opt_in(view_transition_opt_in);
}

void RenderFrameHostImpl::CreateNewPopupWidget(
    mojo::PendingAssociatedReceiver<blink::mojom::PopupWidgetHost>
        blink_popup_widget_host,
    mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
    mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget) {
  // Do not open popups in an inactive document.
  if (!IsActive()) {
    // Sending this message requires user activation, which is impossible
    // for a prerendering document, so the renderer process should be
    // terminated. See
    // https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation.
    if (lifecycle_state() == LifecycleStateImpl::kPrerendering) {
      bad_message::ReceivedBadMessage(
          GetProcess(), bad_message::RFH_POPUP_REQUEST_WHILE_PRERENDERING);
    }
    return;
  }

  // We still need to allocate a widget routing id. Even though the renderer
  // doesn't receive it, the browser side still uses routing ids to track
  // widgets in various global tables.
  int32_t widget_route_id = GetProcess()->GetNextRoutingID();
  RenderWidgetHostImpl* widget = delegate_->CreateNewPopupWidget(
      site_instance_->group()->GetSafeRef(), widget_route_id,
      std::move(blink_popup_widget_host), std::move(blink_widget_host),
      std::move(blink_widget));
  if (!widget)
    return;
  // The renderer-owned widget was created before sending the IPC received here.
  widget->RendererWidgetCreated(/*for_frame_widget=*/false);

  if (create_new_popup_widget_callback_)
    create_new_popup_widget_callback_.Run(widget);
}

void RenderFrameHostImpl::GetKeepAliveHandleFactory(
    mojo::PendingReceiver<blink::mojom::KeepAliveHandleFactory> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (GetProcess()->AreRefCountsDisabled())
    return;

  keep_alive_handle_factory_.Bind(std::move(receiver));
}

void RenderFrameHostImpl::DidChangeSrcDoc(
    const blink::FrameToken& child_frame_token,
    const std::string& srcdoc_value) {
  auto* child =
      FindAndVerifyChild(child_frame_token, bad_message::RFH_OWNER_PROPERTY);
  if (!child)
    return;

  child->SetSrcdocValue(srcdoc_value);
}

void RenderFrameHostImpl::ReceivedDelegatedCapability(
    blink::mojom::DelegatedCapability delegated_capability) {
  // Every delegated capability that is checked or consumed on the browser side
  // needs to be (a) activated here and (b) consumed when RFH handles the
  // capability.
  if (delegated_capability ==
      blink::mojom::DelegatedCapability::kFullscreenRequest) {
    fullscreen_request_token_.Activate();
  }
}

void RenderFrameHostImpl::BeginNavigation(
    blink::mojom::CommonNavigationParamsPtr unvalidated_common_params,
    blink::mojom::BeginNavigationParamsPtr begin_params,
    mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token,
    mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
    mojo::PendingRemote<blink::mojom::PolicyContainerHostKeepAliveHandle>
        initiator_policy_container_host_keep_alive_handle,
    mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
        renderer_cancellation_listener) {
  TRACE_EVENT("navigation", "RenderFrameHostImpl::BeginNavigation",
              ChromeTrackEvent::kRenderFrameHost, this, "url",
              unvalidated_common_params->url.possibly_invalid_spec());

  // Only active and prerendered documents are allowed to start navigation in
  // their frame.
  if (lifecycle_state() != LifecycleStateImpl::kPrerendering) {
    // If this is reached in case the RenderFrameHost is in BackForwardCache
    // evict the document from BackForwardCache.
    if (IsInactiveAndDisallowActivation(
            DisallowActivationReasonId::kBeginNavigation)) {
      return;
    }
  }

  // See `owner_` invariants about `lifecycle_state_`.
  // `IsInactiveAndDisallowActivation()` check cause both pending deletion and
  // bfcached states to return early.
  DCHECK(owner_);
  if (owner_->GetRenderFrameHostManager().is_attaching_inner_delegate()) {
    // Avoid starting any new navigations since this frame is in the process of
    // attaching an inner delegate.
    return;
  }

  DCHECK(navigation_client.is_valid());

  blink::mojom::CommonNavigationParamsPtr validated_common_params =
      unvalidated_common_params.Clone();
  if (!VerifyBeginNavigationCommonParams(*this, &*validated_common_params)) {
    return;
  }

  // BeginNavigation() should only be triggered when the navigation is
  // initiated by a frame in the same process.
  int initiator_process_id = GetProcess()->GetID();
  if (!VerifyNavigationInitiator(this, begin_params->initiator_frame_token,
                                 initiator_process_id)) {
    return;
  }

  // Container-initiated navigations must come from the same process as the
  // parent.
  if (begin_params->is_container_initiated) {
    if (!GetParent() ||
        (initiator_process_id != GetParent()->GetProcess()->GetID())) {
      mojo::ReportBadMessage(
          "container initiated navigation from non-parent process");
      return;
    }
  }

  // If the request is bearing Private State Tokens parameters:
  // - it must not be a main-frame navigation, and
  // - for redemption and signing operations, the frame's parent needs the
  //   trust-token-redemption Permissions Policy feature,
  // - for issue operation, the frame's parent needs the
  //   private-state-token-issuance Permission Policy.
  if (begin_params->trust_token_params) {
    // For Fenced Frame trust_token_params shouldn't be populated since that is
    // driven by the iframe specific attribute as defined here:
    // https://github.com/WICG/trust-token-api#extension-iframe-activation". If
    // this changes, the code below using `GetParent()` will need to be changed.
    // TODO(crbug.com/1254770): For portals implemented with mparch, we'll have
    // a similar problem with `GetParent()` calls as with Fenced Frame.
    if (IsFencedFrameRoot()) {
      mojo::ReportBadMessage(
          "RFHI: Private State Token params in fenced frame nav");
      return;
    }
    if (!GetParent()) {
      mojo::ReportBadMessage(
          "RFHI: Private State Token params in main frame nav");
      return;
    }
    RenderFrameHostImpl* parent = GetParent();
    bool is_right_operation_policy_enabled = false;
    const network::mojom::TrustTokenOperationType& operation =
        begin_params->trust_token_params->operation;
    switch (operation) {
      case network::mojom::TrustTokenOperationType::kRedemption:
      case network::mojom::TrustTokenOperationType::kSigning:
        is_right_operation_policy_enabled = parent->IsFeatureEnabled(
            blink::mojom::PermissionsPolicyFeature::kTrustTokenRedemption);
        break;
      case network::mojom::TrustTokenOperationType::kIssuance:
        is_right_operation_policy_enabled = parent->IsFeatureEnabled(
            blink::mojom::PermissionsPolicyFeature::kPrivateStateTokenIssuance);
        break;
    }

    if (!is_right_operation_policy_enabled) {
      mojo::ReportBadMessage(
          "RFHI: Mandatory Private State Tokens Permissions Policy feature "
          "is absent");
      return;
    }
  }

  // Only uuid-in-package: URL are allowed for navigation to a resource in
  // Subresource WebBundles.
  if (begin_params->web_bundle_token &&
      !validated_common_params->url.SchemeIs(url::kUuidInPackageScheme)) {
    bad_message::ReceivedBadMessage(
        GetProcess(), bad_message::WEB_BUNDLE_INVALID_NAVIGATION_URL);
    return;
  }

  if (begin_params->is_fullscreen_requested) {
    // Fullscreen requests on navigation are only allowed from initial empty
    // documents that are the outermost main frame. The initiator must also have
    // window management granted, otherwise the fullscreen bit is dropped.
    if (!is_initial_empty_document()) {
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFHI_FULLSCREEN_NAV_INVALID_INITIAL_DOCUMENT);
      return;
    }
    if (!IsOutermostMainFrame()) {
      bad_message::ReceivedBadMessage(
          GetProcess(),
          bad_message::RFHI_FULLSCREEN_NAV_NOT_OUTERMOST_MAIN_FRAME);
      return;
    }
    RenderFrameHostImpl* initiator_render_frame_host =
        begin_params->initiator_frame_token
            ? RenderFrameHostImpl::FromFrameToken(
                  initiator_process_id,
                  begin_params->initiator_frame_token.value())
            : nullptr;
    if (!initiator_render_frame_host ||
        !IsWindowManagementGranted(initiator_render_frame_host)) {
      begin_params->is_fullscreen_requested = false;
    }
  }

  GetProcess()->FilterURL(true, &begin_params->searchable_form_url);

  // If the request was for a blob URL, but the validated URL is no longer a
  // blob URL, reset the blob_url_token to prevent hitting the ReportBadMessage
  // below, and to make sure we don't incorrectly try to use the blob_url_token.
  if (unvalidated_common_params->url.SchemeIsBlob() &&
      !validated_common_params->url.SchemeIsBlob()) {
    blob_url_token = mojo::NullRemote();
  }

  if (blob_url_token && !validated_common_params->url.SchemeIsBlob()) {
    mojo::ReportBadMessage("Blob URL Token, but not a blob: URL");
    return;
  }
  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  if (blob_url_token) {
    blob_url_loader_factory =
        ChromeBlobStorageContext::URLLoaderFactoryForToken(
            GetStoragePartition(), std::move(blob_url_token));
  }

  // If the request was for a blob URL, but no blob_url_token was passed in this
  // is not necessarily an error. Renderer initiated reloads for example are one
  // situation where a renderer currently doesn't have an easy way of resolving
  // the blob URL. For those situations resolve the blob URL here, as we don't
  // care about ordering with other blob URL manipulation anyway.
  if (validated_common_params->url.SchemeIsBlob() && !blob_url_loader_factory) {
    blob_url_loader_factory = ChromeBlobStorageContext::URLLoaderFactoryForUrl(
        GetStoragePartition(), validated_common_params->url);
  }

  if (waiting_for_init_) {
    pending_navigate_ = std::make_unique<PendingNavigation>(
        std::move(validated_common_params), std::move(begin_params),
        std::move(blob_url_loader_factory), std::move(navigation_client),
        std::move(renderer_cancellation_listener));
    return;
  }

  // We can discard the parameter
  // |initiator_policy_container_host_keep_alive_handle|. This is just needed to
  // ensure that the PolicyContainerHost of the initiator RenderFrameHost
  // can still be retrieved even if the RenderFrameHost has been deleted in
  // between. Since the NavigationRequest will be created synchronously as a
  // result of this function's execution, we don't need to pass
  // |initiator_policy_container_host_keep_alive_handle| along.

  owner_->GetCurrentNavigator().OnBeginNavigation(
      frame_tree_node(), std::move(validated_common_params),
      std::move(begin_params), std::move(blob_url_loader_factory),
      std::move(navigation_client), EnsurePrefetchedSignedExchangeCache(),
      std::move(renderer_cancellation_listener));
}

void RenderFrameHostImpl::SubresourceResponseStarted(
    const url::SchemeHostPort& final_response_url,
    net::CertStatus cert_status) {
  OPTIONAL_TRACE_EVENT1("content",
                        "RenderFrameHostImpl::SubresourceResponseStarted",
                        "url", final_response_url.GetURL());
  bool was_processed =
      frame_tree_->controller().ssl_manager()->DidStartResourceResponse(
          final_response_url, cert_status);
  UMA_HISTOGRAM_ENUMERATION("SSL.Experimental.SubresourceResponse",
                            was_processed
                                ? SSLSubresourceResponseType::kProcessed
                                : SSLSubresourceResponseType::kIgnored);
}
void RenderFrameHostImpl::ResourceLoadComplete(
    blink::mojom::ResourceLoadInfoPtr resource_load_info) {
  GlobalRequestID global_request_id;
  const bool is_frame_request =
      blink::IsRequestDestinationFrame(resource_load_info->request_destination);
  if (main_frame_request_ids_.first == resource_load_info->request_id) {
    global_request_id = main_frame_request_ids_.second;
  } else if (is_frame_request) {
    // The load complete message for the main resource arrived before
    // |DidCommitProvisionalLoad()|. We save the load info so
    // |ResourceLoadComplete()| can be called later in
    // |DidCommitProvisionalLoad()| when we can map to the global request ID.
    deferred_main_frame_load_info_ = std::move(resource_load_info);
    return;
  }
  delegate_->ResourceLoadComplete(this, global_request_id,
                                  std::move(resource_load_info));
}

void RenderFrameHostImpl::HandleAXEvents(
    const ui::AXTreeID& tree_id,
    blink::mojom::AXUpdatesAndEventsPtr updates_and_events,
    int32_t reset_token) {
  TRACE_EVENT0("accessibility", "RenderFrameHostImpl::HandleAXEvents");
  SCOPED_UMA_HISTOGRAM_TIMER("Accessibility.Performance.HandleAXEvents");

  if (tree_id != GetAXTreeID()) {
    // The message has arrived after the frame has navigated which means its
    // events are no longer relevant and can be discarded.
    if (!accessibility_testing_callback_.is_null()) {
      // The callback must still run, otherwise dump event tests can hang.
      accessibility_testing_callback_.Run(this, ax::mojom::Event::kNone, 0);
    }
    return;
  }

  // Don't process this IPC if either we're waiting on a reset and this IPC
  // doesn't have the matching token ID, or if we're not waiting on a reset but
  // this message includes a reset token.
  if (accessibility_reset_token_ != reset_token) {
    return;
  }
  accessibility_reset_token_ = 0;

  ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();

  if (accessibility_mode.is_mode_off()) {
    return;
  }
  if (base::FeatureList::IsEnabled(features::kEvictOnAXEvents)) {
    // If the flag is on, evict the bfcache entry now that AX events are
    // received.
    if (IsInactiveAndDisallowActivationForAXEvents(updates_and_events->events))
      return;
  } else {
    // If the page is in back/forward cache, do not return early and continue to
    // apply AX tree updates.
    // TODO(https://crbug.com/1328126): Define and implement the behavior for
    // when the page is prerendering, too.
    if (!IsInBackForwardCache() &&
        IsInactiveAndDisallowActivation(DisallowActivationReasonId::kAXEvent)) {
      return;
    }
  }

  GetOrCreateBrowserAccessibilityManager();

  AXEventNotificationDetails details;
  details.ax_tree_id = tree_id;

  // TODO(1213848): Remove the false path when the experiment is complete.
  if (base::FeatureList::IsEnabled(kRenderAccessibilityHostAvoidCopying)) {
    details.events = std::move(updates_and_events->events);

    details.updates = std::move(updates_and_events->updates);
    for (auto& update : details.updates) {
      if (update.has_tree_data) {
        DCHECK_EQ(tree_id, update.tree_data.tree_id);
        ax_tree_data_ = update.tree_data;
        update.tree_data = GetAXTreeData();
      }
    }
  } else {
    details.events = updates_and_events->events;

    details.updates.resize(updates_and_events->updates.size());
    for (size_t i = 0; i < updates_and_events->updates.size(); ++i) {
      details.updates[i] = updates_and_events->updates[i];
      if (updates_and_events->updates[i].has_tree_data) {
        DCHECK_EQ(tree_id, updates_and_events->updates[i].tree_data.tree_id);
        ax_tree_data_ = updates_and_events->updates[i].tree_data;
        details.updates[i].tree_data = GetAXTreeData();
      }
    }
  }

  if (needs_ax_root_id_) {
    // This is the first update after the tree id changed. AXTree must be sent
    // a new root id, otherwise crashes are likely to result.
    DCHECK(!details.updates.empty());
    DCHECK_NE(ui::kInvalidAXNodeID, details.updates[0].root_id);
    needs_ax_root_id_ = false;
  }

  SendAccessibilityEventsToManager(details);

  delegate_->AccessibilityEventReceived(details);

  // For testing only.
  if (!accessibility_testing_callback_.is_null()) {
    if (details.events.empty()) {
      // Objects were marked dirty but no events were provided.
      // The callback must still run, otherwise dump event tests can hang.
      accessibility_testing_callback_.Run(this, ax::mojom::Event::kNone, 0);
    } else {
      // Call testing callback functions for each event to fire.
      for (auto& event : details.events) {
        if (static_cast<int>(event.event_type) < 0)
          continue;

        accessibility_testing_callback_.Run(this, event.event_type, event.id);
      }
    }
  }
}

void RenderFrameHostImpl::HandleAXLocationChanges(
    const ui::AXTreeID& tree_id,
    std::vector<blink::mojom::LocationChangesPtr> changes) {
  if (tree_id != GetAXTreeID()) {
    // The message has arrived after the frame has navigated which means its
    // changes are no longer relevant and can be discarded.
    return;
  }

  if (accessibility_reset_token_ ||
      IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kAXLocationChange))
    return;

  BrowserAccessibilityManager* manager =
      GetOrCreateBrowserAccessibilityManager();
  if (manager)
    manager->OnLocationChanges(changes);

  // Send the updates to the automation extension API.
  std::vector<AXLocationChangeNotificationDetails> details;
  details.reserve(changes.size());
  for (auto& change : changes) {
    AXLocationChangeNotificationDetails detail;
    detail.id = change->id;
    detail.ax_tree_id = GetAXTreeID();
    detail.new_location = change->new_location;
    details.push_back(detail);
  }
  delegate_->AccessibilityLocationChangesReceived(details);
}

media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
RenderFrameHostImpl::GetRecordAggregateWatchTimeCallback() {
  // The URL used for UKM must always be the top level frame.
  return delegate_->GetRecordAggregateWatchTimeCallback(
      GetOutermostMainFrame()->GetLastCommittedURL());
}

void RenderFrameHostImpl::ResetWaitingState() {
  // We don't allow resetting waiting state when the RenderFrameHost is either
  // in BackForwardCache or in pending deletion state, as we don't allow
  // navigations from either of these two states.
  DCHECK(!IsInBackForwardCache());
  DCHECK(!IsPendingDeletion());

  // Whenever we reset the RFH state, we should not be waiting for beforeunload
  // or close acks.  We clear them here to be safe, since they can cause
  // navigations to be ignored in DidCommitProvisionalLoad.
  if (is_waiting_for_beforeunload_completion_) {
    is_waiting_for_beforeunload_completion_ = false;
    if (beforeunload_timeout_)
      beforeunload_timeout_->Stop();
    has_shown_beforeunload_dialog_ = false;
    beforeunload_pending_replies_.clear();
  }
  send_before_unload_start_time_ = base::TimeTicks();
  page_close_state_ = PageCloseState::kNotClosing;
}

CanCommitStatus RenderFrameHostImpl::CanCommitOriginAndUrl(
    const url::Origin& origin,
    const GURL& url,
    bool is_same_document_navigation,
    bool is_pdf,
    bool is_sandboxed) {
  // Note that callers are responsible for avoiding this function in modes that
  // can bypass these rules, such as --disable-web-security or certain Android
  // WebView features like universal access from file URLs.

  // Renderer-debug URLs can never be committed.
  if (blink::IsRendererDebugURL(url)) {
    LogCanCommitOriginAndUrlFailureReason("is_renderer_debug_url");
    return CanCommitStatus::CANNOT_COMMIT_URL;
  }

  // Verify that if this RenderFrameHost is for a WebUI it is not committing a
  // URL which is not allowed in a WebUI process. As we are at the commit stage,
  // set OriginIsolationRequest to kNone (this is implicitly done by the
  // UrlInfoInit constructor).
  if (!Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
          this, UrlInfo(UrlInfoInit(url).WithOrigin(origin).WithIsPdf(is_pdf)),
          /*is_renderer_initiated_check=*/true)) {
    return CanCommitStatus::CANNOT_COMMIT_URL;
  }

  // MHTML subframes can supply URLs at commit time that do not match the
  // process lock. For example, it can be either "cid:..." or arbitrary URL at
  // which the frame was at the time of generating the MHTML
  // (e.g. "http://localhost"). In such cases, don't verify the URL, but require
  // the URL to commit in the process of the main frame.
  if (!is_main_frame()) {
    RenderFrameHostImpl* main_frame = GetMainFrame();
    if (main_frame->is_mhtml_document()) {
      if (IsSameSiteInstance(main_frame))
        return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;

      // If an MHTML subframe commits in a different process (even one that
      // appears correct for the subframe's URL), then we aren't correctly
      // loading it from the archive and should kill the renderer.
      static auto* const oopif_in_mhtml_page_key =
          base::debug::AllocateCrashKeyString(
              "oopif_in_mhtml_page", base::debug::CrashKeySize::Size32);
      base::debug::SetCrashKeyString(
          oopif_in_mhtml_page_key,
          is_mhtml_document() ? "is_mhtml_doc" : "not_mhtml_doc");
      LogCanCommitOriginAndUrlFailureReason("oopif_in_mhtml_page");
      return CanCommitStatus::CANNOT_COMMIT_URL;
    }
  }

  // Same-document navigations cannot change origins, as long as these checks
  // aren't being bypassed in unusual modes. This check must be after the MHTML
  // check, as shown by NavigationMhtmlBrowserTest.IframeAboutBlankNotFound.
  if (is_same_document_navigation && origin != GetLastCommittedOrigin()) {
    LogCanCommitOriginAndUrlFailureReason("cross_origin_same_document");
    return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
  }

  // Give the client a chance to disallow URLs from committing.
  if (!GetContentClient()->browser()->CanCommitURL(GetProcess(), url)) {
    LogCanCommitOriginAndUrlFailureReason(
        "content_client_disallowed_commit_url");
    return CanCommitStatus::CANNOT_COMMIT_URL;
  }

  // Check with ChildProcessSecurityPolicy, which enforces Site Isolation, etc.
  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
  const CanCommitStatus can_commit_status = policy->CanCommitOriginAndUrl(
      GetProcess()->GetID(), GetSiteInstance()->GetIsolationContext(),
      UrlInfo(
          UrlInfoInit(url)
              .WithOrigin(origin)
              .WithStoragePartitionConfig(
                  GetSiteInstance()->GetSiteInfo().storage_partition_config())
              .WithWebExposedIsolationInfo(
                  GetSiteInstance()->GetWebExposedIsolationInfo())
              .WithSandbox(is_sandboxed)));
  if (can_commit_status != CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL) {
    LogCanCommitOriginAndUrlFailureReason("cpspi_disallowed_commit");
    return can_commit_status;
  }

  const auto origin_tuple_or_precursor_tuple =
      origin.GetTupleOrPrecursorTupleIfOpaque();
  if (origin_tuple_or_precursor_tuple.IsValid()) {
    // Verify that the origin/precursor is allowed to commit in this process.
    // Note: This also handles non-standard cases for |url|, such as
    // about:blank, data, and blob URLs.

    // Renderer-debug URLs can never be committed.
    if (blink::IsRendererDebugURL(origin_tuple_or_precursor_tuple.GetURL())) {
      LogCanCommitOriginAndUrlFailureReason(
          "origin_or_precursor_is_renderer_debug_url");
      return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
    }

    // Give the client a chance to disallow origin URLs from committing.
    // TODO(acolwell): Fix this code to work with opaque origins. Currently
    // some opaque origin precursors, like chrome-extension schemes, can trigger
    // the commit to fail. These need to be investigated.
    if (!origin.opaque() && !GetContentClient()->browser()->CanCommitURL(
                                GetProcess(), origin.GetURL())) {
      LogCanCommitOriginAndUrlFailureReason(
          "content_client_disallowed_commit_origin");
      return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
    }
  }

  return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
}

bool RenderFrameHostImpl::CanSubframeCommitOriginAndUrl(
    NavigationRequest* navigation_request) {
  DCHECK(navigation_request);
  DCHECK(!is_main_frame());

  const int nav_entry_id = navigation_request->nav_entry_id();
  // No validation of the main frame's origin is needed if the subframe
  // navigation doesn't target a different existing NavigationEntry. In such
  // cases, it's unlikely the wrong main frame origin could be displayed
  // afterward.
  if (nav_entry_id == 0)
    return true;

  const int last_nav_entry_index = navigation_request->frame_tree_node()
                                       ->navigator()
                                       .controller()
                                       .GetLastCommittedEntryIndex();
  const int dest_nav_entry_index = navigation_request->frame_tree_node()
                                       ->navigator()
                                       .controller()
                                       .GetEntryIndexWithUniqueID(nav_entry_id);
  if (dest_nav_entry_index <= 0 || dest_nav_entry_index == last_nav_entry_index)
    return true;

  NavigationEntryImpl* dest_nav_entry =
      navigation_request->frame_tree_node()
          ->navigator()
          .controller()
          .GetEntryAtIndex(dest_nav_entry_index);
  auto dest_main_frame_fne = dest_nav_entry->root_node()->frame_entry;

  // A subframe navigation should never lead to a NavigationEntry that looks
  // like a cross-document navigation in the main frame, since cross-document
  // navigations destroy all subframes.
  int64_t dest_main_frame_dsn = dest_main_frame_fne->document_sequence_number();
  int64_t actual_main_frame_dsn = navigation_request->frame_tree_node()
                                      ->navigator()
                                      .controller()
                                      .GetLastCommittedEntry()
                                      ->root_node()
                                      ->frame_entry->document_sequence_number();
  if (dest_main_frame_dsn != actual_main_frame_dsn)
    return false;

  // At this point in request handling RenderFrameHostImpl may not have enough
  // information to derive an accurate value for what would be the new main
  // frame origin if this request gets committed. One case this can happen is
  // a NavigtionEntry from a restored session. To make the best of this
  // situation, skip validation of main frame origin and assume it will be valid
  // by assigning a known good value - i.e. the current main frame origin - then
  // continue so at least CanCommitOriginAndUrl can perform its checks using
  // dest_top_url.
  url::Origin dest_top_origin =
      dest_main_frame_fne->committed_origin().value_or(
          GetMainFrame()->GetLastCommittedOrigin());

  const GURL& dest_top_url = dest_nav_entry->GetURL();

  // Assume the change in main frame FrameNavigationEntry won't affect whether
  // the main frame is showing a PDF or a sandboxed document, since we don't
  // track that in FrameNavigationEntry.
  const bool is_top_pdf =
      GetMainFrame()->GetSiteInstance()->GetSiteInfo().is_pdf();
  const bool is_top_sandboxed =
      GetMainFrame()->GetSiteInstance()->GetSiteInfo().is_sandboxed();

  return GetMainFrame()->CanCommitOriginAndUrl(
             dest_top_origin, dest_top_url,
             true /* is_same_document_navigation */, is_top_pdf,
             is_top_sandboxed) == CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
}

void RenderFrameHostImpl::Stop() {
  TRACE_EVENT1("navigation", "RenderFrameHostImpl::Stop", "render_frame_host",
               this);
  // Don't call GetAssociatedLocalFrame before the RenderFrame is created.
  if (!IsRenderFrameLive())
    return;
  GetAssociatedLocalFrame()->StopLoading();
}

void RenderFrameHostImpl::DispatchBeforeUnload(BeforeUnloadType type,
                                               bool is_reload) {
  bool for_navigation =
      type == BeforeUnloadType::BROWSER_INITIATED_NAVIGATION ||
      type == BeforeUnloadType::RENDERER_INITIATED_NAVIGATION;
  bool for_inner_delegate_attach =
      type == BeforeUnloadType::INNER_DELEGATE_ATTACH;
  DCHECK(for_navigation || for_inner_delegate_attach || !is_reload);

  // TAB_CLOSE and DISCARD should only dispatch beforeunload on outermost main
  // frames.
  DCHECK(type == BeforeUnloadType::BROWSER_INITIATED_NAVIGATION ||
         type == BeforeUnloadType::RENDERER_INITIATED_NAVIGATION ||
         type == BeforeUnloadType::INNER_DELEGATE_ATTACH ||
         IsOutermostMainFrame());

  CHECK(owner_);  // Only active documents are subject to BeforeUnload.

  if (!for_navigation) {
    // Cancel any pending navigations, to avoid their navigation commit/fail
    // event from wiping out the is_waiting_for_beforeunload_completion_ state.
    owner_->CancelNavigation();
  }

  // In renderer-initiated navigations, don't check for beforeunload in the
  // navigating frame, as it has already run beforeunload before it sent the
  // BeginNavigation IPC.
  bool check_subframes_only =
      type == BeforeUnloadType::RENDERER_INITIATED_NAVIGATION;
  if (!ShouldDispatchBeforeUnload(check_subframes_only)) {
    // When running beforeunload for navigations, ShouldDispatchBeforeUnload()
    // is checked earlier and we would only get here if it had already returned
    // true.
    DCHECK(!for_navigation);

    // Dispatch the ACK to prevent re-entrancy.
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&RenderFrameHostManager::BeforeUnloadCompleted,
                       owner_->GetRenderFrameHostManager().GetWeakPtr(),
                       /*proceed=*/true));
    return;
  }
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
      "navigation", "RenderFrameHostImpl BeforeUnload", TRACE_ID_LOCAL(this),
      "render_frame_host", this);

  // This may be called more than once (if the user clicks the tab close button
  // several times, or if they click the tab close button then the browser close
  // button), and we only send the message once.
  if (is_waiting_for_beforeunload_completion_) {
    // Some of our close messages could be for the tab, others for cross-site
    // transitions. We always want to think it's for closing the tab if any
    // of the messages were, since otherwise it might be impossible to close
    // (if there was a cross-site "close" request pending when the user clicked
    // the close button). We want to keep the "for cross site" flag only if
    // both the old and the new ones are also for cross site.
    unload_ack_is_for_navigation_ =
        unload_ack_is_for_navigation_ && for_navigation;
  } else {
    // Start the hang monitor in case the renderer hangs in the beforeunload
    // handler.
    is_waiting_for_beforeunload_completion_ = true;
    beforeunload_dialog_request_cancels_unload_ = false;
    unload_ack_is_for_navigation_ = for_navigation;
    send_before_unload_start_time_ = base::TimeTicks::Now();
    if (delegate_->IsJavaScriptDialogShowing()) {
      // If there is a JavaScript dialog up, don't bother sending the renderer
      // the unload event because it is known unresponsive, waiting for the
      // reply from the dialog. If this incoming request is for a DISCARD be
      // sure to reply with |proceed = false|, because the presence of a dialog
      // indicates that the page can't be discarded.
      SimulateBeforeUnloadCompleted(type != BeforeUnloadType::DISCARD);
    } else {
      // Start a timer that will be shared by all frames that need to run
      // beforeunload in the current frame's subtree.
      if (beforeunload_timeout_)
        beforeunload_timeout_->Start(beforeunload_timeout_delay_);

      beforeunload_pending_replies_.clear();
      beforeunload_dialog_request_cancels_unload_ =
          (type == BeforeUnloadType::DISCARD);

      // Run beforeunload in this frame and its cross-process descendant
      // frames, in parallel.
      CheckOrDispatchBeforeUnloadForSubtree(
          check_subframes_only,
          /*send_ipc=*/true, is_reload,
          /*no_dispatch_because_avoid_unnecessary_sync=*/nullptr);
    }
  }
}

bool RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForSubtree(
    bool subframes_only,
    bool send_ipc,
    bool is_reload,
    bool* no_dispatch_because_avoid_unnecessary_sync) {
  // Beforeunload is not supported inside fenced frame trees.
  if (IsFencedFrameRoot())
    return false;

  bool found_beforeunload = false;
  bool run_beforeunload_for_legacy = false;

  ForEachRenderFrameHostWithAction(
      [this, subframes_only, send_ipc, is_reload, &found_beforeunload,
       &run_beforeunload_for_legacy](
          RenderFrameHostImpl* rfh) -> FrameIterationAction {
        return CheckOrDispatchBeforeUnloadForFrame(
            subframes_only, send_ipc, is_reload, &found_beforeunload,
            &run_beforeunload_for_legacy, rfh);
      });

  if (run_beforeunload_for_legacy) {
    DCHECK(send_ipc);
    beforeunload_pending_replies_.insert(this);
    SendBeforeUnload(is_reload, GetWeakPtr(), /*for_legacy=*/true);
  } else if (no_dispatch_because_avoid_unnecessary_sync &&
             !found_beforeunload && !subframes_only && IsRenderFrameLive() &&
             IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled()) {
    *no_dispatch_because_avoid_unnecessary_sync = true;
  }

  return found_beforeunload;
}

RenderFrameHost::FrameIterationAction
RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForFrame(
    bool subframes_only,
    bool send_ipc,
    bool is_reload,
    bool* found_beforeunload,
    bool* run_beforeunload_for_legacy,
    RenderFrameHostImpl* rfh) {
  // Don't traverse into inner pages.
  if (&GetPage() != &rfh->GetPage())
    return FrameIterationAction::kSkipChildren;

  // If |subframes_only| is true, skip this frame and its same-site
  // descendants.  This happens for renderer-initiated navigations, where
  // these frames have already run beforeunload.
  if (subframes_only && rfh->GetSiteInstance() == GetSiteInstance())
    return FrameIterationAction::kContinue;

  // No need to run beforeunload if the RenderFrame isn't live.
  if (!rfh->IsRenderFrameLive())
    return FrameIterationAction::kContinue;

  // Only run beforeunload in frames that have registered a beforeunload
  // handler. See description of SendBeforeUnload() for details on simulating
  // beforeunload for legacy reasons. If
  // `kAvoidUnnecessaryBeforeUnloadCheckSync` is true and there is no
  // beforeunload handler for the navigating frame, then do not simulate a
  // beforeunload handler, and navigation can continue.
  const bool run_beforeunload_for_legacy_frame =
      rfh == this && !rfh->has_before_unload_handler_ &&
      !IsAvoidUnnecessaryBeforeUnloadCheckSyncEnabled();
  const bool should_run_beforeunload =
      rfh->has_before_unload_handler_ || run_beforeunload_for_legacy_frame;

  if (!should_run_beforeunload)
    return FrameIterationAction::kContinue;

  // If we're only checking whether there's at least one frame with
  // beforeunload, then we've just found one, so we can return now.
  *found_beforeunload = true;
  if (!send_ipc)
    return FrameIterationAction::kStop;

  // Otherwise, figure out whether we need to send the IPC, or whether this
  // beforeunload was already triggered by an ancestor frame's IPC.

  // Only send beforeunload to local roots, and let Blink handle any
  // same-site frames under them. That is, if a frame has a beforeunload
  // handler, ask its local root to run it. If we've already sent the message
  // to that local root, skip this frame. For example, in A1(A2,A3), if A2
  // and A3 contain beforeunload handlers, and all three frames are
  // same-site, we ask A1 to run beforeunload for all three frames, and only
  // ask it once.
  while (!rfh->is_local_root() && rfh != this)
    rfh = rfh->GetParent();
  if (base::Contains(beforeunload_pending_replies_, rfh))
    return FrameIterationAction::kContinue;

  // For a case like A(B(A)), it's not necessary to send an IPC for the
  // innermost frame, as Blink will walk all same-site (local)
  // descendants. Detect cases like this and skip them.
  bool has_same_site_ancestor = false;
  for (auto* added_rfh : beforeunload_pending_replies_) {
    if (rfh->IsDescendantOfWithinFrameTree(added_rfh) &&
        rfh->GetSiteInstance() == added_rfh->GetSiteInstance()) {
      has_same_site_ancestor = true;
      break;
    }
  }
  if (has_same_site_ancestor)
    return FrameIterationAction::kContinue;

  if (run_beforeunload_for_legacy_frame &&
      IsAvoidUnnecessaryBeforeUnloadCheckPostTaskEnabled()) {
    // Wait to schedule until all frames have been processed. The legacy
    // beforeunload is not needed if another frame has a beforeunload
    // handler. Note that for `kAvoidUnnecessaryBeforeUnloadCheckSync`
    // `run_beforeunload_for_legacy_frame` is never true.
    *run_beforeunload_for_legacy = true;
    return FrameIterationAction::kContinue;
  }

  *run_beforeunload_for_legacy = false;

  // Add |rfh| to the list of frames that need to receive beforeunload
  // ACKs.
  beforeunload_pending_replies_.insert(rfh);

  SendBeforeUnload(is_reload, rfh->GetWeakPtr(), /*for_legacy=*/false);
  return FrameIterationAction::kContinue;
}

void RenderFrameHostImpl::SimulateBeforeUnloadCompleted(bool proceed) {
  DCHECK(is_waiting_for_beforeunload_completion_);
  base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;

  // Dispatch the ACK to prevent re-entrancy.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE,
      base::BindOnce(&RenderFrameHostImpl::ProcessBeforeUnloadCompleted,
                     weak_ptr_factory_.GetWeakPtr(), proceed,
                     /*treat_as_final_completion_callback=*/true,
                     approx_renderer_start_time, base::TimeTicks::Now(),
                     /*for_legacy=*/false));
}

bool RenderFrameHostImpl::ShouldDispatchBeforeUnload(
    bool check_subframes_only,
    bool* no_dispatch_because_avoid_unnecessary_sync) {
  return CheckOrDispatchBeforeUnloadForSubtree(
      check_subframes_only, /*send_ipc=*/false, /*is_reload=*/false,
      no_dispatch_because_avoid_unnecessary_sync);
}

void RenderFrameHostImpl::SetBeforeUnloadTimeoutDelayForTesting(
    const base::TimeDelta& timeout) {
  beforeunload_timeout_delay_ = timeout;
}

void RenderFrameHostImpl::StartPendingDeletionOnSubtree(
    RenderFrameHostImpl::PendingDeletionReason pending_deletion_reason) {
  DCHECK(IsPendingDeletion());

  if (pending_deletion_reason == PendingDeletionReason::kFrameDetach ||
      !ShouldAvoidRedundantNavigationCancellations()) {
    // Reset all navigations happening in the FrameTreeNode only when entering
    // "pending deletion" state due to frame detach if the
    // kStopCancellingNavigationsOnCommitAndNewNavigation flag is enabled, or
    // for all pending deletion cases otherwise.
    NavigationDiscardReason reason = NavigationDiscardReason::kWillRemoveFrame;
    GetFrameTreeNodeForUnload()->CancelNavigation();
    GetFrameTreeNodeForUnload()
        ->GetRenderFrameHostManager()
        .DiscardSpeculativeRFH(reason);
    ResetOwnedNavigationRequests(reason);
  } else {
    CHECK(pending_deletion_reason == PendingDeletionReason::kSwappedOut ||
          ShouldAvoidRedundantNavigationCancellations());
    // The pending deletion state is caused by swapping out the RFH. Reset only
    // the navigations that are owned by or will be using the swapped out RFH,
    // and also reset all navigations happening in the descendant frames.
    ResetNavigationsUsingSwappedOutRFH();
  }

  // For the child frames, we should delete all ongoing navigations
  // unconditionally, because the child frames will be deleted when this RFH
  // gets unloaded. Note that we are going through the RFH's children instead of
  // the FrameTreeNode's children, as the FTN might already have children that
  // isn't shared with the detached/swapped out RFH, e.g. in case of prerender
  // activation.
  for (auto& subframe : children_) {
    subframe->ResetAllNavigationsForFrameDetach();
  }

  for (std::unique_ptr<FrameTreeNode>& child_frame : children_) {
    for (FrameTreeNode* node : frame_tree()->SubtreeNodes(child_frame.get())) {
      RenderFrameHostImpl* child = node->current_frame_host();
      if (child->IsPendingDeletion())
        continue;

      // Blink handles deletion of all same-process descendants, running their
      // unload handler if necessary. So delegate sending IPC on the topmost
      // ancestor using the same process.
      RenderFrameHostImpl* local_ancestor = child;
      for (auto* rfh = child->parent_.get(); rfh != parent_;
           rfh = rfh->parent_) {
        if (rfh->GetSiteInstance() == child->GetSiteInstance())
          local_ancestor = rfh;
      }

      local_ancestor->DeleteRenderFrame(
          mojom::FrameDeleteIntention::kNotMainFrame);
      if (local_ancestor != child) {
        // In case of BackForwardCache, page is evicted directly from the cache
        // and deleted immediately, without waiting for unload handlers.
        child->SetLifecycleState(
            child->ShouldWaitForUnloadHandlers()
                ? LifecycleStateImpl::kRunningUnloadHandlers
                : LifecycleStateImpl::kReadyToBeDeleted);
      }

      node->frame_tree().FrameUnloading(node);
    }
  }
}

void RenderFrameHostImpl::PendingDeletionCheckCompleted() {
  if (lifecycle_state() == LifecycleStateImpl::kReadyToBeDeleted &&
      children_.empty()) {
    check_deletion_for_bug_1276535_ = false;
    if (is_waiting_for_unload_ack_) {
      OnUnloaded();  // Delete |this|.
      // Do not add code after this.
    } else {
      parent_->RemoveChild(GetFrameTreeNodeForUnload());
    }
  }
}

void RenderFrameHostImpl::PendingDeletionCheckCompletedOnSubtree() {
  if (children_.empty()) {
    PendingDeletionCheckCompleted();  // This might delete |this|.
    // DO NOT add code after this.
    return;
  }

  base::WeakPtr<RenderFrameHostImpl> self = GetWeakPtr();
  check_deletion_for_bug_1276535_ = true;

  // Collect children first before calling PendingDeletionCheckCompleted(). It
  // can delete them and invalidate |children_|. When the last child has been
  // deleted, it might also delete |this|.
  //
  // https://crbug.com/1276535: We collect WeakPtr, because we suspect deleting
  // a child might delete the whole WebContent and everything it contained.
  std::vector<base::WeakPtr<RenderFrameHostImpl>> weak_children;
  for (std::unique_ptr<FrameTreeNode>& child : children_) {
    RenderFrameHostImpl* child_rfh = child->current_frame_host();
    CHECK(child_rfh);
    weak_children.push_back(child_rfh->GetWeakPtr());
  }

  // DO NOT use |this| after this line.

  for (base::WeakPtr<RenderFrameHostImpl>& child_rfh : weak_children) {
    if (child_rfh) {
      child_rfh->PendingDeletionCheckCompletedOnSubtree();
    }
  }

  if (self) {
    check_deletion_for_bug_1276535_ = false;
  }
}

void RenderFrameHostImpl::ResetNavigationsUsingSwappedOutRFH() {
  // Only delete the navigation owned by the swapped out RFH or those that
  // intend to use the swapped out RFH.
  ResetOwnedNavigationRequests(
      NavigationDiscardReason::kRenderFrameHostDestruction);
  const NavigationRequest* navigation_request =
      frame_tree_node_->navigation_request();
  if (navigation_request &&
      navigation_request->state() >= NavigationRequest::WILL_PROCESS_RESPONSE &&
      navigation_request->GetRenderFrameHost() == this) {
    // It's possible for a RenderFrameHost to already have been picked for a
    // navigation but the NavigationRequest's ownership hasn't been moved to the
    // RenderFrameHost yet, if the navigation is deferred by a
    // NavigationThrottle or CommitDeferringCondition. We need to reset the
    // NavigationRequest to prevent it from trying to commit in the pending
    // deletion RFH.
    frame_tree_node_->ResetNavigationRequest(
        NavigationDiscardReason::kRenderFrameHostDestruction);
  }
}

void RenderFrameHostImpl::OnUnloadTimeout() {
  DCHECK(IsPendingDeletion());
  parent_->RemoveChild(GetFrameTreeNodeForUnload());
}

void RenderFrameHostImpl::SetFocusedFrame() {
  GetAssociatedLocalFrame()->Focus();
}

void RenderFrameHostImpl::AdvanceFocus(blink::mojom::FocusType type,
                                       RenderFrameProxyHost* source_proxy) {
  DCHECK(!source_proxy ||
         (source_proxy->GetProcess()->GetID() == GetProcess()->GetID()));

  absl::optional<blink::RemoteFrameToken> frame_token;
  if (source_proxy)
    frame_token = source_proxy->GetFrameToken();

  GetAssociatedLocalFrame()->AdvanceFocusInFrame(type, frame_token);
}

void RenderFrameHostImpl::JavaScriptDialogClosed(
    JavaScriptDialogCallback dialog_closed_callback,
    bool success,
    const std::u16string& user_input) {
  GetProcess()->SetBlocked(false);
  std::move(dialog_closed_callback).Run(success, user_input);
  // If executing as part of beforeunload event handling, there may have been
  // timers stopped in this frame or a frame up in the frame hierarchy. Restart
  // any timers that were stopped in OnRunBeforeUnloadConfirm().
  for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
    if (frame->is_waiting_for_beforeunload_completion_ &&
        frame->beforeunload_timeout_) {
      frame->beforeunload_timeout_->Start(beforeunload_timeout_delay_);
    }
  }
}

bool RenderFrameHostImpl::ShouldDispatchPagehideAndVisibilitychangeDuringCommit(
    RenderFrameHostImpl* old_frame_host,
    const UrlInfo& dest_url_info) {
  // Only return true if this is a same-site navigation and we did a proactive
  // BrowsingInstance swap but we're reusing the old page's renderer process.
  DCHECK(old_frame_host);
  if (old_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
          GetSiteInstance())) {
    return false;
  }
  if (old_frame_host->GetProcess() != GetProcess()) {
    return false;
  }
  if (!old_frame_host->IsNavigationSameSite(dest_url_info)) {
    return false;
  }
  DCHECK(is_main_frame());
  DCHECK_NE(old_frame_host, this);
  DCHECK_NE(old_frame_host->GetSiteInstance(), GetSiteInstance());
  return true;
}

void RenderFrameHostImpl::SendAllPendingBeaconsOnNavigation() {
  if (auto* pending_beacon_host =
          PendingBeaconHost::GetForCurrentDocument(this)) {
    pending_beacon_host->SendAllOnNavigation();
  }
  // TODO(crbug.com/1293679): Address FencedFrame.
  for (auto& child : children_) {
    child->current_frame_host()->SendAllPendingBeaconsOnNavigation();
  }
}

bool RenderFrameHostImpl::is_initial_empty_document() const {
  return frame_tree_node_->is_on_initial_empty_document();
}

uint32_t RenderFrameHostImpl::FindSuddenTerminationHandlers(bool same_origin) {
  uint32_t navigation_termination = 0;
  // Search this frame and subframes for sudden termination disablers
  ForEachRenderFrameHostWithAction([this, same_origin, &navigation_termination](
                                       RenderFrameHost* rfh) {
    if (same_origin &&
        GetLastCommittedOrigin() != rfh->GetLastCommittedOrigin()) {
      return FrameIterationAction::kSkipChildren;
    }
    if (rfh->GetSuddenTerminationDisablerState(
            blink::mojom::SuddenTerminationDisablerType::kUnloadHandler)) {
      navigation_termination = navigation_termination |
                               NavigationSuddenTerminationDisablerType::kUnload;
      // We can stop when we find the first unload handler. If we ever start
      // reporting other types of sudden termination handler, we will need to
      // continue.
      return FrameIterationAction::kStop;
    }
    return FrameIterationAction::kContinue;
  });
  return navigation_termination;
}

void RenderFrameHostImpl::RecordNavigationSuddenTerminationHandlers() {
  uint32_t navigation_termination =
      is_main_frame() ? NavigationSuddenTerminationDisablerType::kMainFrame : 0;

  if (is_initial_empty_document()) {
    navigation_termination |=
        NavigationSuddenTerminationDisablerType::kInitialEmptyDocument;
  }

  if (!GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
    navigation_termination |= NavigationSuddenTerminationDisablerType::kNotHttp;
  }

  base::UmaHistogramExactLinear(
      "Navigation.SuddenTerminationDisabler.AllOrigins",
      navigation_termination |
          FindSuddenTerminationHandlers(/*same_origin=*/false),
      NavigationSuddenTerminationDisablerType::kMaxValue * 2);
  base::UmaHistogramExactLinear(
      "Navigation.SuddenTerminationDisabler.SameOrigin",
      navigation_termination |
          FindSuddenTerminationHandlers(/*same_origin=*/true),
      NavigationSuddenTerminationDisablerType::kMaxValue * 2);
}

void RenderFrameHostImpl::CommitNavigation(
    NavigationRequest* navigation_request,
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    network::mojom::URLResponseHeadPtr response_head,
    mojo::ScopedDataPipeConsumerHandle response_body,
    network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
    absl::optional<SubresourceLoaderParams> subresource_loader_params,
    absl::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
        subresource_overrides,
    blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
    const absl::optional<blink::DocumentToken>& document_token,
    const base::UnguessableToken& devtools_navigation_token
#if BUILDFLAG(IS_OHOS_PRPP)
    ,
    uint64_t addr_web_handle
#endif
    ) {
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::CommitNavigation",
               "navigation_request", navigation_request, "url",
               common_params->url);
  DCHECK(!blink::IsRendererDebugURL(common_params->url));
  DCHECK(navigation_request);
  DCHECK_EQ(this, navigation_request->GetRenderFrameHost());
  AssertBrowserContextShutdownHasntStarted();

  bool is_same_document =
      NavigationTypeUtils::IsSameDocument(common_params->navigation_type);
  bool is_mhtml_subframe = navigation_request->IsForMhtmlSubframe();

  // A |response| and a |url_loader_client_endpoints| must always be provided,
  // except for edge cases, where another way to load the document exist.
  DCHECK((response_head && url_loader_client_endpoints) ||
         common_params->url.SchemeIs(url::kDataScheme) || is_same_document ||
         !IsURLHandledByNetworkStack(common_params->url) || is_mhtml_subframe);

  // All children of MHTML documents must be MHTML documents.
  // As a defensive measure, crash the browser if something went wrong.
  if (!is_main_frame()) {
    RenderFrameHostImpl* root = GetMainFrame();
    if (root->is_mhtml_document_) {
      bool loaded_from_outside_the_archive =
          response_head || url_loader_client_endpoints;
      CHECK(!loaded_from_outside_the_archive ||
            common_params->url.SchemeIs(url::kDataScheme));
      CHECK(navigation_request->IsForMhtmlSubframe());
      CHECK_EQ(GetSiteInstance(), root->GetSiteInstance());
      CHECK_EQ(GetProcess(), root->GetProcess());
    } else {
      DCHECK(!navigation_request->IsForMhtmlSubframe());
    }
  }

  bool is_srcdoc = common_params->url.IsAboutSrcdoc();
  if (is_srcdoc) {
    // TODO(wjmaclean): initialize this in NavigationRequest's constructor
    // instead.
    commit_params->srcdoc_value =
        navigation_request->frame_tree_node()->srcdoc_value();
    // Main frame srcdoc navigation are meaningless. They are blocked whenever a
    // navigation attempt is made. It shouldn't reach CommitNavigation.
    CHECK(!is_main_frame());

    // For a srcdoc iframe, we expect it to either be in its parent's
    // SiteInstance (either AreIsolatedSandboxedIframesEnabled is false or
    // both parent and child are sandboxed), or that the two are in different
    // SiteInstances when only the child is sandboxed.
    CHECK(GetSiteInstance() == parent_->GetSiteInstance() ||
          !parent_->GetSiteInstance()->GetSiteInfo().is_sandboxed() &&
              GetSiteInstance()->GetSiteInfo().is_sandboxed());
  }

  // TODO(https://crbug.com/888079): Compute the Origin to commit here.

  // If this is an attempt to commit a URL in an incompatible process, capture a
  // crash dump to diagnose why it is occurring.
  // TODO(creis): Remove this check after we've gathered enough information to
  // debug issues with browser-side security checks. https://crbug.com/931895.
  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
  const ProcessLock process_lock =
      ProcessLock::FromSiteInfo(GetSiteInstance()->GetSiteInfo());
  if (!process_lock.is_error_page() && common_params->url.IsStandard() &&
      !is_mhtml_subframe &&
      // TODO(https://crbug.com/888079): Replace `common_params().url` with
      // the origin to commit calculated on the browser side.
      !policy->CanAccessDataForOrigin(
          GetProcess()->GetID(), url::Origin::Create(common_params->url))) {
    SCOPED_CRASH_KEY_STRING64("CommitNavigation", "lock_url",
                              process_lock.ToString());
    SCOPED_CRASH_KEY_STRING64(
        "CommitNavigation", "commit_origin",
        common_params->url.DeprecatedGetOriginAsURL().spec());
    SCOPED_CRASH_KEY_BOOL("CommitNavigation", "is_main_frame", is_main_frame());
    // The reason this isn't is_outermost_main_frame is so that the full name of
    // the key does not exceed the 40 character limit.
    SCOPED_CRASH_KEY_BOOL("CommitNavigation", "is_outermost_frame",
                          IsOutermostMainFrame());
    NOTREACHED() << "Commiting in incompatible process for URL: "
                 << process_lock.lock_url() << " lock vs "
                 << common_params->url.DeprecatedGetOriginAsURL();
    base::debug::DumpWithoutCrashing();
  }

  const bool is_first_navigation = !has_committed_any_navigation_;
  has_committed_any_navigation_ = true;

  if (!is_same_document) {
    // Reset |subresource_web_bundle_navigation_info_| to the new one
    // if this is NOT for same-document navigation. For same-document
    // navigation, |navigation_request| doesn't have bundle information so
    // existing one should be kept around.
    subresource_web_bundle_navigation_info_ =
        navigation_request->GetSubresourceWebBundleNavigationInfo();
  }

  UpdatePermissionsForNavigation(navigation_request);

  // Get back to a clean state, in case we start a new navigation without
  // completing an unload handler.
  ResetWaitingState();

  // The renderer can exit view source mode when any error or cancellation
  // happen. When reusing the same renderer, overwrite to recover the mode.
  if (commit_params->is_view_source && IsActive()) {
    DCHECK(!GetParentOrOuterDocument());
    GetAssociatedLocalFrame()->EnableViewSourceMode();
  }

  // TODO(lfg): The renderer is not able to handle a null response, so the
  // browser provides an empty response instead. See the DCHECK in the beginning
  // of this method for the edge cases where a response isn't provided.
  network::mojom::URLResponseHeadPtr head =
      response_head ? std::move(response_head)
                    : network::mojom::URLResponseHead::New();

  std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
      subresource_loader_factories;
  mojo::PendingRemote<blink::mojom::ResourceCache> resource_cache_remote;
  if (!is_same_document || is_first_navigation) {
    recreate_default_url_loader_factory_after_network_service_crash_ = false;
    subresource_loader_factories =
        std::make_unique<blink::PendingURLLoaderFactoryBundle>();
    BrowserContext* browser_context = GetSiteInstance()->GetBrowserContext();
    auto subresource_loader_factories_config =
        SubresourceLoaderFactoriesConfig::ForPendingNavigation(
            *navigation_request);

    // Calculate `effective_scheme` - this will be the main input for deciding
    // whether the new document should have access to special URLLoaderFactories
    // like FileURLLoaderFactory, ContentURLLoaderFactory,
    // WebUIURLLoaderFactory, etc.  We look at GetTupleOrPrecursorTupleIfOpaque
    // to make sure the old behavior of sandboxed frames is preserved - see also
    // the FileURLLoaderFactory...SubresourcesInSandboxedFileFrame test.
    const std::string& effective_scheme =
        subresource_loader_factories_config.origin()
            .GetTupleOrPrecursorTupleIfOpaque()
            .scheme();

    ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;

    // Set up the default factory.
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        pending_default_factory;

    // See if this is for WebUI.
    const auto& webui_schemes = URLDataManagerBackend::GetWebUISchemes();
    if (base::Contains(webui_schemes, effective_scheme)) {
      mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_for_webui;
      auto factory_receiver =
          factory_for_webui.InitWithNewPipeAndPassReceiver();
      GetContentClient()->browser()->WillCreateURLLoaderFactory(
          browser_context, this, GetProcess()->GetID(),
          ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource,
          subresource_loader_factories_config.origin(),
          /*navigation_id=*/absl::nullopt,
          subresource_loader_factories_config.ukm_source_id(),
          &factory_receiver, /*header_client=*/nullptr,
          /*bypass_redirect_checks=*/nullptr,
          /*disable_secure_dns=*/nullptr, /*factory_override=*/nullptr);
      mojo::Remote<network::mojom::URLLoaderFactory> direct_factory_for_webui(
          CreateWebUIURLLoaderFactory(this, effective_scheme, {}));
      direct_factory_for_webui->Clone(std::move(factory_receiver));

      // If the renderer has webui bindings, then don't give it access to
      // network loader for security reasons.
      // http://crbug.com/829412: make an exception for a small whitelist
      // of WebUIs that need to be fixed to not make network requests in JS.
      if ((enabled_bindings_ & kWebUIBindingsPolicyMask) &&
          !GetContentClient()->browser()->IsWebUIAllowedToMakeNetworkRequests(
              subresource_loader_factories_config.origin())) {
        pending_default_factory = std::move(factory_for_webui);
        // WebUIURLLoaderFactory will kill the renderer if it sees a request
        // with a non-chrome scheme. Register a URLLoaderFactory for the about
        // scheme so about:blank doesn't kill the renderer.
        non_network_factories[url::kAboutScheme] =
            AboutURLLoaderFactory::Create();
      } else {
        // This is a webui scheme that doesn't have webui bindings. Give it
        // access to the network loader as it might require it.
        subresource_loader_factories->pending_scheme_specific_factories()
            .emplace(effective_scheme, std::move(factory_for_webui));
      }
    }

    if (navigation_request->IsMhtmlOrSubframe()) {
      // MHTML frames are not allowed to make any network requests - all their
      // subresource requests should be fulfilled from the MHTML archive.
      pending_default_factory =
          network::NotImplementedURLLoaderFactory::Create();
    }

    if (!pending_default_factory) {
      // Otherwise default to a Network Service-backed loader from the
      // appropriate NetworkContext.
      recreate_default_url_loader_factory_after_network_service_crash_ = true;

      // Otherwise default to a Network Service-backed loader from the
      // appropriate NetworkContext.
      bool bypass_redirect_checks =
          CreateNetworkServiceDefaultFactoryAndObserve(
              CreateURLLoaderFactoryParamsForMainWorld(
                  subresource_loader_factories_config,
                  "RFHI::CommitNavigation"),
              subresource_loader_factories_config.ukm_source_id(),
              pending_default_factory.InitWithNewPipeAndPassReceiver());
      subresource_loader_factories->set_bypass_redirect_checks(
          bypass_redirect_checks);
    }

    DCHECK(pending_default_factory);
    subresource_loader_factories->pending_default_factory() =
        std::move(pending_default_factory);

    // Only documents from a file precursor scheme can load file subresoruces.
    //
    // For loading Web Bundle files, we don't set FileURLLoaderFactory.
    // Because loading local files from a Web Bundle file is prohibited.
    if (effective_scheme == url::kFileScheme) {
      // USER_BLOCKING because this scenario is exactly one of the examples
      // given by the doc comment for USER_BLOCKING: Loading and rendering a web
      // page after the user clicks a link.
      base::TaskPriority file_factory_priority =
          base::TaskPriority::USER_BLOCKING;
      non_network_factories.emplace(
          url::kFileScheme,
          FileURLLoaderFactory::Create(
              browser_context->GetPath(),
              browser_context->GetSharedCorsOriginAccessList(),
              file_factory_priority));
    }

#if BUILDFLAG(IS_ANDROID)
    if (effective_scheme == url::kContentScheme) {
      // Only content:// URLs can load content:// subresources
      non_network_factories.emplace(url::kContentScheme,
                                    ContentURLLoaderFactory::Create());
    }
#endif

#ifdef OHOS_HAP_DECOMPRESSED
    if (effective_scheme == url::kResourcesScheme) {
      base::TaskPriority file_factory_priority =
          base::TaskPriority::USER_BLOCKING;
      non_network_factories.emplace(
          url::kResourcesScheme,
          FileURLLoaderFactory::Create(
              browser_context->GetPath(),
              browser_context->GetSharedCorsOriginAccessList(),
              file_factory_priority));
    }
#endif

    auto* partition = GetStoragePartition();
    non_network_factories.emplace(
        url::kFileSystemScheme,
        CreateFileSystemURLLoaderFactory(
            GetProcess()->GetID(), GetFrameTreeNodeId(),
            partition->GetFileSystemContext(), partition->GetPartitionDomain(),
            commit_params->storage_key));

    non_network_factories.emplace(url::kDataScheme,
                                  DataURLLoaderFactory::Create());

    GetContentClient()
        ->browser()
        ->RegisterNonNetworkSubresourceURLLoaderFactories(
            GetProcess()->GetID(), routing_id_,
            subresource_loader_factories_config.origin(),
            &non_network_factories);

    for (auto& factory : non_network_factories) {
      const std::string& scheme = factory.first;
      mojo::PendingRemote<network::mojom::URLLoaderFactory>
          original_pending_factory = std::move(factory.second);
      // TODO(https://crbug.com/1376879): Remove the workaround below once the
      // root cause of the bug has been fixed.
      if (!original_pending_factory)
        continue;

      mojo::PendingRemote<network::mojom::URLLoaderFactory>
          pending_factory_proxy;
      mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver =
          pending_factory_proxy.InitWithNewPipeAndPassReceiver();
      WillCreateURLLoaderFactory(
          subresource_loader_factories_config.origin(), &factory_receiver,
          subresource_loader_factories_config.ukm_source_id());

      mojo::Remote<network::mojom::URLLoaderFactory> remote(
          std::move(original_pending_factory));
      remote->Clone(std::move(factory_receiver));
      subresource_loader_factories->pending_scheme_specific_factories().emplace(
          scheme, std::move(pending_factory_proxy));
    }

    subresource_loader_factories->pending_isolated_world_factories() =
        CreateURLLoaderFactoriesForIsolatedWorlds(
            subresource_loader_factories_config,
            isolated_worlds_requiring_separate_url_loader_factory_);

    ResourceCacheManager* resource_cache_manager =
        partition->GetResourceCacheManager();
    if (resource_cache_manager) {
      resource_cache_manager
          ->MaybeInitializeResourceCacheRemoteOnCommitNavigation(
              resource_cache_remote, *navigation_request);
    }
  }

  // It is imperative that cross-document navigations always provide a set of
  // subresource ULFs.
  DCHECK(is_same_document || !is_first_navigation || is_srcdoc ||
         subresource_loader_factories);

  if (is_same_document) {
    DCHECK_EQ(navigation_request->frame_tree_node()->current_frame_host(),
              this);
    const base::UnguessableToken& navigation_token =
        commit_params->navigation_token;
    DCHECK(GetSameDocumentNavigationRequest(navigation_token));
    bool should_replace_current_entry =
        common_params->should_replace_current_entry;
    GetMojomFrameInRenderer()->CommitSameDocumentNavigation(
        std::move(common_params), std::move(commit_params),
        base::BindOnce(&RenderFrameHostImpl::OnSameDocumentCommitProcessed,
                       base::Unretained(this), navigation_token,
                       should_replace_current_entry));
  } else {
    // Pass the controller service worker info if we have one.
    blink::mojom::ControllerServiceWorkerInfoPtr controller;
    mojo::PendingAssociatedRemote<blink::mojom::ServiceWorkerObject>
        remote_object;
    blink::mojom::ServiceWorkerState sent_state;
    if (subresource_loader_params &&
        subresource_loader_params->controller_service_worker_info) {
      controller =
          std::move(subresource_loader_params->controller_service_worker_info);
      if (controller->object_info) {
        controller->object_info->receiver =
            remote_object.InitWithNewEndpointAndPassReceiver();
        sent_state = controller->object_info->state;
      }
    }

    // Set up prefetch loader factory.
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        factory_bundle_for_prefetch;
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        prefetch_loader_factory;
    if (subresource_loader_factories) {
      // Clone the factory bundle for prefetch.
      auto bundle = base::MakeRefCounted<blink::URLLoaderFactoryBundle>(
          std::move(subresource_loader_factories));
      subresource_loader_factories = CloneFactoryBundle(bundle);
      factory_bundle_for_prefetch = CloneFactoryBundle(bundle);
    }

    if (factory_bundle_for_prefetch) {
      if (prefetched_signed_exchange_cache_) {
        prefetched_signed_exchange_cache_->RecordHistograms();
        // Reset |prefetched_signed_exchange_cache_|, not to reuse the cached
        // signed exchange which was prefetched in the previous page.
        prefetched_signed_exchange_cache_.reset();
      }

      // Also set-up URLLoaderFactory for prefetch using the same loader
      // factories. TODO(kinuko): Consider setting this up only when prefetch
      // is used. Currently we have this here to make sure we have non-racy
      // situation (https://crbug.com/849929).
      auto* storage_partition = GetStoragePartition();
      storage_partition->GetPrefetchURLLoaderService()->GetFactory(
          prefetch_loader_factory.InitWithNewPipeAndPassReceiver(),
          navigation_request->frame_tree_node()->frame_tree_node_id(),
          std::move(factory_bundle_for_prefetch),
          weak_ptr_factory_.GetWeakPtr(),
          EnsurePrefetchedSignedExchangeCache());
    }

    // Set up the topics loader factory. It is used to proxy the topics
    // subresource request via the browser process. The topics loader factory
    // does not depend on the prefetch loader factory (or vice versa), as they
    // are intended for disjoint request types (i.e.
    // fetch(<url>, {browsingTopics: true}) v.s. <link rel="prefetch">).
    mojo::PendingRemote<network::mojom::URLLoaderFactory> topics_loader_factory;
    if (base::FeatureList::IsEnabled(blink::features::kBrowsingTopics)) {
      std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
          factory_bundle_for_topics;

      if (subresource_loader_factories) {
        // Clone the factory bundle for topics.
        auto bundle = base::MakeRefCounted<blink::URLLoaderFactoryBundle>(
            std::move(subresource_loader_factories));
        subresource_loader_factories = CloneFactoryBundle(bundle);
        factory_bundle_for_topics = CloneFactoryBundle(bundle);
      }

      if (factory_bundle_for_topics) {
        // Also set-up URLLoaderFactory for topics using the same loader
        // factories.
        auto* storage_partition = GetStoragePartition();

        base::WeakPtr<BrowsingTopicsURLLoaderService::BindContext>
            bind_context =
                storage_partition->GetBrowsingTopicsURLLoaderService()
                    ->GetFactory(
                        topics_loader_factory.InitWithNewPipeAndPassReceiver(),
                        std::move(factory_bundle_for_topics));

        navigation_request->set_topics_url_loader_service_bind_context(
            bind_context);
      }
    }

    // Set up keepalive loader factory. It is used to proxy the keepalive
    // requests, i.e. fetch(..., {keepalive: true}), via the browser process.
    // See
    // https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit
    // Note that this loader does not depend on `prefetch_loader_factory` nor
    // `topics_loader_factory`.
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        keep_alive_loader_factory;
    if (base::FeatureList::IsEnabled(
            blink::features::kKeepAliveInBrowserMigration)) {
      std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
          factory_bundle_for_keep_alive;

      if (subresource_loader_factories) {
        // Clone the factory bundle for keepalive.
        auto bundle = base::MakeRefCounted<blink::URLLoaderFactoryBundle>(
            std::move(subresource_loader_factories));
        subresource_loader_factories = CloneFactoryBundle(bundle);
        factory_bundle_for_keep_alive = CloneFactoryBundle(bundle);
      }

      if (factory_bundle_for_keep_alive) {
        // Also setting up URLLoaderFactory for keepalive using the same loader
        // factories.
        GetStoragePartition()->GetKeepAliveURLLoaderService()->BindFactory(
            keep_alive_loader_factory.InitWithNewPipeAndPassReceiver(),
            std::move(factory_bundle_for_keep_alive),
            navigation_request->GetPolicyContainerHost());
      }
    }

    mojom::NavigationClient* navigation_client =
        navigation_request->GetCommitNavigationClient();

    // Record the metrics about the state of the old main frame at the moment
    // when we navigate away from it as it matters for whether the page
    // is eligible for being put into back-forward cache.
    //
    // Ideally we would do this when we are just about to swap out the old
    // render frame and swap in the new one, but we can't do this for
    // same-process navigations yet as we are reusing the RenderFrameHost and
    // as the local frame navigates it overrides the values that we are
    // interested in. The cross-process navigation case is handled in
    // RenderFrameHostManager::UnloadOldFrame.
    //
    // Here we are recording the metrics for same-process navigations at the
    // point just before the navigation commits.
    // TODO(altimin, crbug.com/933147): Remove this logic after we are done with
    // implementing back-forward cache.
    if (!GetParent() &&
        navigation_request->frame_tree_node()->current_frame_host() == this) {
      if (NavigationEntryImpl* last_committed_entry =
              NavigationEntryImpl::FromNavigationEntry(
                  navigation_request->frame_tree_node()
                      ->frame_tree()
                      .controller()
                      .GetLastCommittedEntry())) {
        if (last_committed_entry->back_forward_cache_metrics()) {
          last_committed_entry->back_forward_cache_metrics()
              ->RecordFeatureUsage(this);
        }
      }
    }

    blink::mojom::PolicyContainerPtr policy_container =
        navigation_request->CreatePolicyContainerForBlink();

    auto isolation_info = GetSiteInstance()->GetWebExposedIsolationInfo();
    RenderFrameHostImpl* parent_frame_host = GetParentOrOuterDocument();

    absl::optional<blink::ParsedPermissionsPolicy> manifest_policy;
    if (!parent_frame_host && isolation_info.is_isolated_application()) {
      if (auto isolated_web_app_permissions_policy =
              GetContentClient()
                  ->browser()
                  ->GetPermissionsPolicyForIsolatedWebApp(
                      GetBrowserContext(), isolation_info.origin())) {
        manifest_policy = std::move(isolated_web_app_permissions_policy);
      }
    }

    // TODO(crbug.com/1126305): Once the Prerender2 moves to use the MPArch, we
    // need to check the relevant FrameTree to know the precise prerendering
    // state to update commit_params.is_prerendering here.
    // Current design doesn't capture the cases NavigationRequest is created via
    // CreateRendererInitiated or CreateForSynchronousRendererCommit.
    SendCommitNavigation(
        navigation_client, navigation_request, std::move(common_params),
        std::move(commit_params), std::move(head), std::move(response_body),
        std::move(url_loader_client_endpoints),
        std::move(subresource_loader_factories),
        std::move(subresource_overrides), std::move(controller),
        std::move(container_info), std::move(prefetch_loader_factory),
        std::move(topics_loader_factory), std::move(keep_alive_loader_factory),
        std::move(resource_cache_remote), manifest_policy,
        std::move(policy_container), *document_token,
        devtools_navigation_token);
    navigation_request->frame_tree_node()
        ->navigator()
        .LogCommitNavigationSent();

    // |remote_object| is an associated interface ptr, so calls can't be made on
    // it until its request endpoint is sent. Now that the request endpoint was
    // sent, it can be used, so add it to ServiceWorkerObjectHost.
    if (remote_object.is_valid()) {
      subresource_loader_params->controller_service_worker_object_host
          ->AddRemoteObjectPtrAndUpdateState(std::move(remote_object),
                                             sent_state);
    }
  }
}

void RenderFrameHostImpl::FailedNavigation(
    NavigationRequest* navigation_request,
    const blink::mojom::CommonNavigationParams& common_params,
    const blink::mojom::CommitNavigationParams& commit_params,
    bool has_stale_copy_in_cache,
    int error_code,
    int extended_error_code,
    const absl::optional<std::string>& error_page_content,
    const blink::DocumentToken& document_token) {
  TRACE_EVENT2("navigation", "RenderFrameHostImpl::FailedNavigation",
               "navigation_request", navigation_request, "error", error_code);

  DCHECK(navigation_request);

  // Update renderer permissions even for failed commits, so that for example
  // the URL bar correctly displays privileged URLs instead of filtering them.
  UpdatePermissionsForNavigation(navigation_request);

  // Get back to a clean state, in case a new navigation started without
  // completing an unload handler.
  ResetWaitingState();

  std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
      subresource_loader_factories;
  mojo::PendingRemote<network::mojom::URLLoaderFactory> default_factory_remote;
  bool bypass_redirect_checks = CreateNetworkServiceDefaultFactoryAndObserve(
      CreateURLLoaderFactoryParamsForMainWorld(
          SubresourceLoaderFactoriesConfig::ForPendingNavigation(
              *navigation_request),
          "RFHI::FailedNavigation"),
      ukm::kInvalidSourceIdObj,
      default_factory_remote.InitWithNewPipeAndPassReceiver());
  subresource_loader_factories =
      std::make_unique<blink::PendingURLLoaderFactoryBundle>(
          std::move(default_factory_remote),
          blink::PendingURLLoaderFactoryBundle::SchemeMap(),
          blink::PendingURLLoaderFactoryBundle::OriginMap(),
          bypass_redirect_checks);
  recreate_default_url_loader_factory_after_network_service_crash_ = true;

  mojom::NavigationClient* navigation_client =
      navigation_request->GetCommitNavigationClient();

  blink::mojom::PolicyContainerPtr policy_container =
      navigation_request->CreatePolicyContainerForBlink();

  SendCommitFailedNavigation(navigation_client, navigation_request,
                             common_params.Clone(), commit_params.Clone(),
                             has_stale_copy_in_cache, error_code,
                             extended_error_code, error_page_content,
                             std::move(subresource_loader_factories),
                             document_token, std::move(policy_container));

  // TODO(crbug/1129537): support UKM source creation for failed navigations
  // too.

  has_committed_any_navigation_ = true;
  DCHECK(navigation_request && navigation_request->IsNavigationStarted() &&
         navigation_request->DidEncounterError());
}

void RenderFrameHostImpl::AddResourceTimingEntryForFailedSubframeNavigation(
    FrameTreeNode* child_frame,
    base::TimeTicks start_time,
    base::TimeTicks redirect_time,
    const GURL& initial_url,
    const GURL& final_url,
    network::mojom::URLResponseHeadPtr response_head,
    bool allow_response_details,
    const network::URLLoaderCompletionStatus& completion_status) {
  uint32_t status_code = 0;
  std::string mime_type;
  std::string normalized_server_timing;

  response_head->headers->GetNormalizedHeader("Server-Timing",
                                              &normalized_server_timing);

  if (allow_response_details) {
    status_code = response_head->headers->response_code();
    mime_type = response_head->mime_type;
  }

  // To avoid cross-origin leaks, make sure to only to pass here data that
  // is OK when TAO-gated (as in, timing information only).

  absl::optional<blink::FrameToken> child_token_in_parent =
      child_frame->GetRenderFrameHostManager()
          .GetFrameTokenForSiteInstanceGroup(GetSiteInstance()->group());

  if (!child_token_in_parent) {
    return;
  }

  GetAssociatedLocalFrame()->AddResourceTimingEntryForFailedSubframeNavigation(
      child_token_in_parent.value(), initial_url, start_time, redirect_time,
      response_head->request_start, response_head->response_start, status_code,
      mime_type, response_head->load_timing, response_head->connection_info,
      response_head->alpn_negotiated_protocol,
      base::Contains(url::GetSecureSchemes(),
                     url::Origin::Create(final_url).scheme()),
      response_head->is_validated, normalized_server_timing, completion_status);
}

void RenderFrameHostImpl::HandleRendererDebugURL(const GURL& url) {
  DCHECK(blink::IsRendererDebugURL(url));

  GetAssociatedLocalFrame()->HandleRendererDebugURL(url);

  // Ensure that the renderer process is marked as used after processing a
  // renderer debug URL, since this process is now unsafe to be reused by sites
  // that require a dedicated process.  Usually this happens at ready-to-commit
  // (NavigationRequest::OnResponseStarted) time for regular navigations, but
  // renderer debug URLs don't go through that path.  This matters for initial
  // navigations to renderer debug URLs.  See https://crbug.com/1074108.
  GetProcess()->SetIsUsed();
}

void RenderFrameHostImpl::CreateBroadcastChannelProvider(
    mojo::PendingAssociatedReceiver<blink::mojom::BroadcastChannelProvider>
        receiver) {
  auto* storage_partition_impl =
      static_cast<StoragePartitionImpl*>(GetStoragePartition());

  auto* broadcast_channel_service =
      storage_partition_impl->GetBroadcastChannelService();
  broadcast_channel_service->AddAssociatedReceiver(
      std::make_unique<BroadcastChannelProvider>(broadcast_channel_service,
                                                 storage_key()),
      std::move(receiver));
}

void RenderFrameHostImpl::BindBlobUrlStoreAssociatedReceiver(
    mojo::PendingAssociatedReceiver<blink::mojom::BlobURLStore> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto* storage_partition_impl =
      static_cast<StoragePartitionImpl*>(GetStoragePartition());

  storage_partition_impl->GetBlobUrlRegistry()->AddReceiver(
      storage_key(), std::move(receiver));
}

void RenderFrameHostImpl::BindBlobUrlStoreReceiver(
    mojo::PendingReceiver<blink::mojom::BlobURLStore> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto* storage_partition_impl =
      static_cast<StoragePartitionImpl*>(GetStoragePartition());

  storage_partition_impl->GetBlobUrlRegistry()->AddReceiver(
      storage_key(), std::move(receiver));
}

bool RenderFrameHostImpl::IsFocused() {
  if (!GetMainFrame()->GetRenderWidgetHost()->is_focused() ||
      !frame_tree_->GetFocusedFrame())
    return false;

  RenderFrameHostImpl* focused_rfh =
      frame_tree_->GetFocusedFrame()->current_frame_host();
  return focused_rfh == this ||
         focused_rfh->IsDescendantOfWithinFrameTree(this);
}

void RenderFrameHostImpl::SetWebUI(NavigationRequest& request) {
  std::unique_ptr<WebUIImpl> new_web_ui = request.TakeWebUI();
  // This function should only be called to set a WebUI object. To clear an
  // existing WebUI object, call `ClearWebUI()` instead.
  CHECK(new_web_ui);
  // If a WebUI has been set for a RenderFrameHost, we shouldn't overwrite it
  // with a new WebUI.
  CHECK(!web_ui_);

  // Verify expectation that WebUI should not be created for error pages.
  DCHECK(!GetSiteInstance()->GetSiteInfo().is_error_page());

  WebUI::TypeID new_web_ui_type =
      WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
          GetSiteInstance()->GetBrowserContext(), request.GetURL());
  CHECK_NE(new_web_ui_type, WebUI::kNoWebUI);

  web_ui_ = std::move(new_web_ui);
  web_ui_->SetRenderFrameHost(this);

  // It is not expected for GuestView to be able to navigate to WebUI.
  DCHECK(!GetProcess()->IsForGuestsOnly());

  web_ui_type_ = new_web_ui_type;

  // WebUIs need the ability to request certain schemes.
  for (const auto& scheme : web_ui_->GetRequestableSchemes()) {
    ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestScheme(
        GetProcess()->GetID(), scheme);
  }

  // Since this is new WebUI instance, this RenderFrameHostImpl should not
  // have had any bindings. Verify that and grant the required bindings.
  DCHECK_EQ(BINDINGS_POLICY_NONE, GetEnabledBindings());
  AllowBindings(web_ui_->GetBindings());
}

void RenderFrameHostImpl::ClearWebUI() {
  web_ui_type_ = WebUI::kNoWebUI;
  web_ui_.reset();  // This might delete `this`.
  // DO NOT ADD CODE after this.
}

const mojo::Remote<blink::mojom::ImageDownloader>&
RenderFrameHostImpl::GetMojoImageDownloader() {
  // TODO(https://crbug.com/1249933): Call AssertNonSpeculativeFrame() here.
  if (!mojo_image_downloader_.is_bound() && GetRemoteInterfaces()) {
    GetRemoteInterfaces()->GetInterface(
        mojo_image_downloader_.BindNewPipeAndPassReceiver());
  }
  return mojo_image_downloader_;
}

const mojo::AssociatedRemote<blink::mojom::FindInPage>&
RenderFrameHostImpl::GetFindInPage() {
  if (!find_in_page_)
    GetRemoteAssociatedInterfaces()->GetInterface(&find_in_page_);
  return find_in_page_;
}

const mojo::AssociatedRemote<blink::mojom::LocalFrame>&
RenderFrameHostImpl::GetAssociatedLocalFrame() {
  if (!local_frame_)
    GetRemoteAssociatedInterfaces()->GetInterface(&local_frame_);
  return local_frame_;
}

blink::mojom::LocalMainFrame*
RenderFrameHostImpl::GetAssociatedLocalMainFrame() {
  DCHECK(is_main_frame());
  if (!local_main_frame_)
    GetRemoteAssociatedInterfaces()->GetInterface(&local_main_frame_);
  return local_main_frame_.get();
}

const mojo::Remote<blink::mojom::HighPriorityLocalFrame>&
RenderFrameHostImpl::GetHighPriorityLocalFrame() {
  if (!high_priority_local_frame_.is_bound()) {
    GetRemoteInterfaces()->GetInterface(
        high_priority_local_frame_.BindNewPipeAndPassReceiver());
  }
  return high_priority_local_frame_;
}

const mojo::AssociatedRemote<mojom::FrameBindingsControl>&
RenderFrameHostImpl::GetFrameBindingsControl() {
  if (!frame_bindings_control_)
    GetRemoteAssociatedInterfaces()->GetInterface(&frame_bindings_control_);
  return frame_bindings_control_;
}

void RenderFrameHostImpl::ResetLoadingState() {
  if (is_loading()) {
    // When pending deletion, just set the loading state to not loading.
    // Otherwise, DidStopLoading will take care of that, as well as sending
    // notification to the FrameTreeNode about the change in loading state.
    if (IsPendingDeletion() || IsInBackForwardCache()) {
      loading_state_ = LoadingState::NONE;
    } else {
      DidStopLoading();
    }
  }
}

void RenderFrameHostImpl::ClearFocusedElement() {
  has_focused_editable_element_ = false;
  GetAssociatedLocalFrame()->ClearFocusedElement();
}

void RenderFrameHostImpl::BindDevToolsAgent(
    mojo::PendingAssociatedRemote<blink::mojom::DevToolsAgentHost> host,
    mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> receiver) {
  GetAssociatedLocalFrame()->BindDevToolsAgent(std::move(host),
                                               std::move(receiver));
}

bool RenderFrameHostImpl::IsSameSiteInstance(
    RenderFrameHostImpl* other_render_frame_host) {
  // As a sanity check, make sure the frame belongs to the same BrowserContext.
  CHECK_EQ(GetSiteInstance()->GetBrowserContext(),
           other_render_frame_host->GetSiteInstance()->GetBrowserContext());
  return GetSiteInstance() == other_render_frame_host->GetSiteInstance();
}

void RenderFrameHostImpl::UpdateAccessibilityMode() {
  // Don't update accessibility mode for a frame that hasn't been created yet.
  if (!IsRenderFrameLive())
    return;

  ui::AXMode ax_mode = delegate_->GetAccessibilityMode();

  // Disable BackForwardCache if ScreenReader is on.
  // TODO(crbug.com/1271450): Screen readers do not recognize a navigation when
  // the page is served from bfcache. Remove the flag and this section once the
  // fix is landed.
  if (ax_mode.has_mode(ui::AXMode::kScreenReader) &&
      !BackForwardCacheImpl::IsScreenReaderAllowed()) {
    BackForwardCache::DisableForRenderFrameHost(
        this, BackForwardCacheDisable::DisabledReason(
                  BackForwardCacheDisable::DisabledReasonId::kScreenReader));
  }

  if (ax_mode.has_mode(ui::AXMode::kWebContents)) {
    if (!render_accessibility_) {
      // Render accessibility is not enabled yet, so bind the interface first.
      GetRemoteAssociatedInterfaces()->GetInterface(&render_accessibility_);
      DCHECK(render_accessibility_);
    }
    render_accessibility_->SetMode(ax_mode);
  } else {
    // Resetting the Remote signals the renderer to shutdown accessibility
    // in the renderer.
    render_accessibility_.reset();
  }

  if (!ax_mode.has_mode(ui::kAXModeBasic.flags()) &&
      browser_accessibility_manager_) {
    // Missing either kWebContents and kNativeAPIs, so
    // BrowserAccessibilityManager is no longer necessary.
    browser_accessibility_manager_->DetachFromParentManager();
    browser_accessibility_manager_.reset();
  }
}

void RenderFrameHostImpl::SnapshotDocumentForViewTransition(
    blink::mojom::LocalFrame::SnapshotDocumentForViewTransitionCallback
        callback) {
  DCHECK(IsRenderFrameLive());
  GetAssociatedLocalFrame()->SnapshotDocumentForViewTransition(
      std::move(callback));
}

#if BUILDFLAG(ENABLE_PPAPI)
RenderFrameHostImplPpapiSupport& RenderFrameHostImpl::GetPpapiSupport() {
  if (!ppapi_support_) {
    ppapi_support_ = std::make_unique<RenderFrameHostImplPpapiSupport>(*this);
  }
  return *ppapi_support_;
}
#endif

void RenderFrameHostImpl::RequestAXTreeSnapshot(
    AXTreeSnapshotCallback callback,
    mojom::SnapshotAccessibilityTreeParamsPtr params) {
  // TODO(https://crbug.com/859110): Remove once frame_ can no longer be null.
  if (!IsRenderFrameLive())
    return;

  GetMojomFrameInRenderer()->SnapshotAccessibilityTree(
      std::move(params),
      base::BindOnce(&RenderFrameHostImpl::RequestAXTreeSnapshotCallback,
                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

void RenderFrameHostImpl::GetSavableResourceLinksFromRenderer() {
  if (!IsRenderFrameLive())
    return;
  GetAssociatedLocalFrame()->GetSavableResourceLinks(
      base::BindOnce(&RenderFrameHostImpl::GetSavableResourceLinksCallback,
                     weak_ptr_factory_.GetWeakPtr()));
}

void RenderFrameHostImpl::SetAccessibilityCallbackForTesting(
    const AccessibilityCallbackForTesting& callback) {
  accessibility_testing_callback_ = callback;
}

void RenderFrameHostImpl::UpdateAXTreeData() {
  ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
  if (accessibility_mode.is_mode_off() ||
      IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kAXUpdateTree)) {
    return;
  }

  // If `needs_ax_root_id_` is true, an AXTreeUpdate has not been sent to
  // the renderer with a valid root id. This effectively means the renderer
  // does not really know about the tree, and the renderer should not be
  // updated. The renderer will be updated once the root id is known.
  if (needs_ax_root_id_)
    return;

  if (ax_defer_scope_count_ > 0) {
    ax_update_deferred_ = true;
    return;
  }

  AXEventNotificationDetails detail;
  detail.ax_tree_id = GetAXTreeID();
  detail.updates.resize(1);
  detail.updates[0].has_tree_data = true;
  detail.updates[0].tree_data = GetAXTreeData();

  SendAccessibilityEventsToManager(detail);
  delegate_->AccessibilityEventReceived(detail);
}

RenderFrameHostImpl::UpdateAXFocusDeferScope::UpdateAXFocusDeferScope(
    RenderFrameHostImpl& rfh)
    : rfh_(rfh.GetSafeRef()) {
  ++rfh_->ax_defer_scope_count_;
}

RenderFrameHostImpl::UpdateAXFocusDeferScope::~UpdateAXFocusDeferScope() {
  DCHECK_GE(rfh_->ax_defer_scope_count_, 1);
  --rfh_->ax_defer_scope_count_;
  if (!rfh_->ax_defer_scope_count_ && rfh_->ax_update_deferred_) {
    rfh_->ax_update_deferred_ = false;
    rfh_->UpdateAXTreeData();
  }
}

BrowserAccessibilityManager*
RenderFrameHostImpl::GetOrCreateBrowserAccessibilityManager() {
  // Never create a BrowserAccessibilityManager unless needed for the AXMode.
  // At least basic mode is required; it contains kWebContents and KNativeAPIs.
  ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
  if (!accessibility_mode.has_mode(ui::AXMode::kNativeAPIs)) {
    DCHECK(!browser_accessibility_manager_);
    return nullptr;
  }

  if (browser_accessibility_manager_ ||
      no_create_browser_accessibility_manager_for_testing_)
    return browser_accessibility_manager_.get();

  browser_accessibility_manager_.reset(
      BrowserAccessibilityManager::Create(this));
  return browser_accessibility_manager_.get();
}

void RenderFrameHostImpl::ActivateFindInPageResultForAccessibility(
    int request_id) {
  BrowserAccessibilityManager* manager =
      GetOrCreateBrowserAccessibilityManager();
  if (manager)
    manager->ActivateFindInPageResult(request_id);
}

void RenderFrameHostImpl::InsertVisualStateCallback(
    VisualStateCallback callback) {
  GetRenderWidgetHost()->InsertVisualStateCallback(std::move(callback));
}

bool RenderFrameHostImpl::IsLastCommitIPAddressPubliclyRoutable() const {
  net::IPEndPoint ip_end_point =
      last_response_head().get() ? last_response_head().get()->remote_endpoint
                                 : net::IPEndPoint();

  return ip_end_point.address().IsPubliclyRoutable();
}

bool RenderFrameHostImpl::IsRenderFrameLive() {
  bool is_live =
      GetProcess()->IsInitializedAndNotDead() && is_render_frame_created();

  // Sanity check: the `blink::WebView` should always be live if the RenderFrame
  // is.
  DCHECK(!is_live || render_view_host_->IsRenderViewLive());

  return is_live;
}

RenderFrameHost::LifecycleState RenderFrameHostImpl::GetLifecycleState() {
  return GetLifecycleStateFromImpl(lifecycle_state());
}

bool RenderFrameHostImpl::IsInLifecycleState(LifecycleState state) {
  if (lifecycle_state() == LifecycleStateImpl::kSpeculative)
    return false;
  return state == GetLifecycleState();
}

RenderFrameHost::LifecycleState RenderFrameHostImpl::GetLifecycleStateFromImpl(
    LifecycleStateImpl state) {
  switch (state) {
    case LifecycleStateImpl::kSpeculative:
      // TODO(https://crbug.com/1183639): Ensure that Speculative
      // RenderFrameHosts are not exposed to embedders.
      NOTREACHED();
      return LifecycleState::kPendingCommit;
    case LifecycleStateImpl::kPendingCommit:
      return LifecycleState::kPendingCommit;
    case LifecycleStateImpl::kPrerendering:
      return LifecycleState::kPrerendering;
    case LifecycleStateImpl::kActive:
      return LifecycleState::kActive;
    case LifecycleStateImpl::kInBackForwardCache:
      return LifecycleState::kInBackForwardCache;
    case LifecycleStateImpl::kRunningUnloadHandlers:
      return LifecycleState::kPendingDeletion;
    case LifecycleStateImpl::kReadyToBeDeleted:
      return LifecycleState::kPendingDeletion;
  }
}

bool RenderFrameHostImpl::IsActive() {
  // When the document is transitioning away from kActive/kPrerendering to a
  // yet-to-be-determined state, the RenderFrameHostManager has already
  // updated its active RenderFrameHost, and the old document is no longer
  // the active one. In that case, return false.
  if (has_pending_lifecycle_state_update_)
    return false;

  return lifecycle_state() == LifecycleStateImpl::kActive;
}

size_t RenderFrameHostImpl::GetProxyCount() {
  if (!IsActive())
    return 0;
  return browsing_context_state_->GetProxyCount();
}

bool RenderFrameHostImpl::HasSelection() {
  return has_selection_;
}

FrameTreeNode* RenderFrameHostImpl::PreviousSibling() const {
  return GetSibling(-1);
}

FrameTreeNode* RenderFrameHostImpl::NextSibling() const {
  return GetSibling(1);
}

FrameTreeNode* RenderFrameHostImpl::GetSibling(int relative_offset) const {
  if (!parent_ || !parent_->child_count())
    return nullptr;

  for (size_t i = 0; i < parent_->child_count(); ++i) {
    // Frame tree node id will only be known for subframes, and will therefore
    // be accessible in this iteration, as all children are subframes.
    if (parent_->child_at(i)->frame_tree_node_id() != GetFrameTreeNodeId()) {
      continue;
    }

    if (relative_offset < 0 && base::checked_cast<size_t>(-relative_offset) > i)
      return nullptr;
    if (i + relative_offset >= parent_->child_count())
      return nullptr;
    return parent_->child_at(i + relative_offset);
  }

  NOTREACHED() << "FrameTreeNode not found in its parent's children.";
  return nullptr;
}

RenderFrameHostImpl* RenderFrameHostImpl::GetMainFrame() {
  return const_cast<RenderFrameHostImpl*>(std::as_const(*this).GetMainFrame());
}

const RenderFrameHostImpl* RenderFrameHostImpl::GetMainFrame() const {
  // Iteration over the GetParent() chain is used below, because returning
  // |frame_tree().root()->current_frame_host()| might
  // give an incorrect result after |this| has been detached from the frame
  // tree.
  const RenderFrameHostImpl* main_frame = this;
  while (const RenderFrameHostImpl* parent = main_frame->GetParent()) {
    main_frame = parent;
  }
  return main_frame;
}

bool RenderFrameHostImpl::IsInPrimaryMainFrame() {
  return !GetParent() && GetPage().IsPrimary();
}

RenderFrameHostImpl* RenderFrameHostImpl::GetOutermostMainFrame() {
  RenderFrameHostImpl* current = this;
  while (RenderFrameHostImpl* parent_or_outer_doc =
             current->GetParentOrOuterDocument()) {
    current = parent_or_outer_doc;
  }
  return current;
}

bool RenderFrameHostImpl::CanAccessFilesOfPageState(
    const blink::PageState& state) {
  return ChildProcessSecurityPolicyImpl::GetInstance()->CanReadAllFiles(
      GetProcess()->GetID(), state.GetReferencedFiles());
}

void RenderFrameHostImpl::GrantFileAccessFromPageState(
    const blink::PageState& state) {
  GrantFileAccess(GetProcess()->GetID(), state.GetReferencedFiles());
}

void RenderFrameHostImpl::SetHasPendingLifecycleStateUpdate() {
  DCHECK(!has_pending_lifecycle_state_update_);
  for (auto& child : children_)
    child->current_frame_host()->SetHasPendingLifecycleStateUpdate();
  has_pending_lifecycle_state_update_ = true;
}

void RenderFrameHostImpl::GrantFileAccessFromResourceRequestBody(
    const network::ResourceRequestBody& body) {
  GrantFileAccess(GetProcess()->GetID(), body.GetReferencedFiles());
}

void RenderFrameHostImpl::UpdatePermissionsForNavigation(
    NavigationRequest* request) {
  // Browser plugin guests are not allowed to navigate outside web-safe schemes,
  // so do not grant them the ability to commit additional URLs.
  if (!GetProcess()->IsForGuestsOnly()) {
    ChildProcessSecurityPolicyImpl::GetInstance()->GrantCommitURL(
        GetProcess()->GetID(), request->common_params().url);
    if (request->IsLoadDataWithBaseURL()) {
      // When there's a base URL specified for the data URL, we also need to
      // grant access to the base URL. This allows file: and other unexpected
      // schemes to be accepted at commit time and during CORS checks (e.g., for
      // font requests).
      ChildProcessSecurityPolicyImpl::GetInstance()->GrantCommitURL(
          GetProcess()->GetID(),
          request->common_params().base_url_for_data_url);
    }
  }

  // We may be returning to an existing NavigationEntry that had been granted
  // file access.  If this is a different process, we will need to grant the
  // access again.  Abuse is prevented, because the files listed in the page
  // state are validated earlier, when they are received from the renderer (in
  // RenderFrameHostImpl::CanAccessFilesOfPageState).
  blink::PageState page_state = blink::PageState::CreateFromEncodedData(
      request->commit_params().page_state);
  if (page_state.IsValid())
    GrantFileAccessFromPageState(page_state);

  // We may be here after transferring navigation to a different renderer
  // process.  In this case, we need to ensure that the new renderer retains
  // ability to access files that the old renderer could access.  Abuse is
  // prevented, because the files listed in ResourceRequestBody are validated
  // earlier, when they are received from the renderer.
  if (request->common_params().post_data)
    GrantFileAccessFromResourceRequestBody(*request->common_params().post_data);
}

bool RenderFrameHostImpl::WindowManagementAllowsFullscreen() {
  return IsWindowManagementGranted(this) &&
         delegate_->IsTransientAllowFullscreenActive();
}

mojo::AssociatedRemote<mojom::NavigationClient>
RenderFrameHostImpl::GetNavigationClientFromInterfaceProvider() {
  mojo::AssociatedRemote<mojom::NavigationClient> navigation_client_remote;
  GetRemoteAssociatedInterfaces()->GetInterface(&navigation_client_remote);
  return navigation_client_remote;
}

void RenderFrameHostImpl::NavigationRequestCancelled(
    NavigationRequest* navigation_request) {
  // Remove the requests from the list of NavigationRequests waiting to commit.
  // RenderDocument should obsolete the need for this, as always swapping RFHs
  // means that it won't be necessary to clean up the list of navigation
  // requests when the renderer aborts a navigation--instead, we'll always just
  // throw away the entire speculative RFH.
  navigation_requests_.erase(navigation_request);
}

NavigationRequest*
RenderFrameHostImpl::FindLatestNavigationRequestThatIsStillCommitting() {
  // Find the most recent NavigationRequest that has triggered a Commit IPC to
  // the renderer process.  Once the renderer process handles the IPC, it may
  // possibly change the origin from |last_committed_origin_| to another origin.
  NavigationRequest* found_request = nullptr;
  for (const auto& it : navigation_requests_) {
    NavigationRequest* candidate = it.first;
    DCHECK_EQ(candidate, it.second.get());

    if (candidate->state() < NavigationRequest::READY_TO_COMMIT)
      continue;
    if (candidate->state() >= NavigationRequest::DID_COMMIT)
      continue;

    if (!found_request ||
        found_request->NavigationStart() < candidate->NavigationStart()) {
      found_request = candidate;
    }
  }

  return found_request;
}

network::mojom::URLLoaderFactoryParamsPtr
RenderFrameHostImpl::CreateURLLoaderFactoryParamsForMainWorld(
    const SubresourceLoaderFactoriesConfig& config,
    base::StringPiece debug_tag) {
  return URLLoaderFactoryParamsHelper::CreateForFrame(
      this, config.origin(), config.isolation_info(),
      config.GetClientSecurityState(), config.GetCoepReporter(), GetProcess(),
      config.trust_token_issuance_policy(),
      config.trust_token_redemption_policy(), config.cookie_setting_overrides(),
      debug_tag
#if BUILDFLAG(IS_OHOS_PRPP)
      ,
      config.main_url(),
      config.addr_web_handle()
#endif
      );
}

bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactoryAndObserve(
    network::mojom::URLLoaderFactoryParamsPtr params,
    ukm::SourceIdObj ukm_source_id,
    mojo::PendingReceiver<network::mojom::URLLoaderFactory>
        default_factory_receiver) {
  bool bypass_redirect_checks = CreateNetworkServiceDefaultFactoryInternal(
      std::move(params), ukm_source_id, std::move(default_factory_receiver));

  // Add a disconnect handler when Network Service is running
  // out-of-process.
  if (IsOutOfProcessNetworkService() &&
      (!network_service_disconnect_handler_holder_ ||
       !network_service_disconnect_handler_holder_.is_connected())) {
    network_service_disconnect_handler_holder_.reset();
    StoragePartition* storage_partition = GetStoragePartition();
    network::mojom::URLLoaderFactoryParamsPtr monitoring_factory_params =
        network::mojom::URLLoaderFactoryParams::New();
    monitoring_factory_params->process_id = GetProcess()->GetID();
    monitoring_factory_params->debug_tag = "RFHI - monitoring_factory_params";

    // This factory should never be used to issue actual requests (i.e. it
    // should only be used to monitor for Network Service crashes).  Below is an
    // attempt to enforce that the factory cannot be used in practice.
    monitoring_factory_params->request_initiator_origin_lock =
        url::Origin::Create(
            GURL("https://monitoring.url.loader.factory.invalid"));

    storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
        network_service_disconnect_handler_holder_.BindNewPipeAndPassReceiver(),
        std::move(monitoring_factory_params));
    network_service_disconnect_handler_holder_.set_disconnect_handler(
        base::BindOnce(&RenderFrameHostImpl::UpdateSubresourceLoaderFactories,
                       weak_ptr_factory_.GetWeakPtr()));
  }
  return bypass_redirect_checks;
}

bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactoryInternal(
    network::mojom::URLLoaderFactoryParamsPtr params,
    ukm::SourceIdObj ukm_source_id,
    mojo::PendingReceiver<network::mojom::URLLoaderFactory>
        default_factory_receiver) {
  DCHECK(params->request_initiator_origin_lock.has_value());
  const url::Origin& request_initiator =
      params->request_initiator_origin_lock.value();

  bool bypass_redirect_checks = false;
  WillCreateURLLoaderFactory(
      request_initiator, &default_factory_receiver, ukm_source_id,
      &params->header_client, &bypass_redirect_checks,
      &params->disable_secure_dns, &params->factory_override);

  GetProcess()->CreateURLLoaderFactory(std::move(default_factory_receiver),
                                       std::move(params));

  return bypass_redirect_checks;
}

void RenderFrameHostImpl::WillCreateURLLoaderFactory(
    const url::Origin& request_initiator,
    mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver,
    ukm::SourceIdObj ukm_source_id,
    mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
        header_client,
    bool* bypass_redirect_checks,
    bool* disable_secure_dns,
    network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
  GetContentClient()->browser()->WillCreateURLLoaderFactory(
      GetBrowserContext(), this, GetProcess()->GetID(),
      ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource,
      request_initiator, /*navigation_id=*/absl::nullopt, ukm_source_id,
      factory_receiver, header_client, bypass_redirect_checks,
      disable_secure_dns, factory_override);

  // Keep DevTools proxy last, i.e. closest to the network.
  devtools_instrumentation::WillCreateURLLoaderFactory(
      this, /*is_navigation=*/false, /*is_download=*/false, factory_receiver,
      factory_override);
}

bool RenderFrameHostImpl::CanExecuteJavaScript() {
  if (g_allow_injecting_javascript)
    return true;

  return !GetLastCommittedURL().is_valid() ||
         GetLastCommittedURL().SchemeIs(kChromeDevToolsScheme) ||
         ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
             GetProcess()->GetID()) ||
         // It's possible to load about:blank in a Web UI renderer.
         // See http://crbug.com/42547
         (GetLastCommittedURL().spec() == url::kAboutBlankURL);
}

// static
int RenderFrameHost::GetFrameTreeNodeIdForRoutingId(int process_id,
                                                    int routing_id) {
  auto frame_or_proxy = LookupRenderFrameHostOrProxy(process_id, routing_id);
  if (frame_or_proxy)
    return frame_or_proxy.GetFrameTreeNode()->frame_tree_node_id();
  return kNoFrameTreeNodeId;
}

// static
int RenderFrameHost::GetFrameTreeNodeIdForFrameToken(
    int process_id,
    const ::blink::FrameToken& frame_token) {
  auto frame_or_proxy = LookupRenderFrameHostOrProxy(process_id, frame_token);
  if (frame_or_proxy)
    return frame_or_proxy.GetFrameTreeNode()->frame_tree_node_id();
  return kNoFrameTreeNodeId;
}

// static
RenderFrameHost* RenderFrameHost::FromPlaceholderToken(
    int render_process_id,
    const blink::RemoteFrameToken& placeholder_frame_token) {
  RenderFrameProxyHost* rfph = RenderFrameProxyHost::FromFrameToken(
      render_process_id, placeholder_frame_token);
  FrameTreeNode* node = rfph ? rfph->frame_tree_node() : nullptr;
  return node ? node->current_frame_host() : nullptr;
}

ui::AXTreeID RenderFrameHostImpl::GetParentAXTreeID() {
  auto* parent = GetParentOrOuterDocumentOrEmbedderExcludingProspectiveOwners();
  if (!parent) {
    DCHECK(AccessibilityIsRootFrame())
        << "Child frame requires a parent, root=" << GetLastCommittedURL();
    return ui::AXTreeIDUnknown();
  }
  // TODO(accessibility) The following check fails when running this test with
  // --force-renderer-accessibility:
  // http/tests/devtools/resource-tree/resource-tree-frame-in-crafted-frame.js
  // It seems that fabricating a frame with document.write() results in a
  // frame that has no embedding token.
  // DCHECK(parent->GetAXTreeID() != ui::AXTreeIDUnknown())
  //     << "Parent frame must have an id, child url = " <<
  //     GetLastCommittedURL()
  //     << "    parent url = " << parent->GetLastCommittedURL();
  DCHECK(!AccessibilityIsRootFrame())
      << "Root frame must not have a parent, root=" << GetLastCommittedURL()
      << "  parent=" << parent->GetLastCommittedURL();
  return parent->GetAXTreeID();
}

ui::AXTreeID RenderFrameHostImpl::GetFocusedAXTreeID() {
  // If this is not the root frame tree node, we're done.
  if (!AccessibilityIsRootFrame())
    return ui::AXTreeIDUnknown();

  RenderFrameHostImpl* focused_frame = delegate_->GetFocusedFrame();
  if (focused_frame)
    return focused_frame->GetAXTreeID();

  // It's possible for there to be no focused RenderFrameHost (e.g. the frame
  // that had focus was destroyed). Note however, that this doesn't mean that
  // keyboard events are ignored; they'd be sent by default to the root
  // RenderWidgetHost.
  return ui::AXTreeIDUnknown();
}

ui::AXTreeData RenderFrameHostImpl::GetAXTreeData() {
  // Make sure to update the locally stored |ax_tree_data_| to include
  // references to the relevant AXTreeIDs before returning its value.
  ax_tree_data_.tree_id = GetAXTreeID();
  ax_tree_data_.parent_tree_id = GetParentAXTreeID();
  ax_tree_data_.focused_tree_id = GetFocusedAXTreeID();
  return ax_tree_data_;
}

void RenderFrameHostImpl::AccessibilityHitTestCallback(
    int request_id,
    ax::mojom::Event event_to_fire,
    base::OnceCallback<void(ui::AXPlatformTreeManager* hit_manager,
                            ui::AXNodeID hit_node_id)> opt_callback,
    blink::mojom::HitTestResponsePtr hit_test_response) {
  if (!hit_test_response) {
    if (opt_callback)
      std::move(opt_callback).Run(nullptr, 0);
    return;
  }

  auto frame_or_proxy = LookupRenderFrameHostOrProxy(
      GetProcess()->GetID(), hit_test_response->hit_frame_token);
  RenderFrameHostImpl* hit_frame = frame_or_proxy.GetCurrentFrameHost();

  if (!hit_frame || hit_frame->IsInactiveAndDisallowActivation(
                        DisallowActivationReasonId::kAXHitTestCallback)) {
    if (opt_callback)
      std::move(opt_callback).Run(nullptr, 0);
    return;
  }

  // If the hit node's routing ID is the same frame, we're done. If a
  // callback was provided, call it with the information about the hit node.
  if (hit_frame->GetFrameToken() == frame_token_) {
    if (opt_callback) {
      std::move(opt_callback)
          .Run(hit_frame->browser_accessibility_manager(),
               hit_test_response->hit_node_id);
    }
    return;
  }

  // The hit node has a child frame. Do a hit test in that frame's renderer.
  hit_frame->AccessibilityHitTest(
      hit_test_response->hit_frame_transformed_point, event_to_fire, request_id,
      std::move(opt_callback));
}

void RenderFrameHostImpl::RequestAXTreeSnapshotCallback(
    AXTreeSnapshotCallback callback,
    const ui::AXTreeUpdate& snapshot) {
  // Since |snapshot| is const, we need to make a copy in order to modify the
  // tree data.
  ui::AXTreeUpdate dst_snapshot;
  CopyAXTreeUpdate(snapshot, &dst_snapshot);
  std::move(callback).Run(dst_snapshot);
}

void RenderFrameHostImpl::CopyAXTreeUpdate(const ui::AXTreeUpdate& snapshot,
                                           ui::AXTreeUpdate* snapshot_copy) {
  snapshot_copy->root_id = snapshot.root_id;
  snapshot_copy->nodes.resize(snapshot.nodes.size());
  for (size_t i = 0; i < snapshot.nodes.size(); ++i)
    snapshot_copy->nodes[i] = snapshot.nodes[i];

  if (snapshot.has_tree_data) {
    ax_tree_data_ = snapshot.tree_data;
    // Set the AXTreeData to be the last |ax_tree_data_| received from the
    // render frame.
    snapshot_copy->tree_data = GetAXTreeData();
    snapshot_copy->has_tree_data = true;
  }
}

void RenderFrameHostImpl::CreatePaymentManager(
    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
  if (!IsFeatureEnabled(blink::mojom::PermissionsPolicyFeature::kPayment)) {
    mojo::ReportBadMessage("Permissions policy blocks Payment");
    return;
  }
  GetProcess()->CreatePaymentManagerForOrigin(GetLastCommittedOrigin(),
                                              std::move(receiver));

  // Blocklist PaymentManager from the back-forward cache as at the moment we
  // don't cancel pending payment requests when the RenderFrameHost is stored
  // in back-forward cache.
  OnBackForwardCacheDisablingStickyFeatureUsed(
      BackForwardCacheDisablingFeature::kPaymentManager);
}

WebBluetoothServiceImpl*
RenderFrameHostImpl::GetWebBluetoothServiceForTesting() {
  if (!document_associated_data_ || !last_web_bluetooth_service_for_testing_)
    return nullptr;

  return last_web_bluetooth_service_for_testing_;
}

void RenderFrameHostImpl::CreateWebBluetoothService(
    mojo::PendingReceiver<blink::mojom::WebBluetoothService> receiver) {
  BackForwardCache::DisableForRenderFrameHost(
      this, BackForwardCacheDisable::DisabledReason(
                BackForwardCacheDisable::DisabledReasonId::kWebBluetooth));

  // Although the returned pointer is being stored for test support, this is not
  // a test-only function, and the call of WebBluetoothServiceImpl::Create below
  // is how the WebBluetoothServiceImpl instance gets created.
  last_web_bluetooth_service_for_testing_ =
      WebBluetoothServiceImpl::Create(this, std::move(receiver));
}

void RenderFrameHostImpl::CreateWebUsbService(
    mojo::PendingReceiver<blink::mojom::WebUsbService> receiver) {
  if (!base::FeatureList::IsEnabled(features::kWebUsb)) {
    return;
  }
  if (!IsFeatureEnabled(blink::mojom::PermissionsPolicyFeature::kUsb)) {
    mojo::ReportBadMessage("Permissions policy blocks access to USB.");
    return;
  }
  if (GetOutermostMainFrame()->GetLastCommittedOrigin().opaque()) {
    mojo::ReportBadMessage(
        "WebUSB is not allowed when the top-level document has an opaque "
        "origin.");
    return;
  }
  BackForwardCache::DisableForRenderFrameHost(
      this, BackForwardCacheDisable::DisabledReason(
                BackForwardCacheDisable::DisabledReasonId::kWebUSB));
  WebUsbServiceImpl::Create(*this, std::move(receiver));
}

void RenderFrameHostImpl::ResetPermissionsPolicy() {
  if (IsFencedFrameRoot()) {
    const absl::optional<FencedFrameProperties>& properties =
        frame_tree_node()->GetFencedFrameProperties();
    // Fenced frames have a list of required permission policies to load and
    // can't be granted extra policies, so use the required policies instead of
    // inheriting from its parent. Note that the parent policies must allow the
    // required policies, which is checked separately in
    // NavigationRequest::CheckPermissionsPoliciesForFencedFrames.
    permissions_policy_ = blink::PermissionsPolicy::CreateForFencedFrame(
        last_committed_origin_,
        properties.has_value() &&
            properties->mode_ ==
                blink::FencedFrame::DeprecatedFencedFrameMode::kOpaqueAds);
    return;
  }

  RenderFrameHostImpl* parent_frame_host = GetParent();
  auto isolation_info = GetSiteInstance()->GetWebExposedIsolationInfo();

  if (!parent_frame_host && isolation_info.is_isolated_application()) {
    // In Isolated Apps, the top level frame should use the policy declared in
    // the Web App Manifest.
    if (auto isolated_web_app_permissions_policy =
            GetContentClient()
                ->browser()
                ->GetPermissionsPolicyForIsolatedWebApp(
                    GetBrowserContext(), isolation_info.origin())) {
      permissions_policy_ = blink::PermissionsPolicy::CreateFromParsedPolicy(
          *isolated_web_app_permissions_policy, last_committed_origin_);
      return;
    }
  }

  const blink::PermissionsPolicy* parent_policy =
      parent_frame_host ? parent_frame_host->permissions_policy() : nullptr;
  blink::ParsedPermissionsPolicy container_policy =
      browsing_context_state_->effective_frame_policy().container_policy;

  permissions_policy_ = blink::PermissionsPolicy::CreateFromParentPolicy(
      parent_policy, container_policy, last_committed_origin_);
}

void RenderFrameHostImpl::CreateAudioInputStreamFactory(
    mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
        receiver) {
  BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
  DCHECK(browser_main_loop);
  MediaStreamManager* msm = browser_main_loop->media_stream_manager();
  audio_service_audio_input_stream_factory_.emplace(std::move(receiver), msm,
                                                    this);
}

void RenderFrameHostImpl::CreateAudioOutputStreamFactory(
    mojo::PendingReceiver<blink::mojom::RendererAudioOutputStreamFactory>
        receiver) {
  media::AudioSystem* audio_system =
      BrowserMainLoop::GetInstance()->audio_system();
  MediaStreamManager* media_stream_manager =
      BrowserMainLoop::GetInstance()->media_stream_manager();

  // This message can be received before navigation commit, because the renderer
  // requests this when initializing the main frame of `blink::WebView` which
  // happens before commit. Use FrameTree::is_prerendering() rather than
  // lifecycle_state() because prerendering lifecycle state only is set after
  // navigation commit.
  bool restricted_mode = frame_tree()->is_prerendering();
  if (restricted_mode) {
    audio_service_audio_output_stream_factory_.emplace(
        this, audio_system, media_stream_manager, std::move(receiver),
        base::BindOnce(
            base::IgnoreResult(&RenderFrameHostImpl::CancelPrerendering),
            base::Unretained(this),
            PrerenderCancellationReason(
                PrerenderFinalStatus::kAudioOutputDeviceRequested)));
  } else {
    audio_service_audio_output_stream_factory_.emplace(
        this, audio_system, media_stream_manager, std::move(receiver),
        /*restricted_callback=*/absl::nullopt);
  }
}

void RenderFrameHostImpl::GetFeatureObserver(
    mojo::PendingReceiver<blink::mojom::FeatureObserver> receiver) {
  if (!feature_observer_) {
    // Lazy initialize because tests sets the overridden content client
    // after the RFHI constructor.
    auto* client = GetContentClient()->browser()->GetFeatureObserverClient();
    if (!client)
      return;
    feature_observer_ = std::make_unique<FeatureObserver>(
        client, GlobalRenderFrameHostId(GetProcess()->GetID(), routing_id_));
  }
  feature_observer_->GetFeatureObserver(std::move(receiver));
}

void RenderFrameHostImpl::BindRenderAccessibilityHost(
    mojo::PendingReceiver<blink::mojom::RenderAccessibilityHost> receiver) {
  // There must be an accessibility token as
  // RenderAccessibilityImpl::ScheduleSendPendingAccessibilityEvents will only
  // attempt to send updates once it has created one, which happens as part of
  // the commit which in turns updates the browser's token before this method
  // could be called.
  DCHECK(GetAXTreeID().token());
  // `render_accessibility_host_` is reset in `TearDownMojoConnection()`, but
  // this Mojo endpoint lives on another sequence and posts tasks back to this
  // `RenderFrameHostImpl` on the UI thread. After the reset, there may still be
  // tasks in flight: use `render_frame_scoped_weak_ptr_factory_` to ensure
  // those tasks are dropped if they arrive after the reset of their
  // corresponding RenderAccessibilityHost.
  ui::AXTreeID ax_tree_id = GetAXTreeID();
  if (!render_accessibility_host_ ||
      ax_tree_id != render_accessibility_host_ax_tree_id_) {
    render_accessibility_host_ = base::SequenceBound<RenderAccessibilityHost>(
        base::FeatureList::IsEnabled(
            features::kRenderAccessibilityHostDeserializationOffMainThread)
            ? base::ThreadPool::CreateSequencedTaskRunner({})
            : base::SequencedTaskRunner::GetCurrentDefault(),
        render_frame_scoped_weak_ptr_factory_.GetWeakPtr(), ax_tree_id);
  }
  render_accessibility_host_ax_tree_id_ = ax_tree_id;
  render_accessibility_host_.AsyncCall(&RenderAccessibilityHost::Bind)
      .WithArgs(std::move(receiver));
}

void RenderFrameHostImpl::BindNonAssociatedLocalFrameHost(
    mojo::PendingReceiver<blink::mojom::NonAssociatedLocalFrameHost> receiver) {
  non_associated_local_frame_host_receiver_.Bind(std::move(receiver));
}

bool RenderFrameHostImpl::CancelPrerendering(
    const PrerenderCancellationReason& reason) {
  // A prerendered page is identified by its root FrameTreeNode id, so if this
  // RenderFrameHost is in any way embedded, we need to iterate up to the
  // prerender root.
  FrameTreeNode* outermost_frame =
      GetOutermostMainFrameOrEmbedder()->frame_tree_node();

  // We need to explicitly check that `outermost_frame` is in a prerendering
  // frame tree before accessing `GetPrerenderHostRegistry()`. Non-prerendered
  // frames may outlive the PrerenderHostRegistry during WebContents
  // destruction.
  if (outermost_frame->GetFrameType() != FrameType::kPrerenderMainFrame)
    return false;

  return delegate_->GetPrerenderHostRegistry()->CancelHost(
      outermost_frame->frame_tree_node_id(), reason);
}

void RenderFrameHostImpl::CancelPrerenderingByMojoBinderPolicy(
    const std::string& interface_name) {
  // A prerendered page is identified by its root FrameTreeNode id, so if this
  // RenderFrameHost is in any way embedded, we need to iterate up to the
  // prerender root.
  FrameTreeNode* outermost_frame =
      GetOutermostMainFrameOrEmbedder()->frame_tree_node();
  PrerenderHost* prerender_host =
      delegate_->GetPrerenderHostRegistry()->FindNonReservedHostById(
          outermost_frame->frame_tree_node_id());
  if (!prerender_host)
    return;

  bool canceled = CancelPrerendering(
      PrerenderCancellationReason::BuildForMojoBinderPolicy(interface_name));
  // This function is called from MojoBinderPolicyApplier, which should only be
  // active during prerendering. It would be an error to call this while not
  // prerendering, as it could mean an interface request is never resolved for
  // an active page.
  DCHECK(canceled);
}

void RenderFrameHostImpl::RendererWillActivateForPrerendering() {
  if (audio_service_audio_output_stream_factory_) {
    audio_service_audio_output_stream_factory_->ReleaseRestriction();
  }
  // Loosen the policies of the Mojo capability control during dispatching the
  // prerenderingchange event in Blink, because the page may start legitimately
  // using controlled interfaces once prerenderingchange is dispatched. We
  // cannot release policies at this point, i.e., we cannot run the deferred
  // binders, because the Mojo message pipes are not channel-associated and we
  // should ensure that ActivateForPrerendering() arrives on the renderer
  // earlier than these deferred messages.
  DCHECK(mojo_binder_policy_applier_)
      << "prerendering pages should have a policy applier";
  mojo_binder_policy_applier_->PrepareToGrantAll();
}

void RenderFrameHostImpl::BindMediaInterfaceFactoryReceiver(
    mojo::PendingReceiver<media::mojom::InterfaceFactory> receiver) {
  MediaInterfaceProxy::GetOrCreateForCurrentDocument(this)->Bind(
      std::move(receiver));
}

void RenderFrameHostImpl::BindMediaMetricsProviderReceiver(
    mojo::PendingReceiver<media::mojom::MediaMetricsProvider> receiver) {
  // Only save decode stats when BrowserContext provides a VideoPerfHistory.
  // Off-the-record contexts will internally use an ephemeral history DB.
  media::VideoDecodePerfHistory::SaveCallback save_stats_cb;
  if (GetSiteInstance()->GetBrowserContext()->GetVideoDecodePerfHistory()) {
    save_stats_cb = GetSiteInstance()
                        ->GetBrowserContext()
                        ->GetVideoDecodePerfHistory()
                        ->GetSaveCallback();
  }

  auto is_shutting_down_cb = base::BindRepeating(
      []() { return GetContentClient()->browser()->IsShuttingDown(); });

  media::MediaMetricsProvider::Create(
      GetProcess()->GetBrowserContext()->IsOffTheRecord()
          ? media::MediaMetricsProvider::BrowsingMode::kIncognito
          : media::MediaMetricsProvider::BrowsingMode::kNormal,
      IsOutermostMainFrame()
          ? media::MediaMetricsProvider::FrameStatus::kTopFrame
          : media::MediaMetricsProvider::FrameStatus::kNotTopFrame,
      GetPage().last_main_document_source_id(),
      media::learning::FeatureValue(GetLastCommittedOrigin().host()),
      std::move(save_stats_cb),
      base::BindRepeating(
          [](base::WeakPtr<RenderFrameHostImpl> frame)
              -> media::learning::LearningSession* {
            if (!base::FeatureList::IsEnabled(media::kMediaLearningFramework) ||
                !frame) {
              return nullptr;
            }

            return frame->GetProcess()
                ->GetBrowserContext()
                ->GetLearningSession();
          },
          weak_ptr_factory_.GetWeakPtr()),
      base::BindRepeating(
          &RenderFrameHostImpl::GetRecordAggregateWatchTimeCallback,
          base::Unretained(this)),
      std::move(is_shutting_down_cb), std::move(receiver));
}

#if BUILDFLAG(ENABLE_MEDIA_REMOTING)
void RenderFrameHostImpl::BindMediaRemoterFactoryReceiver(
    mojo::PendingReceiver<media::mojom::RemoterFactory> receiver) {
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<RemoterFactoryImpl>(GetProcess()->GetID(), routing_id_),
      std::move(receiver));
}
#endif

void RenderFrameHostImpl::CreateWebSocketConnector(
    mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver) {
  mojo::MakeSelfOwnedReceiver(std::make_unique<WebSocketConnectorImpl>(
                                  GetProcess()->GetID(), routing_id_,
                                  last_committed_origin_, isolation_info_),
                              std::move(receiver));
}

void RenderFrameHostImpl::CreateWebTransportConnector(
    mojo::PendingReceiver<blink::mojom::WebTransportConnector> receiver) {
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<WebTransportConnectorImpl>(
          GetProcess()->GetID(), weak_ptr_factory_.GetWeakPtr(),
          last_committed_origin_, isolation_info_.network_anonymization_key()),
      std::move(receiver));
}

void RenderFrameHostImpl::CreateNotificationService(
    mojo::PendingReceiver<blink::mojom::NotificationService> receiver) {
  GetProcess()->CreateNotificationService(
      GetGlobalId(),
      RenderProcessHost::NotificationServiceCreatorType::kDocument,
      storage_key(), std::move(receiver));
}

void RenderFrameHostImpl::CreateInstalledAppProvider(
    mojo::PendingReceiver<blink::mojom::InstalledAppProvider> receiver) {
  InstalledAppProviderImpl::Create(*this, std::move(receiver));
}

void RenderFrameHostImpl::CreateCodeCacheHostWithKeys(
    mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver,
    const net::NetworkIsolationKey& nik,
    const blink::StorageKey& storage_key) {
  // Create a new CodeCacheHostImpl and bind it to the given receiver.
  code_cache_host_receivers_.Add(GetProcess()->GetID(), nik, storage_key,
                                 std::move(receiver),
                                 GetCodeCacheHostReceiverHandler());
}

void RenderFrameHostImpl::CreateCodeCacheHost(
    mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver) {
  CreateCodeCacheHostWithKeys(std::move(receiver), GetNetworkIsolationKey(),
                              storage_key());
}

void RenderFrameHostImpl::CreateDedicatedWorkerHostFactory(
    mojo::PendingReceiver<blink::mojom::DedicatedWorkerHostFactory> receiver) {
  // Allocate the worker in the same process as the creator.
  int worker_process_id = GetProcess()->GetID();

  base::WeakPtr<CrossOriginEmbedderPolicyReporter> coep_reporter;
  if (coep_reporter_) {
    coep_reporter = coep_reporter_->GetWeakPtr();
  }

  // When a dedicated worker is created from the frame script, the frame is both
  // the creator and the ancestor.
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<DedicatedWorkerHostFactoryImpl>(
          worker_process_id,
          /*creator_render_frame_host_id=*/GetGlobalId(),
          /*creator_worker_token=*/absl::nullopt,
          /*ancestor_render_frame_host_id=*/GetGlobalId(), storage_key(),
          isolation_info_, BuildClientSecurityState(),
          /*creator_coep_reporter=*/coep_reporter,
          /*ancestor_coep_reporter=*/coep_reporter),
      std::move(receiver));
}

#if BUILDFLAG(IS_ANDROID)
void RenderFrameHostImpl::BindNFCReceiver(
    mojo::PendingReceiver<device::mojom::NFC> receiver) {
  delegate_->GetNFC(this, std::move(receiver));
}
#endif

#if !BUILDFLAG(IS_ANDROID)
void RenderFrameHostImpl::BindSerialService(
    mojo::PendingReceiver<blink::mojom::SerialService> receiver) {
  if (!IsFeatureEnabled(blink::mojom::PermissionsPolicyFeature::kSerial)) {
    mojo::ReportBadMessage("Permissions policy blocks access to Serial.");
    return;
  }

  // Powerful features like Serial API for FencedFrames are blocked by
  // PermissionsPolicy. But as the interface is still exposed to the renderer,
  // still good to have a secondary check per-API basis to handle compromised
  // renderers. Ignore the request and mark it as bad to kill the initiating
  // renderer if it happened for some reason.
  if (IsNestedWithinFencedFrame()) {
    mojo::ReportBadMessage("Web Serial is not allowed in fences frames.");
    return;
  }

  // Rejects using Serial API when the top-level document has an opaque origin.
  if (GetOutermostMainFrame()->GetLastCommittedOrigin().opaque()) {
    mojo::ReportBadMessage(
        "Web Serial is not allowed when the top-level document has an opaque "
        "origin.");
    return;
  }

  SerialService::GetOrCreateForCurrentDocument(this)->Bind(std::move(receiver));
}

void RenderFrameHostImpl::GetHidService(
    mojo::PendingReceiver<blink::mojom::HidService> receiver) {
  HidService::Create(this, std::move(receiver));
}
#endif

#if BUILDFLAG(IS_CHROMEOS)
void RenderFrameHostImpl::GetSmartCardService(
    mojo::PendingReceiver<blink::mojom::SmartCardService> receiver) {
  SmartCardService::Create(this, std::move(receiver));
}
#endif

IdleManagerImpl* RenderFrameHostImpl::GetIdleManager() {
  return idle_manager_.get();
}

void RenderFrameHostImpl::BindIdleManager(
    mojo::PendingReceiver<blink::mojom::IdleManager> receiver) {
  if (!IsFeatureEnabled(
          blink::mojom::PermissionsPolicyFeature::kIdleDetection)) {
    mojo::ReportBadMessage(
        "Permissions policy blocks access to IdleDetection.");
    return;
  }

  idle_manager_->CreateService(std::move(receiver));
  OnBackForwardCacheDisablingStickyFeatureUsed(
      BackForwardCacheDisablingFeature::kIdleManager);
}

void RenderFrameHostImpl::GetPresentationService(
    mojo::PendingReceiver<blink::mojom::PresentationService> receiver) {
  if (!presentation_service_)
    presentation_service_ = PresentationServiceImpl::Create(this);
  presentation_service_->Bind(std::move(receiver));
}

PresentationServiceImpl&
RenderFrameHostImpl::GetPresentationServiceForTesting() {
  DCHECK(presentation_service_);
  return *presentation_service_.get();
}

void RenderFrameHostImpl::GetSpeechSynthesis(
    mojo::PendingReceiver<blink::mojom::SpeechSynthesis> receiver) {
  if (!speech_synthesis_impl_) {
    speech_synthesis_impl_ = std::make_unique<SpeechSynthesisImpl>(
        GetProcess()->GetBrowserContext(), this);
  }
  speech_synthesis_impl_->AddReceiver(std::move(receiver));

  if (!base::FeatureList::IsEnabled(
          features::kUnblockSpeechSynthesisForBFCache)) {
    // Blocklist SpeechSynthesis for BackForwardCache when the flag is off.
    OnBackForwardCacheDisablingFeatureUsed(
        BackForwardCacheDisablingFeature::kSpeechSynthesis);
  }
}

void RenderFrameHostImpl::GetSensorProvider(
    mojo::PendingReceiver<device::mojom::SensorProvider> receiver) {
  SensorProviderProxyImpl::GetOrCreateForCurrentDocument(this)->Bind(
      std::move(receiver));
}

void RenderFrameHostImpl::BindCacheStorage(
    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
  BindCacheStorageInternal(
      std::move(receiver),
      storage::BucketLocator::ForDefaultBucket(storage_key()));
}

void RenderFrameHostImpl::BindCacheStorageInternal(
    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver,
    const storage::BucketLocator& bucket_locator) {
  mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
      coep_reporter_remote;
  if (coep_reporter_) {
    coep_reporter_->Clone(
        coep_reporter_remote.InitWithNewPipeAndPassReceiver());
  }
  GetProcess()->BindCacheStorage(cross_origin_embedder_policy(),
                                 std::move(coep_reporter_remote),
                                 bucket_locator, std::move(receiver));
}

void RenderFrameHostImpl::BindInputInjectorReceiver(
    mojo::PendingReceiver<mojom::InputInjector> receiver) {
  InputInjectorImpl::Create(weak_ptr_factory_.GetWeakPtr(),
                            std::move(receiver));
}

void RenderFrameHostImpl::BindWebOTPServiceReceiver(
    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver) {
  auto* fetcher = SmsFetcher::Get(GetProcess()->GetBrowserContext());
  if (WebOTPService::Create(fetcher, this, std::move(receiver)))
    document_used_web_otp_ = true;
}

void RenderFrameHostImpl::BindFederatedAuthRequestReceiver(
    mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver) {
  FederatedAuthRequestImpl::Create(this, std::move(receiver));
}

void RenderFrameHostImpl::BindRestrictedCookieManager(
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) {
  BindRestrictedCookieManagerWithOrigin(
      std::move(receiver), GetIsolationInfoForSubresources(),
      GetLastCommittedOrigin(), GetCookieSettingOverrides());
}

void RenderFrameHostImpl::BindRestrictedCookieManagerWithOrigin(
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver,
    const net::IsolationInfo& isolation_info,
    const url::Origin& origin,
    net::CookieSettingOverrides cookie_setting_overrides) {
  // CookieSettingOverrides is passesd in instead of calling
  // GetCookieSettingOverrides, because this call can happen before the frame
  // is committed.
  GetStoragePartition()->CreateRestrictedCookieManager(
      network::mojom::RestrictedCookieManagerRole::SCRIPT, origin,
      isolation_info,
      /*is_service_worker=*/false, GetProcess()->GetID(), GetRoutingID(),
      cookie_setting_overrides, std::move(receiver),
      CreateCookieAccessObserver());
}

void RenderFrameHostImpl::BindTrustTokenQueryAnswerer(
    mojo::PendingReceiver<network::mojom::TrustTokenQueryAnswerer> receiver) {
  auto top_frame_origin = ComputeTopFrameOrigin(GetLastCommittedOrigin());

  // A check at the callsite in the renderer ensures a correctly functioning
  // renderer will only request this Mojo handle if the top-frame origin is
  // potentially trustworthy and has scheme HTTP or HTTPS.
  if ((top_frame_origin.scheme() != url::kHttpScheme &&
       top_frame_origin.scheme() != url::kHttpsScheme) ||
      !network::IsOriginPotentiallyTrustworthy(top_frame_origin)) {
    mojo::ReportBadMessage(
        "Attempted to get a TrustTokenQueryAnswerer for a non-trustworthy or "
        "non-HTTP/HTTPS top-frame origin.");
    return;
  }

  // Both flags are enforced in benign renderers by the
  // RuntimeEnabled=PrivateStateTokens IDL attribute (the base::Feature's value
  // is tied to the RuntimeEnabledFeature's).
  if (!base::FeatureList::IsEnabled(network::features::kPrivateStateTokens) &&
      !base::FeatureList::IsEnabled(network::features::kFledgePst)) {
    mojo::ReportBadMessage(
        "Attempted to get a TrustTokenQueryAnswerer with Private State Tokens "
        "disabled.");
    return;
  }

  // TODO(crbug.com/1145346): Document.hasPrivateToken is restricted to
  // secure contexts, so we could additionally add a check verifying that the
  // bind request "is coming from a secure context"---but there's currently no
  // direct way to perform such a check in the browser.
  GetProcess()->GetStoragePartition()->CreateTrustTokenQueryAnswerer(
      std::move(receiver), ComputeTopFrameOrigin(GetLastCommittedOrigin()));
}

void RenderFrameHostImpl::GetAudioContextManager(
    mojo::PendingReceiver<blink::mojom::AudioContextManager> receiver) {
  AudioContextManagerImpl::Create(this, std::move(receiver));
}

void RenderFrameHostImpl::GetFileSystemManager(
    mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) {
  // This is safe because file_system_manager_ is deleted on the IO thread
  GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(&FileSystemManagerImpl::BindReceiver,
                                base::Unretained(file_system_manager_.get()),
                                storage_key(), std::move(receiver)));
}

void RenderFrameHostImpl::GetGeolocationService(
    mojo::PendingReceiver<blink::mojom::GeolocationService> receiver) {
  if (!geolocation_service_) {
    auto* geolocation_context = delegate_->GetGeolocationContext();
    if (!geolocation_context)
      return;
    geolocation_service_ =
        std::make_unique<GeolocationServiceImpl>(geolocation_context, this);
  }
  geolocation_service_->Bind(std::move(receiver));
}

void RenderFrameHostImpl::GetPendingBeaconHost(
    mojo::PendingReceiver<blink::mojom::PendingBeaconHost> receiver) {
  PendingBeaconHost::CreateForCurrentDocument(
      this, GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess(),
      PendingBeaconService::GetInstance());
  PendingBeaconHost* pbh = PendingBeaconHost::GetForCurrentDocument(this);
  pbh->SetReceiver(std::move(receiver));
}

void RenderFrameHostImpl::GetDeviceInfoService(
    mojo::PendingReceiver<blink::mojom::DeviceAPIService> receiver) {
  GetContentClient()->browser()->CreateDeviceInfoService(this,
                                                         std::move(receiver));
}

void RenderFrameHostImpl::GetManagedConfigurationService(
    mojo::PendingReceiver<blink::mojom::ManagedConfigurationService> receiver) {
  GetContentClient()->browser()->CreateManagedConfigurationService(
      this, std::move(receiver));
}

void RenderFrameHostImpl::GetFontAccessManager(
    mojo::PendingReceiver<blink::mojom::FontAccessManager> receiver) {
  GetStoragePartition()->GetFontAccessManager()->BindReceiver(
      GetGlobalId(), std::move(receiver));
}

void RenderFrameHostImpl::GetFileSystemAccessManager(
    mojo::PendingReceiver<blink::mojom::FileSystemAccessManager> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  auto* manager = GetStoragePartition()->GetFileSystemAccessManager();
  manager->BindReceiver(
      FileSystemAccessManagerImpl::BindingContext(
          storage_key(), GetLastCommittedURL(), GetGlobalId()),
      std::move(receiver));
}

void RenderFrameHostImpl::CreateLockManager(
    mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
  GetProcess()->CreateLockManager(storage_key(), std::move(receiver));
}

void RenderFrameHostImpl::CreateIDBFactory(
    mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) {
  GetProcess()->BindIndexedDB(storage_key(), GetGlobalId(),
                              std::move(receiver));
}

void RenderFrameHostImpl::CreateBucketManagerHost(
    mojo::PendingReceiver<blink::mojom::BucketManagerHost> receiver) {
  GetProcess()->BindBucketManagerHost(weak_ptr_factory_.GetWeakPtr(),
                                      std::move(receiver));
}

void RenderFrameHostImpl::CreatePermissionService(
    mojo::PendingReceiver<blink::mojom::PermissionService> receiver) {
  PermissionServiceContext::GetOrCreateForCurrentDocument(this)->CreateService(
      std::move(receiver));
}

void RenderFrameHostImpl::GetWebAuthenticationService(
    mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
#if !BUILDFLAG(IS_ANDROID)
  AuthenticatorImpl::Create(this, std::move(receiver));
#else
  GetJavaInterfaces()->GetInterface(std::move(receiver));
#endif  // !BUILDFLAG(IS_ANDROID)
}

void RenderFrameHostImpl::GetPushMessaging(
    mojo::PendingReceiver<blink::mojom::PushMessaging> receiver) {
  if (!push_messaging_manager_) {
    auto* rph = GetProcess();
    push_messaging_manager_ = std::make_unique<PushMessagingManager>(
        *rph, routing_id_,
        base::WrapRefCounted(GetStoragePartition()->GetServiceWorkerContext()));
  }

  push_messaging_manager_->AddPushMessagingReceiver(std::move(receiver));
}

void RenderFrameHostImpl::GetVirtualAuthenticatorManager(
    mojo::PendingReceiver<blink::test::mojom::VirtualAuthenticatorManager>
        receiver) {
#if !BUILDFLAG(IS_ANDROID)
  // VirtualAuthenticatorManagerImpl is enabled at the frame level. Inactive
  // document are detached. They don't have a frame anymore, so they can't be
  // used to enable this test-only feature.
  if (!IsActive()) {
    return;
  }

  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableWebAuthDeprecatedMojoTestingApi)) {
    CHECK(owner_);
    owner_->GetVirtualAuthenticatorManager(std::move(receiver));
  }
#endif  // !BUILDFLAG(IS_ANDROID)
}

bool IsInitialSynchronousAboutBlankCommit(const GURL& url,
                                          bool is_initial_empty_document) {
  return url.SchemeIs(url::kAboutScheme) && url != GURL(url::kAboutSrcdocURL) &&
         is_initial_empty_document;
}

std::unique_ptr<NavigationRequest>
RenderFrameHostImpl::CreateNavigationRequestForSynchronousRendererCommit(
    const GURL& url,
    const url::Origin& origin,
    const absl::optional<GURL>& initiator_base_url,
    blink::mojom::ReferrerPtr referrer,
    const ui::PageTransition& transition,
    bool should_replace_current_entry,
    bool has_user_gesture,
    const std::vector<GURL>& redirects,
    const GURL& original_request_url,
    bool is_same_document,
    bool is_same_document_history_api_navigation) {
  // This function must only be called when there are no NavigationRequests for
  // a navigation can be found at DidCommit time, which can only happen in two
  // cases:
  // 1) This was a synchronous renderer-initiated navigation to about:blank
  // after the initial empty document.
  // 2) This was a renderer-initiated same-document navigation.
  DCHECK(IsInitialSynchronousAboutBlankCommit(
             url, frame_tree_node_->is_on_initial_empty_document()) ||
         is_same_document);
  DCHECK(!is_same_document_history_api_navigation || is_same_document);
  DCHECK(!IsPendingDeletion());     // IPC is filtered out by the caller.
  DCHECK(!IsInBackForwardCache());  // A page in the BackForwardCache is fully
                                    // loaded and has no pending navigations.
  // See `owner_` invariants about IsPendingDeletion() and
  // IsInBackForwardCache().
  CHECK(owner_);

  net::IsolationInfo isolation_info = ComputeIsolationInfoInternal(
      origin, net::IsolationInfo::RequestType::kOther, IsCredentialless(),
      /*fenced_frame_nonce_for_navigation=*/absl::nullopt);

  std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter;
  // We don't switch the COEP reporter on same-document navigations, so create
  // one only for cross-document navigations.
  if (!is_same_document) {
    auto* storage_partition =
        static_cast<StoragePartitionImpl*>(GetProcess()->GetStoragePartition());
    coep_reporter = std::make_unique<CrossOriginEmbedderPolicyReporter>(
        storage_partition->GetWeakPtr(), url,
        cross_origin_embedder_policy().reporting_endpoint,
        cross_origin_embedder_policy().report_only_reporting_endpoint,
        GetReportingSource(), isolation_info.network_anonymization_key());
  }

  std::unique_ptr<SubresourceWebBundleNavigationInfo>
      subresource_web_bundle_navigation_info;
  if (is_same_document && subresource_web_bundle_navigation_info_) {
    // Propagate |subresource_web_bundle_navigation_info_| to NavigationRequest
    // for same-document navigation. This will be passed to
    // FrameNavigationEntry, and will be used for subsequent history
    // navigations.
    subresource_web_bundle_navigation_info =
        subresource_web_bundle_navigation_info_->Clone();
  }

  std::string method = "GET";
  if (is_same_document && !is_same_document_history_api_navigation) {
    // Preserve the HTTP method used by the last navigation if this is a
    // same-document navigation that is not triggered by the history API
    // (history.replaceState/pushState). See spec:
    // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
    method = last_http_method_;
  }

  // HTTP status code:
  // - For same-document navigations, we should retain the HTTP status code from
  // the last committed navigation.
  // - For initial about:blank navigation, the HTTP status code is 0.
  int http_status_code = is_same_document ? last_http_status_code_ : 0;

  // Same-document navigation should retain is_overriding_user_agent from the
  // last committed navigation.
  bool is_overriding_user_agent = is_same_document && is_overriding_user_agent_;

  return owner_->CreateNavigationRequestForSynchronousRendererCommit(
      this, is_same_document, url, origin, initiator_base_url, isolation_info,
      std::move(referrer), transition, should_replace_current_entry, method,
      has_user_gesture, is_overriding_user_agent, redirects,
      original_request_url, std::move(coep_reporter),
      std::move(subresource_web_bundle_navigation_info), http_status_code);
}

void RenderFrameHostImpl::BeforeUnloadTimeout() {
  if (delegate_->ShouldIgnoreUnresponsiveRenderer()) {
    return;
  }

  SimulateBeforeUnloadCompleted(/*proceed=*/true);
}

void RenderFrameHostImpl::SetLastCommittedSiteInfo(const UrlInfo& url_info) {
  BrowserContext* browser_context = GetSiteInstance()->GetBrowserContext();
  SiteInfo site_info =
      url_info.url.is_empty()
          ? SiteInfo(browser_context)
          : SiteInfo::Create(GetSiteInstance()->GetIsolationContext(),
                             url_info);

  if (last_committed_site_info_ == site_info)
    return;

  if (!last_committed_site_info_.site_url().is_empty()) {
    RenderProcessHostImpl::RemoveFrameWithSite(browser_context, GetProcess(),
                                               last_committed_site_info_);
  }

  last_committed_site_info_ = site_info;

  if (!last_committed_site_info_.site_url().is_empty()) {
    RenderProcessHostImpl::AddFrameWithSite(browser_context, GetProcess(),
                                            last_committed_site_info_);
  }
}

#if BUILDFLAG(IS_ANDROID)
base::android::ScopedJavaLocalRef<jobject>
RenderFrameHostImpl::GetJavaRenderFrameHost() {
  RenderFrameHostAndroid* render_frame_host_android =
      static_cast<RenderFrameHostAndroid*>(
          GetUserData(kRenderFrameHostAndroidKey));
  if (!render_frame_host_android) {
    render_frame_host_android = new RenderFrameHostAndroid(this);
    SetUserData(kRenderFrameHostAndroidKey,
                base::WrapUnique(render_frame_host_android));
  }
  return render_frame_host_android->GetJavaObject();
}

service_manager::InterfaceProvider* RenderFrameHostImpl::GetJavaInterfaces() {
  if (!java_interfaces_) {
    mojo::PendingRemote<service_manager::mojom::InterfaceProvider> provider;
    BindInterfaceRegistryForRenderFrameHost(
        provider.InitWithNewPipeAndPassReceiver(), this);
    java_interfaces_ = std::make_unique<service_manager::InterfaceProvider>(
        base::SingleThreadTaskRunner::GetCurrentDefault());
    java_interfaces_->Bind(std::move(provider));
  }
  return java_interfaces_.get();
}
#endif

void RenderFrameHostImpl::ForEachImmediateLocalRoot(
    base::FunctionRef<void(RenderFrameHostImpl*)> func_ref) {
  ForEachRenderFrameHostWithAction([func_ref, this](RenderFrameHostImpl* rfh) {
    if (rfh->is_local_root() && rfh != this) {
      func_ref(rfh);
      return FrameIterationAction::kSkipChildren;
    }
    return FrameIterationAction::kContinue;
  });
}

void RenderFrameHostImpl::SetVisibilityForChildViews(bool visible) {
  ForEachImmediateLocalRoot([visible](RenderFrameHostImpl* frame_host) {
    if (auto* view = frame_host->GetView())
      return visible ? view->Show() : view->Hide();
  });
}

mojom::Frame* RenderFrameHostImpl::GetMojomFrameInRenderer() {
  DCHECK(frame_);
  return frame_.get();
}

bool RenderFrameHostImpl::ShouldBypassSecurityChecksForErrorPage(
    NavigationRequest* navigation_request,
    bool* should_commit_error_page) {
  if (should_commit_error_page)
    *should_commit_error_page = false;

  if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(is_main_frame())) {
    if (GetSiteInstance()->GetSiteInfo().is_error_page()) {
      if (should_commit_error_page)
        *should_commit_error_page = true;

      // With error page isolation, any URL can commit in an error page process.
      return true;
    }
  } else {
    // Without error page isolation, a blocked navigation is expected to
    // commit in the old renderer process.  This may be true for subframe
    // navigations even when error page isolation is enabled for main frames.
    if (navigation_request &&
        net::IsRequestBlockedError(navigation_request->GetNetErrorCode())) {
      return true;
    }
  }

  return false;
}

void RenderFrameHostImpl::SetAudioOutputDeviceIdForGlobalMediaControls(
    std::string hashed_device_id) {
  audio_service_audio_output_stream_factory_
      ->SetAuthorizedDeviceIdForGlobalMediaControls(
          std::move(hashed_device_id));
}

std::unique_ptr<mojo::MessageFilter>
RenderFrameHostImpl::CreateMessageFilterForAssociatedReceiver(
    const char* interface_name) {
  return CreateMessageFilterForAssociatedReceiverInternal(
      interface_name,
      BackForwardCacheImpl::GetChannelAssociatedMessageHandlingPolicy());
}

network::mojom::ClientSecurityStatePtr
RenderFrameHostImpl::BuildClientSecurityState() const {
  // TODO(https://crbug.com/1184150) Remove this bandaid.
  //
  // Due to a race condition, CreateCrossOriginPrefetchLoaderFactoryBundle() is
  // sometimes called on the previous document, before the new document is
  // committed. In that case, it mistakenly builds a client security state
  // based on the policies of the previous document. If no document has ever
  // committed, there is no PolicyContainerHost to source policies from. To
  // avoid crashes, this returns a maximally-restrictive value instead.
  if (!policy_container_host_) {
    // Prevent other code paths from depending on this bandaid.
    DCHECK_EQ(lifecycle_state_, LifecycleStateImpl::kSpeculative);

    // Omitted: reporting endpoint, report-only value and reporting endpoint.
    network::CrossOriginEmbedderPolicy coep;
    coep.value = network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;

    return network::mojom::ClientSecurityState::New(
        std::move(coep),
        /*is_web_secure_context=*/false,
        network::mojom::IPAddressSpace::kUnknown,
        network::mojom::LocalNetworkRequestPolicy::kBlock);
  }

  auto client_security_state = network::mojom::ClientSecurityState::New();

  const PolicyContainerPolicies& policies = policy_container_host_->policies();
  client_security_state->is_web_secure_context = policies.is_web_secure_context;
  client_security_state->ip_address_space = policies.ip_address_space;

  client_security_state->local_network_request_policy =
      local_network_request_policy_;
  client_security_state->cross_origin_embedder_policy =
      policies.cross_origin_embedder_policy;

  return client_security_state;
}

network::mojom::ClientSecurityStatePtr
RenderFrameHostImpl::BuildClientSecurityStateForWorkers() const {
  auto client_security_state = BuildClientSecurityState();

  client_security_state->local_network_request_policy =
      DerivePrivateNetworkRequestPolicy(
          client_security_state->ip_address_space,
          client_security_state->is_web_secure_context,
          PrivateNetworkRequestContext::kWorker);

  return client_security_state;
}

bool RenderFrameHostImpl::IsNavigationSameSite(
    const UrlInfo& dest_url_info) const {
  if (!WebExposedIsolationInfo::AreCompatible(
          GetSiteInstance()->GetWebExposedIsolationInfo(),
          dest_url_info.web_exposed_isolation_info)) {
    return false;
  }
  return GetSiteInstance()->IsNavigationSameSite(
      last_successful_url(), GetLastCommittedOrigin(), IsOutermostMainFrame(),
      dest_url_info);
}

bool RenderFrameHostImpl::ValidateDidCommitParams(
    NavigationRequest* navigation_request,
    mojom::DidCommitProvisionalLoadParams* params,
    bool is_same_document_navigation) {
  DCHECK(params);
  RenderProcessHost* process = GetProcess();

  // Error pages may sometimes commit a URL in the wrong process, which requires
  // an exception for the CanCommitOriginAndUrl() checks.  This is ok as long
  // as the origin is opaque.
  bool should_commit_error_page = false;
  bool bypass_checks_for_error_page = ShouldBypassSecurityChecksForErrorPage(
      navigation_request, &should_commit_error_page);

  // Commits in the error page process must only be failures, otherwise
  // successful navigations could commit documents from origins different
  // than the chrome-error://chromewebdata/ one and violate expectations.
  if (should_commit_error_page &&
      (navigation_request && !navigation_request->DidEncounterError())) {
    DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, params->origin);
    bad_message::ReceivedBadMessage(
        process, bad_message::RFH_ERROR_PROCESS_NON_ERROR_COMMIT);
    return false;
  }

  // Error pages must commit in a opaque origin. Terminate the renderer
  // process if this is violated.
  if (bypass_checks_for_error_page && !params->origin.opaque()) {
    DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, params->origin);
    bad_message::ReceivedBadMessage(
        process, bad_message::RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT);
    return false;
  }

  if (!bypass_checks_for_error_page &&
      !ValidateURLAndOrigin(params->url, params->origin,
                            is_same_document_navigation, navigation_request)) {
    return false;
  }

  // Without this check, an evil renderer can trick the browser into creating
  // a navigation entry for a banned URL.  If the user clicks the back button
  // followed by the forward button (or clicks reload, or round-trips through
  // session restore, etc), we'll think that the browser commanded the
  // renderer to load the URL and grant the renderer the privileges to request
  // the URL.  To prevent this attack, we block the renderer from inserting
  // banned URLs into the navigation controller in the first place.
  process->FilterURL(false, &params->url);
  process->FilterURL(true, &params->referrer->url);

  // Without this check, the renderer can trick the browser into using
  // filenames it can't access in a future session restore.
  if (!CanAccessFilesOfPageState(params->page_state)) {
    bad_message::ReceivedBadMessage(
        process, bad_message::RFH_CAN_ACCESS_FILES_OF_PAGE_STATE);
    return false;
  }

  // A cross-document navigation requires an embedding token. Navigations
  // activating an existing document do not require new embedding tokens as the
  // token is already set.
  bool is_page_activation =
      navigation_request && navigation_request->IsPageActivation();
  DCHECK(!is_page_activation || embedding_token_.has_value());
  if (!is_page_activation) {
    if (!is_same_document_navigation && !params->embedding_token.has_value()) {
      bad_message::ReceivedBadMessage(process,
                                      bad_message::RFH_MISSING_EMBEDDING_TOKEN);
      return false;
    } else if (is_same_document_navigation &&
               params->embedding_token.has_value()) {
      bad_message::ReceivedBadMessage(
          process, bad_message::RFH_UNEXPECTED_EMBEDDING_TOKEN);
      return false;
    }
  }

  // Note: document_policy_header is the document policy state used to
  // initialize |document_policy_| in SecurityContext on renderer side. It is
  // supposed to be compatible with required_document_policy. If not, kill the
  // renderer.
  if (!blink::DocumentPolicy::IsPolicyCompatible(
          browsing_context_state_->effective_frame_policy()
              .required_document_policy,
          params->document_policy_header)) {
    bad_message::ReceivedBadMessage(
        process, bad_message::RFH_BAD_DOCUMENT_POLICY_HEADER);
    return false;
  }

  // If a frame claims the navigation was same-document, it must be the current
  // frame, not a pending one.
  if (is_same_document_navigation &&
      lifecycle_state() == LifecycleStateImpl::kPendingCommit) {
    bad_message::ReceivedBadMessage(process,
                                    bad_message::NI_IN_PAGE_NAVIGATION);
    return false;
  }

  // Same document navigations should not be possible on post-commit error pages
  // and would leave the NavigationController in a weird state. Kill the
  // renderer before getting to NavigationController::RendererDidNavigate if
  // that happens.
  if (is_same_document_navigation) {
    // `owner_` must exist, because `DidCommitSameDocumentNavigation()` returns
    // early for RenderFrameHost pending deletion or in the BackForwardCache.
    CHECK(owner_);
    if (owner_->GetCurrentNavigator()
            .controller()
            .has_post_commit_error_entry()) {
      bad_message::ReceivedBadMessage(
          process, bad_message::NC_SAME_DOCUMENT_POST_COMMIT_ERROR);
      return false;
    }
  }

  // Check(s) specific to sub-frame navigation.
  if (navigation_request && !is_main_frame()) {
    if (!CanSubframeCommitOriginAndUrl(navigation_request)) {
      // Terminate the renderer if allowing this subframe navigation to commit
      // would change the origin of the main frame.
      bad_message::ReceivedBadMessage(
          process,
          bad_message::RFHI_SUBFRAME_NAV_WOULD_CHANGE_MAINFRAME_ORIGIN);
      return false;
    }
  }

  return true;
}

bool RenderFrameHostImpl::ValidateURLAndOrigin(
    const GURL& url,
    const url::Origin& origin,
    bool is_same_document_navigation,
    NavigationRequest* navigation_request) {
  // file: URLs can be allowed to access any other origin, based on settings.
  if (origin.scheme() == url::kFileScheme) {
    auto prefs = GetOrCreateWebPreferences();
    if (prefs.allow_universal_access_from_file_urls)
      return true;
  }

  // If the --disable-web-security flag is specified, all bets are off and the
  // renderer process can send any origin it wishes.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableWebSecurity)) {
    return true;
  }

  // WebView's loadDataWithBaseURL API is allowed to bypass normal commit
  // checks because it is allowed to commit anything into its unlocked process
  // and its data: URL and non-opaque origin would fail the normal commit
  // checks. We should also allow same-document navigations within pages loaded
  // with loadDataWithBaseURL. Since renderer-initiated same-document
  // navigations won't have a NavigationRequest at this point, we need to check
  // |renderer_url_info_.was_loaded_from_load_data_with_base_url|.
  DCHECK(navigation_request || is_same_document_navigation ||
         frame_tree_node_->is_on_initial_empty_document());
  RenderProcessHost* process = GetProcess();
  if ((navigation_request && navigation_request->IsLoadDataWithBaseURL()) ||
      (is_same_document_navigation &&
       renderer_url_info_.was_loaded_from_load_data_with_base_url)) {
    // Allow bypass if the process isn't locked. Otherwise run normal checks.
    if (!process->GetProcessLock().is_locked_to_site())
      return true;
  }

  // Use the value of `is_pdf` from `navigation_request` (if provided). This may
  // be needed to verify the process lock in `CanCommitOriginAndUrl()`, but
  // cannot be derived from the URL and origin alone.
  bool is_pdf = navigation_request && navigation_request->GetUrlInfo().is_pdf;
  bool is_sandboxed =
      navigation_request && navigation_request->GetUrlInfo().is_sandboxed;

  // Attempts to commit certain off-limits URL should be caught more strictly
  // than our FilterURL checks.  If a renderer violates this policy, it
  // should be killed.
  switch (CanCommitOriginAndUrl(origin, url, is_same_document_navigation,
                                is_pdf, is_sandboxed)) {
    case CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL:
      // The origin and URL are safe to commit.
      break;
    case CanCommitStatus::CANNOT_COMMIT_URL:
      DLOG(ERROR) << "CANNOT_COMMIT_URL url '" << url << "'"
                  << " origin '" << origin << "'"
                  << " lock '" << process->GetProcessLock().ToString() << "'";
      VLOG(1) << "Blocked URL " << url.spec();
      LogCannotCommitUrlCrashKeys(url, is_same_document_navigation,
                                  navigation_request);

      // Kills the process.
      bad_message::ReceivedBadMessage(process,
                                      bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
      return false;
    case CanCommitStatus::CANNOT_COMMIT_ORIGIN:
      DLOG(ERROR) << "CANNOT_COMMIT_ORIGIN url '" << url << "'"
                  << " origin '" << origin << "'"
                  << " lock '" << process->GetProcessLock().ToString() << "'";
      DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, origin);
      LogCannotCommitOriginCrashKeys(url, origin, process->GetProcessLock(),
                                     is_same_document_navigation,
                                     navigation_request);

      // Kills the process.
      bad_message::ReceivedBadMessage(
          process, bad_message::RFH_INVALID_ORIGIN_ON_COMMIT);
      return false;
  }
  return true;
}

// Simulates the calculation for DidCommitProvisionalLoadParams' `referrer`.
// This is written to preserve the behavior of the calculations that happened in
// the renderer before being moved to the browser. In the future, we might want
// to remove this function in favor of using NavigationRequest::GetReferrer()
// or CommonNavigationParam's `referrer` directly.
blink::mojom::ReferrerPtr GetReferrerForDidCommitParams(
    NavigationRequest* request) {
  if (request->DidEncounterError()) {
    // Error pages always use the referrer from CommonNavigationParams, since
    // it won't go through its server redirects in the renderer, and won't be
    // marked as a client redirect.
    // TODO(https://crbug.com/1218786): Maybe make this case just return the
    // sanitized referrer below once the client redirect bug is fixed. This
    // means GetReferrerForDidCommitParams(), NavigationRequest::GetReferrer()
    // (`sanitized_referrer_`), and CommonNavigationParams's `referrer` will all
    // return the same value, and GetReferrerForDidCommitParams() and
    // `sanitized_referrer_` can be removed, leaving only
    // CommonNavigationParams's `referrer`.
    return request->common_params().referrer.Clone();
  }

  // Otherwise, return the sanitized referrer saved in the NavigationRequest.
  // - For client redirects, this will be the the URL that initiated the
  // navigation. (Note: this will only be sanitized at the start, and not after
  // any redirects, including cross-origin ones. See https://crbug.com/1218786)
  // - For other navigations, this will be the referrer used after the final
  // redirect.
  return request->GetReferrer().Clone();
}

// static
// This function logs metrics about potentially isolatable sandboxed iframes
// that are tracked through calls to UpdateIsolatableSandboxedIframeTracking().
// In addition to reporting the number of potential OOPSIFs, it also reports the
// number of unique origins encountered (to give insight into potential
// behavior if a per-origin isolation model was implemented), and it counts the
// actual number of RenderProcessHosts isolating OOPSIFs using the current
// per-site isolation model.
void RenderFrameHost::LogSandboxedIframesIsolationMetrics() {
  RoutingIDIsolatableSandboxedIframesSet* oopsifs =
      g_routing_id_isolatable_sandboxed_iframes_set.Pointer();

  base::UmaHistogramCounts1000("SiteIsolation.IsolatableSandboxedIframes",
                               oopsifs->size());

  // Count the number of unique origins across all the isolatable sandboxed
  // iframes. This will give us a sense of the potential process overhead if we
  // chose a per-origin process model for isolating these frames instead of the
  // per-site model we plan to use. We use the precursor SchemeHostPort rather
  // than the url::Origin, which is always opaque in these cases.
  {
    std::set<SiteInfo> sandboxed_site_infos;
    std::set<url::SchemeHostPort> sandboxed_origins;
    for (auto rfh_global_id : *oopsifs) {
      auto* rfhi = RenderFrameHostImpl::FromID(rfh_global_id);
      DCHECK(rfhi->GetLastCommittedOrigin().opaque());
      sandboxed_origins.insert(
          rfhi->GetLastCommittedOrigin().GetTupleOrPrecursorTupleIfOpaque());
      sandboxed_site_infos.insert(rfhi->GetSiteInstance()->GetSiteInfo());
    }
    base::UmaHistogramCounts1000(
        "SiteIsolation.IsolatableSandboxedIframes.UniqueOrigins",
        sandboxed_origins.size());
    base::UmaHistogramCounts1000(
        "SiteIsolation.IsolatableSandboxedIframes.UniqueSites",
        sandboxed_site_infos.size());
  }

  // Walk the set and count the number of unique RenderProcessHosts. Using a set
  // allows us to accurately measure process overhead, including cases where
  // SiteInstances from multiple BrowsingInstances are coalesced into a single
  // RenderProcess.
  std::set<RenderProcessHost*> sandboxed_rphs;
  for (auto rfh_global_id : *oopsifs) {
    auto* rfhi = FromID(rfh_global_id);
    DCHECK(rfhi);
    auto* site_instance =
        static_cast<SiteInstanceImpl*>(rfhi->GetSiteInstance());
    DCHECK(site_instance->HasProcess());
    if (site_instance->GetSiteInfo().is_sandboxed())
      sandboxed_rphs.insert(site_instance->GetProcess());
  }
  // There should be no sandboxed RPHs if the feature isn't enabled.
  DCHECK(SiteIsolationPolicy::AreIsolatedSandboxedIframesEnabled() ||
         sandboxed_rphs.size() == 0);
  base::UmaHistogramCounts1000(
      "Memory.RenderProcessHost.Count.SandboxedIframeOverhead",
      sandboxed_rphs.size());
}

void RenderFrameHostImpl::UpdateIsolatableSandboxedIframeTracking(
    NavigationRequest* navigation_request) {
  RoutingIDIsolatableSandboxedIframesSet* oopsifs =
      g_routing_id_isolatable_sandboxed_iframes_set.Pointer();
  GlobalRenderFrameHostId global_id = GetGlobalId();

  // Check if the flags are correct.
  DCHECK(policy_container_host_);
  bool frame_is_isolatable =
      IsSandboxed(network::mojom::WebSandboxFlags::kOrigin);

  if (frame_is_isolatable) {
    // Limit the "isolatable" sandboxed frames to those that are either in the
    // same SiteInstance as their parent/opener (and thus could be isolated), or
    // that are already isolated due to sandbox flags.
    GURL url = GetLastCommittedURL();
    if (url.IsAboutBlank() || url.is_empty()) {
      frame_is_isolatable = false;
    } else {
      // Since this frame could be a main frame, we need to consider the
      // SiteInstance of either the parent or opener (if either exists) of this
      // frame, to see if the url can be placed in an OOPSIF, i.e. it's not
      // already isolated because of being cross-site.
      RenderFrameHost* frame_owner = GetParent();
      FrameTreeNode* opener = navigation_request->frame_tree_node()->opener();
      if (!frame_owner && opener)
        frame_owner = opener->current_frame_host();

      if (!frame_owner) {
        frame_is_isolatable = false;
      } else if (GetSiteInstance()->GetSiteInfo().is_sandboxed()) {
        DCHECK(frame_is_isolatable);
      } else if (frame_owner->GetSiteInstance() != GetSiteInstance()) {
        // If this host's SiteInstance isn't already marked as is_sandboxed
        // (with a frame owner), and yet the SiteInstance doesn't match that of
        // our parent/opener, then it is already isolated for some other reason
        // (cross-site, origin-keyed, etc.).
        frame_is_isolatable = false;
      }
    }
  }

  if (frame_is_isolatable)
    oopsifs->insert(global_id);
  else
    oopsifs->erase(global_id);
}

bool RenderFrameHostImpl::DidCommitNavigationInternal(
    std::unique_ptr<NavigationRequest> navigation_request,
    mojom::DidCommitProvisionalLoadParamsPtr params,
    mojom::DidCommitSameDocumentNavigationParamsPtr same_document_params) {
  const bool is_same_document_navigation = !!same_document_params;
  // Sanity-check the page transition for frame type. Fenced Frames
  // will set page transition to AUTO_SUBFRAME.
  DCHECK_EQ(ui::PageTransitionIsMainFrame(params->transition),
            !GetParent() && !IsFencedFrameRoot());
  if (navigation_request &&
      navigation_request->commit_params().navigation_token !=
          params->navigation_token) {
    // We should have the same navigation_token in CommitNavigationParams and
    // DidCommit's |params| for all navigations, because:
    // - Cross-document navigations use NavigationClient.
    // - Same-document navigations will have a null |navigation_request|
    //   here if the navigation_token doesn't match (checked in
    //   DidCommitSameDocumentNavigation).
    // TODO(https://crbug.com/1131832): Make this a CHECK instead once we're
    // sure we never hit this case.
    LogCannotCommitUrlCrashKeys(params->url, is_same_document_navigation,
                                navigation_request.get());
    base::debug::DumpWithoutCrashing();
  }

  // A matching NavigationRequest should have been found, unless in a few very
  // specific cases:
  // 1) This was a synchronous about:blank navigation triggered by browsing
  // context creation.
  // 2) This was a renderer-initiated same-document navigation.
  // In these cases, we will create a NavigationRequest by calling
  // CreateNavigationRequestForSynchronousRendererCommit() further down.
  // TODO(https://crbug.com/1131832): Make these navigation go through a
  // separate path that does not send
  // FrameHostMsg_DidCommitProvisionalLoad_Params at all.
  // TODO(https://crbug.com/1215096): Tighten the checks for case 1 so that only
  // the synchronous about:blank commit can actually go through (e.g. check
  // if the URL is exactly "about:blank", currently we allow any "about:" URL
  // except for "about:srcdoc").
  const bool is_synchronous_about_blank_commit =
      IsInitialSynchronousAboutBlankCommit(
          params->url, frame_tree_node_->is_on_initial_empty_document());
  if (!navigation_request && !is_synchronous_about_blank_commit &&
      !is_same_document_navigation) {
    LogCannotCommitUrlCrashKeys(params->url, is_same_document_navigation,
                                navigation_request.get());

    bad_message::ReceivedBadMessage(
        GetProcess(),
        bad_message::RFH_NO_MATCHING_NAVIGATION_REQUEST_ON_COMMIT);
    return false;
  }

  if (!ValidateDidCommitParams(navigation_request.get(), params.get(),
                               is_same_document_navigation)) {
    return false;
  }

  // TODO(clamy): We should stop having a special case for same-document
  // navigation and just put them in the general map of NavigationRequests.
  if (navigation_request &&
      navigation_request->common_params().url != params->url &&
      is_same_document_navigation) {
    same_document_navigation_requests_[navigation_request->commit_params()
                                           .navigation_token] =
        std::move(navigation_request);
  }

  // Set is loading to true now if it has not been set yet. This happens for
  // renderer-initiated same-document navigations. It can also happen when a
  // racy DidStopLoading Mojo method resets the loading state that was set to
  // true in CommitNavigation.
  if (!is_loading()) {
    LoadingState previous_frame_tree_loading_state =
        frame_tree()->LoadingTree()->GetLoadingState();
    loading_state_ = is_same_document_navigation
                         ? LoadingState::LOADING_WITHOUT_UI
                         : LoadingState::LOADING_UI_REQUESTED;
    // TODO(https://crbug.com/1405759): Explain why this is true.
    CHECK(owner_);
    owner_->DidStartLoading(previous_frame_tree_loading_state);
  }

  if (navigation_request)
    was_discarded_ = navigation_request->commit_params().was_discarded;

  if (navigation_request) {
    // If the navigation went through the browser before committing, it's
    // possible to calculate the referrer only using information known by the
    // browser.
    // TODO(https://crbug.com/1131832): Get rid of params->referrer completely.
    params->referrer = GetReferrerForDidCommitParams(navigation_request.get());
  } else {
    // For renderer-initiated same-document navigations and the initial
    // about:blank navigation, the referrer policy shouldn't change.
    params->referrer->policy = policy_container_host_->referrer_policy();
  }

  if (!navigation_request) {
    // If there is no valid NavigationRequest corresponding to this commit,
    // create one in order to properly issue DidFinishNavigation calls to
    // WebContentsObservers.
    DCHECK(is_synchronous_about_blank_commit || is_same_document_navigation);

    // Fill the redirect chain for the NavigationRequest. Since this is only for
    // initial empty commits or same-document navigation, we should just push
    // the client-redirect URL (if it is a client redirect) and the final URL.
    std::vector<GURL> redirects;
    if (is_same_document_navigation &&
        same_document_params->is_client_redirect) {
      // If it is a same-document navigation, it might be a client redirect, in
      // which case we should put the previous URL at the front of the redirect
      // chain.
      redirects.push_back(GetLastCommittedURL());
    }
    redirects.push_back(params->url);

    // If this is a (renderer-initiated) same-document navigation, it might be
    // started by a transient activation. The only way to know this is from the
    // `started_with_transient_activation` value of `same_document_params`
    // (because we don't know anything about this navigation before DidCommit).
    bool started_with_transient_activation =
        (is_same_document_navigation &&
         same_document_params->started_with_transient_activation);

    // If this is a (renderer-initiated) same-document navigation, the renderer
    // will tell us whether the navigation should replace the current entry or
    // not. Otherwise, this must be a synchronously committed about:blank, which
    // should always do replacement.
    bool should_replace_current_entry =
        is_same_document_navigation
            ? same_document_params->should_replace_current_entry
            : true;

    // TODO(https://crbug.com/1131832): Do not use |params| to get the values,
    // depend on values known at commit time instead.
    navigation_request = CreateNavigationRequestForSynchronousRendererCommit(
        params->url, params->origin, params->initiator_base_url,
        params->referrer.Clone(), params->transition,
        should_replace_current_entry, started_with_transient_activation,
        redirects, params->url, is_same_document_navigation,
        same_document_params &&
            same_document_params->same_document_navigation_type ==
                blink::mojom::SameDocumentNavigationType::kHistoryApi);
  }

  DCHECK(navigation_request);
  DCHECK(navigation_request->IsNavigationStarted());
  VerifyThatBrowserAndRendererCalculatedDidCommitParamsMatch(
      navigation_request.get(), *params, same_document_params.Clone());

  // Update the page transition. For subframe navigations, the renderer process
  // only gives the correct page transition at commit time.
  // TODO(clamy): We should get the correct page transition when starting the
  // request.
  navigation_request->set_transition(params->transition);

  SetLastCommittedSiteInfo(navigation_request->DidEncounterError()
                               ? UrlInfo()
                               : navigation_request->GetUrlInfo());

  isolation_info_ = navigation_request->isolation_info_for_subresources();

  // Navigations in the same document and page activations do not create a new
  // document.
  bool created_new_document =
      !is_same_document_navigation && !navigation_request->IsPageActivation();

  // TODO(crbug.com/936696): Remove this after we have RenderDocument.
  // IsWaitingToCommit can be false inside DidCommitNavigationInternal only in
  // specific circumstances listed above, and specifically for the fake
  // initial navigations triggered by the blank window.open() and creating a
  // blank iframe. In that case we do not want to reset the per-document
  // states as we are not really creating a new Document and we want to
  // preserve the states set by WebContentsCreated delegate notification
  // (which among other things create tab helpers) or RenderFrameCreated.
  bool navigated_to_new_document =
      created_new_document && navigation_request->IsWaitingToCommit();

  if (navigated_to_new_document) {
    TRACE_EVENT("content", "DidCommitProvisionalLoad_StateResetForNewDocument",
                ChromeTrackEvent::kRenderFrameHost, this);

    last_committed_cross_document_navigation_id_ =
        navigation_request->GetNavigationId();

    if (ShouldResetDocumentAssociatedDataAtCommit()) {
      DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
      // The old Reporting API configuration is no longer valid, as a new
      // document is being loaded into the frame. Inform the network service
      // of this, so that it can send any queued reports and mark the source
      // as expired.
      GetStoragePartition()->GetNetworkContext()->SendReportsAndRemoveSource(
          GetReportingSource());

      // Clear all document-associated data for the non-pending commit
      // RenderFrameHosts because the navigation has created a new document.
      // Make sure the data doesn't get cleared for the cases when the
      // RenderFrameHost commits before the navigation commits. This happens
      // when the current RenderFrameHost crashes before navigating to a new
      // URL.
      document_associated_data_.emplace(*this,
                                        navigation_request->GetDocumentToken());
    } else {
      // Cross-RenderFrameHost navigations that commit into a speculative
      // RenderFrameHost do not create a new DocumentAssociatedData. Ensure that
      // the NavigationRequest was populated with the correct DocumentToken to
      // avoid a mismatched token between the browser and the renderer.
      CHECK_EQ(document_associated_data_->token(),
               navigation_request->GetDocumentToken());
    }

    const absl::optional<FencedFrameProperties>& fenced_frame_properties =
        navigation_request->ComputeFencedFrameProperties();
    // On navigations of fenced frame/urn iframe roots initiated within the
    // fenced frame/urn iframe tree, store document-scoped and page-scoped
    // metadata again.
    // TODO(crbug.com/1347953): Remove this metadata, and access the metadata
    // directly in the FencedFrameProperties.
    if (fenced_frame_properties &&
        (frame_tree_node()->IsFencedFrameRoot() ||
         !frame_tree_node()->IsInFencedFrameTree())) {
      if (fenced_frame_properties->nested_urn_config_pairs_.has_value()) {
        // Store nested ad components in the fenced frame's url map.
        // This may only be done after creating the DocumentAssociatedData for
        // the new document, if appropriate, since `fenced_frame_urls_map` hangs
        // off of that. In urn iframes, unlike in fenced frames, navigations of
        // the urn iframe root don't create a new Page (because the root of the
        // Page is the top-level frame). So this operation is a no-op.
        GetPage().fenced_frame_urls_map().ImportPendingAdComponents(
            fenced_frame_properties->nested_urn_config_pairs_
                ->GetValueIgnoringVisibility());
      }

      if (fenced_frame_properties->ad_auction_data_.has_value()) {
        AdAuctionDocumentData::CreateForCurrentDocument(
            this,
            fenced_frame_properties->ad_auction_data_
                ->GetValueIgnoringVisibility()
                .interest_group_owner,
            fenced_frame_properties->ad_auction_data_
                ->GetValueIgnoringVisibility()
                .interest_group_name);
      }
    }

    // Continue observing the events for the committed navigation.
    for (auto& receiver : navigation_request->TakeCookieObservers()) {
      cookie_observers_.Add(this, std::move(receiver));
    }

    // Continue observing the events for the committed navigation.
    for (auto& receiver : navigation_request->TakeTrustTokenObservers()) {
      trust_token_observers_.Add(this, std::move(receiver));
    }

    // Resets when navigating to a new document. This is needed because
    // RenderFrameHost might be reused for a new document
    document_used_web_otp_ = false;

    // Get the UKM source id sent to the renderer.
    const ukm::SourceId document_ukm_source_id =
        navigation_request->commit_params().document_ukm_source_id;

    ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();

    // Associate the blink::Document source id to the URL. Only URLs on primary
    // main frames can be recorded.
    // TODO(crbug.com/1245014): For prerendering pages, record the source url
    // after activation.
    if (navigation_request->IsInPrimaryMainFrame() &&
        document_ukm_source_id != ukm::kInvalidSourceId) {
      ukm_recorder->UpdateSourceURL(document_ukm_source_id, params->url);
    }
    RecordDocumentCreatedUkmEvent(params->origin, document_ukm_source_id,
                                  ukm_recorder);

    // We only replace the `CookieChangeListener` with the one initialized by
    // the navigation request when navigating to a new document. Otherwise, the
    // existing `CookieChangeListener` will be reused.
    cookie_change_listener_ = navigation_request->TakeCookieChangeListener();
  }

  if (!is_same_document_navigation) {
    DCHECK_EQ(navigation_request->is_overriding_user_agent() && is_main_frame(),
              params->is_overriding_user_agent);
    if (navigation_request->IsPrerenderedPageActivation()) {
      // Set the NavigationStart time for
      // PerformanceNavigationTiming.activationStart.
      // https://wicg.github.io/nav-speculation/prerendering.html#performance-navigation-timing-extension
      GetPage().SetActivationStartTime(navigation_request->NavigationStart());
    }

  } else {
    DCHECK_EQ(is_overriding_user_agent_, params->is_overriding_user_agent);
  }

  if (is_main_frame()) {
    document_associated_data_->owned_page()->set_last_main_document_source_id(
        ukm::ConvertToSourceId(navigation_request->GetNavigationId(),
                               ukm::SourceIdType::NAVIGATION_ID));
  }

  // TODO(https://crbug.com/1131832): Do not pass |params| to DidNavigate().
  NavigationRequest* raw_navigation_request = navigation_request.get();
  raw_navigation_request->frame_tree_node()->navigator().DidNavigate(
      this, *params, std::move(navigation_request),
      is_same_document_navigation);

  // Reset back the state to false after navigation commits.
  // TODO(https://crbug.com/1072817): Undo this plumbing after removing the
  // early post-crash CommitPending() call.
  committed_speculative_rfh_before_navigation_commit_ = false;

  // Store the Commit params so they can be reused if the page is ever
  // restored from the BackForwardCache or a Prerender2 page is activated.
  last_commit_params_ = std::move(params);

  return true;
}

bool RenderFrameHostImpl::ShouldResetDocumentAssociatedDataAtCommit() const {
  return lifecycle_state() != LifecycleStateImpl::kPendingCommit &&
         !committed_speculative_rfh_before_navigation_commit_;
}

// TODO(arthursonzogni): Investigate what must be done when
// navigation_request->IsWaitingToCommit() is false here.
void RenderFrameHostImpl::DidCommitNewDocument(
    const mojom::DidCommitProvisionalLoadParams& params,
    NavigationRequest* navigation_request) {
  // Navigations in the same document and page activations do not create a new
  // document.
  DCHECK(!navigation_request->IsSameDocument());
  DCHECK(!navigation_request->IsPageActivation());

  const GURL& request_url = navigation_request->common_params().url;
  if (request_url.IsAboutBlank() || request_url.IsAboutSrcdoc()) {
    const absl::optional<::GURL>& initiator_base_url =
        navigation_request->common_params().initiator_base_url;
    SetInheritedBaseUrl(initiator_base_url ? initiator_base_url.value()
                                           : GURL::EmptyGURL());
  } else {
    SetInheritedBaseUrl(GURL::EmptyGURL());
  }

  navigation_id_ = navigation_request->GetNavigationId();

  // The nonce to use in credentialless iframe is a page scoped attribute. So it
  // needs to change every time the top-level document change.
  // TODO(https://crbug.com1287458): Once the ShadowDom implementation of
  // FencedFrame is gone, move this attribute back to PageImpl.
  credentialless_iframes_nonce_ = base::UnguessableToken::Create();

  // When the embedder navigates a fenced frame root, the navigation
  // stores a new set of fenced frame properties.
  // (Embedder-initiated fenced frame root navigation  will necessarily create
  // a new document.)
  // This must be done before `ResetPermissionsPolicy()` below, which looks up
  // the stored fenced frame properties.
  if (navigation_request->GetFencedFrameProperties()) {
    frame_tree_node()->set_fenced_frame_properties(
        navigation_request->GetFencedFrameProperties());
  }

  ResetPermissionsPolicy();

  permissions_policy_header_ = params.permissions_policy_header;
  auto isolation_info = GetSiteInstance()->GetWebExposedIsolationInfo();
  // Isolated Apps start with a base policy as defined by the permissions_policy
  // field in its Web App Manifest, which is an allowlist, and then have headers
  // further restrict the policy if applicable. This needs to be handled
  // differently than the normal permissions policy behavior, which uses a fully
  // permissive policy as its base permissions policy and accepts rules
  // specifying which permissions policy features should be blocked, aka a
  // blocklist.
  if (isolation_info.is_isolated_application() && IsOutermostMainFrame()) {
    permissions_policy_->SetHeaderPolicyForIsolatedApp(
        params.permissions_policy_header);
  } else {
    permissions_policy_->SetHeaderPolicy(params.permissions_policy_header);
  }
  document_policy_ = blink::DocumentPolicy::CreateWithHeaderPolicy({
      params.document_policy_header,  // document_policy_header
      {},                             // endpoint_map
  });

  // Since we're changing documents, we should reset the event handler
  // trackers.
  has_before_unload_handler_ = false;
  has_unload_handler_ = false;
  has_pagehide_handler_ = false;
  has_visibilitychange_handler_ = false;

  has_navigate_event_handler_ = false;

  DCHECK(params.embedding_token.has_value());
  SetEmbeddingToken(params.embedding_token.value());

  renderer_reported_bfcache_blocking_details_.clear();
  browser_reported_bfcache_disabling_features_counts_.clear();

  TakeNewDocumentPropertiesFromNavigation(navigation_request);

  // Set embedded documents' cross-origin-opener-policy from their top level:
  //  - Use top level's policy if they are same-origin.
  //  - Use the default policy if they are cross-origin.
  // This COOP value is not used to enforce anything on this frame, but will be
  // inherited to every local-scheme document created from them.
  // It will also be inherited by the initial empty document from its opener.

  // TODO(https://crbug.com/888079) Computing and assigning the
  // cross-origin-opener-policy of an embedded frame should be done in
  // |NavigationRequest::ComputePoliciesToCommit| , but this is not currently
  // possible because we need the origin for the computation. The linked bug
  // moves the origin computation earlier in the navigation request, which will
  // enable the move to |NavigationRequest::ComputePoliciesToCommit|.
  if (parent_) {
    if (GetMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
            params.origin)) {
      policy_container_host_->set_cross_origin_opener_policy(
          GetMainFrame()->cross_origin_opener_policy());
    } else {
      policy_container_host_->set_cross_origin_opener_policy(
          network::CrossOriginOpenerPolicy());
    }
  }

  CrossOriginOpenerPolicyAccessReportManager::InstallAccessMonitorsIfNeeded(
      navigation_request->frame_tree_node());

  // Reset the salt so that media device IDs are reset for the new document
  // if necessary.
  media_device_id_salt_base_ = BrowserContext::CreateRandomMediaDeviceIDSalt();

  accessibility_fatal_error_count_ = 0;

  UpdateIsolatableSandboxedIframeTracking(navigation_request);

  // After commit, the browser process's access of the features' state becomes
  // read-only. (i.e. It can only get feature state, not set)
  RuntimeFeatureStateDocumentData::CreateForCurrentDocument(
      this, navigation_request->GetRuntimeFeatureStateContext());
}

// TODO(arthursonzogni): Below, many NavigationRequest's objects are passed from
// the navigation to the new document. Consider grouping them in a single
// struct.
void RenderFrameHostImpl::TakeNewDocumentPropertiesFromNavigation(
    NavigationRequest* navigation_request) {
  // It should be kept in sync with the check in
  // NavigationRequest::DidCommitNavigation.
  is_error_document_ = navigation_request->DidEncounterError();
  // Overwrite reporter's reporting source with rfh's reporting source.
  std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter =
      navigation_request->coop_status().TakeCoopReporter();
  if (coop_reporter)
    coop_reporter->set_reporting_source(GetReportingSource());
  SetCrossOriginOpenerPolicyReporter(std::move(coop_reporter));
  virtual_browsing_context_group_ =
      navigation_request->coop_status().virtual_browsing_context_group();
  soap_by_default_virtual_browsing_context_group_ =
      navigation_request->coop_status()
          .soap_by_default_virtual_browsing_context_group();

  // Store the required CSP (it will be used by the AncestorThrottle if
  // this frame embeds a subframe when that subframe navigates).
  required_csp_ = navigation_request->TakeRequiredCSP();

  is_fenced_frame_root_originating_from_opaque_url_ =
      navigation_request
          ->is_target_fenced_frame_root_originating_from_opaque_url();

  RuntimeFeatureStateDocumentData::CreateForCurrentDocument(
      this, navigation_request->GetRuntimeFeatureStateContext());

  // TODO(https://crbug.com/888079): Once we are able to compute the origin to
  // commit in the browser, `navigation_request->commit_params().storage_key`
  // will contain the correct origin and it won't be necessary to override it
  // with `param.origin` anymore.
  const blink::StorageKey& provisional_storage_key =
      navigation_request->commit_params().storage_key;

  url::Origin origin = GetLastCommittedOrigin();
  blink::StorageKey storage_key_to_commit = CalculateStorageKey(
      origin, base::OptionalToPtr(provisional_storage_key.nonce()));
  SetStorageKey(storage_key_to_commit);

  coep_reporter_ = navigation_request->TakeCoepReporter();
  if (coep_reporter_) {
    // Set coep reporter to the document reporting source.
    coep_reporter_->set_reporting_source(GetReportingSource());
    mojo::PendingRemote<blink::mojom::ReportingObserver> remote;
    mojo::PendingReceiver<blink::mojom::ReportingObserver> receiver =
        remote.InitWithNewPipeAndPassReceiver();
    coep_reporter_->BindObserver(std::move(remote));
    // As some tests override the associated frame after commit, do not
    // call GetAssociatedLocalFrame now.
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&RenderFrameHostImpl::BindReportingObserver,
                       weak_ptr_factory_.GetWeakPtr(), std::move(receiver)));
  }

  // Set the state whether this navigation is to an MHTML document, since there
  // are certain security checks that we cannot apply to subframes in MHTML
  // documents. Do not trust renderer data when determining that, rather use
  // the |navigation_request|, which was generated and stays browser side.
  is_mhtml_document_ = navigation_request->IsWaitingToCommit() &&
                       navigation_request->IsMhtmlOrSubframe();

  is_overriding_user_agent_ =
      navigation_request->is_overriding_user_agent() && is_main_frame();

  // Mark whether then navigation was intended as a loadDataWithBaseURL or not.
  // If |renderer_url_info_.was_loaded_from_load_data_with_base_url| is true, we
  // will bypass checks in VerifyDidCommitParams for same-document navigations
  // in the loaded document.
  renderer_url_info_.was_loaded_from_load_data_with_base_url =
      navigation_request->IsLoadDataWithBaseURL();

  // If we still have a PeakGpuMemoryTracker, then the loading it was observing
  // never completed. Cancel it's callback so that we don't report partial
  // loads to UMA.
  if (loading_mem_tracker_) {
    loading_mem_tracker_->Cancel();
  }

  // Main Frames will create the tracker, which will be triggered after we
  // receive DidStopLoading.
  loading_mem_tracker_ = navigation_request->TakePeakGpuMemoryTracker();

  early_hints_manager_ = navigation_request->TakeEarlyHintsManager();

  // If fullscreen was requested on the navigation, then set a signal to trigger
  // a fullscreen request when the document element is ready. If a previous
  // navigation was committed without a call to `MainDocumentElementAvailable`
  // before the current navigation then this value is overwritten by the current
  // navigation.
  fullscreen_document_on_document_element_ready_.reset();
  if (navigation_request->begin_params().is_fullscreen_requested) {
    fullscreen_document_on_document_element_ready_ =
        navigation_request->GetDocumentToken();
  }

#ifdef OHOS_BUGFIX_CRASH
  if (fullscreen_document_on_document_element_ready_ && !IsOutermostMainFrame()) {
    LOG(INFO) << "fullscreen element is ready, while document is not main frame";
#ifdef OHOS_LOGGER_REPORT
    LOG_FEEDBACK(INFO) << "fullscreen element is ready, while document is not main frame";
#endif
    return;
  }
#else
  CHECK(!fullscreen_document_on_document_element_ready_ ||
        IsOutermostMainFrame());
#endif

  // Only take some properties if this is not the synchronous initial
  // `about:blank` navigation, because the values set at construction time
  // should remain unmodified.
  if (!navigation_request->IsWaitingToCommit()) {
    return;
  }

  local_network_request_policy_ =
      navigation_request->local_network_request_policy();

  reporting_endpoints_.clear();
  DCHECK(navigation_request);

  // Reporting API: If a Reporting-Endpoints header was received with this
  // document over secure connection, send it to the network service to
  // configure the endpoints in the reporting cache.
  if (GURL::SchemeIsCryptographic(origin.scheme()) &&
      navigation_request->response() &&
      navigation_request->response()->parsed_headers->reporting_endpoints) {
    GetStoragePartition()->GetNetworkContext()->SetDocumentReportingEndpoints(
        GetReportingSource(), origin, isolation_info_,
        *(navigation_request->response()->parsed_headers->reporting_endpoints));
  }

  // We move the PolicyContainerHost of |navigation_request| into the
  // RenderFrameHost unless this is the initial, "fake" navigation to
  // about:blank (because otherwise we would overwrite the PolicyContainerHost
  // of the new document, inherited at RenderFrameHost creation time, with an
  // empty PolicyContainerHost).
  SetPolicyContainerHost(navigation_request->TakePolicyContainerHost());

  if (navigation_request->response())
    last_response_head_ = navigation_request->response()->Clone();
}

void RenderFrameHostImpl::OnSameDocumentCommitProcessed(
    const base::UnguessableToken& navigation_token,
    bool should_replace_current_entry,
    blink::mojom::CommitResult result) {
  auto request = same_document_navigation_requests_.find(navigation_token);
  if (request == same_document_navigation_requests_.end()) {
    // OnSameDocumentCommitProcessed will be called after DidCommitNavigation on
    // successfull same-document commits, so |request| should already be deleted
    // by the time we got here.
    DCHECK_EQ(result, blink::mojom::CommitResult::Ok);
    return;
  }

  if (result == blink::mojom::CommitResult::RestartCrossDocument) {
    // The navigation could not be committed as a same-document navigation.
    // Restart the navigation cross-document.
    // TODO(https://crbug.com/1405759): Explain why `owner_` exists.
    CHECK(owner_);
    owner_->RestartNavigationAsCrossDocument(std::move(request->second));
    return;
  }

  DCHECK_EQ(result, blink::mojom::CommitResult::Aborted);
  // Note: if the commit was successful, the NavigationRequest is moved in
  // DidCommitSameDocumentNavigation.
  same_document_navigation_requests_.erase(navigation_token);
}

void RenderFrameHostImpl::MaybeGenerateCrashReport(
    base::TerminationStatus status,
    int exit_code) {
  if (!last_committed_url_.SchemeIsHTTPOrHTTPS())
    return;

  // Only generate reports for local root frames that are in a different
  // process than their parent.
  if (!is_main_frame() && !IsCrossProcessSubframe())
    return;
  DCHECK(is_local_root());

  // Check the termination status to see if a crash occurred (and potentially
  // determine the |reason| for the crash).
  std::string reason;
  switch (status) {
    case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
      break;
    case base::TERMINATION_STATUS_PROCESS_CRASHED:
      if (exit_code == RESULT_CODE_HUNG)
        reason = "unresponsive";
      break;
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
      if (exit_code == RESULT_CODE_HUNG)
        reason = "unresponsive";
      else
        return;
      break;
    case base::TERMINATION_STATUS_OOM:
#if BUILDFLAG(IS_CHROMEOS)
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
#if BUILDFLAG(IS_ANDROID)
    case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
      reason = "oom";
      break;
    default:
      // Other termination statuses do not indicate a crash.
      return;
  }

  // Construct the crash report.
  base::Value::Dict body;
  if (!reason.empty())
    body.Set("reason", reason);

  // Send the crash report to the Reporting API.
  GetProcess()->GetStoragePartition()->GetNetworkContext()->QueueReport(
      /*type=*/"crash", /*group=*/"default", last_committed_url_,
      GetReportingSource(), isolation_info_.network_anonymization_key(),
      absl::nullopt /* user_agent */, std::move(body));
}

void RenderFrameHostImpl::SendCommitNavigation(
    mojom::NavigationClient* navigation_client,
    NavigationRequest* navigation_request,
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    network::mojom::URLResponseHeadPtr response_head,
    mojo::ScopedDataPipeConsumerHandle response_body,
    network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories,
    absl::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
        subresource_overrides,
    blink::mojom::ControllerServiceWorkerInfoPtr controller,
    blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        prefetch_loader_factory,
    mojo::PendingRemote<network::mojom::URLLoaderFactory> topics_loader_factory,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        keep_alive_loader_factory,
    mojo::PendingRemote<blink::mojom::ResourceCache> resource_cache_remote,
    const absl::optional<blink::ParsedPermissionsPolicy>& permissions_policy,
    blink::mojom::PolicyContainerPtr policy_container,
    const blink::DocumentToken& document_token,
    const base::UnguessableToken& devtools_navigation_token) {
  TRACE_EVENT0("navigation", "RenderFrameHostImpl::SendCommitNavigation");
  base::ElapsedTimer timer;
  DCHECK_EQ(net::OK, navigation_request->GetNetErrorCode());
  // `origin_to_commit` is currently only set only on failed navigations.
  DCHECK(!commit_params->origin_to_commit);
  IncreaseCommitNavigationCounter();
  mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host;
  mojom::CookieManagerInfoPtr cookie_manager_info;
  mojom::StorageInfoPtr storage_info;

  // Until the browser is able to compute the origin accurately in all cases
  // (see https://crbug.com/888079), this is actually just a provisional
  // `storage_key`. The final storage key is computed by the document loader
  // taking into account the origin computed by the renderer.
  auto& code_cache_storage_key = commit_params->storage_key;
  CreateCodeCacheHostWithKeys(
      code_cache_host.InitWithNewPipeAndPassReceiver(),
      navigation_request->isolation_info_for_subresources()
          .network_isolation_key(),
      code_cache_storage_key);

  url::Origin origin_to_commit =
      navigation_request->GetOriginToCommit().value();
  // Make sure the origin of the isolation info and origin to commit match,
  // otherwise the cookie manager will crash. Sending the cookie manager here
  // is just an optimization, so it is fine for it to be null in the case
  // where these don't match.

  if (common_params->url.SchemeIsHTTPOrHTTPS() && !origin_to_commit.opaque() &&
      navigation_request->isolation_info_for_subresources()
              .frame_origin()
              .value() == origin_to_commit) {
    cookie_manager_info = mojom::CookieManagerInfo::New();
    cookie_manager_info->origin = origin_to_commit;
    auto subresource_loader_factories_config =
        SubresourceLoaderFactoriesConfig::ForPendingNavigation(
            *navigation_request);

    BindRestrictedCookieManagerWithOrigin(
        cookie_manager_info->cookie_manager.InitWithNewPipeAndPassReceiver(),
        navigation_request->isolation_info_for_subresources(), origin_to_commit,
        subresource_loader_factories_config.cookie_setting_overrides());

    // Some tests need the StorageArea interfaces to come through DomStorage,
    // so ignore the optimizations in those cases.
    if (!RenderProcessHostImpl::HasDomStorageBinderForTesting()) {
      storage_info = mojom::StorageInfo::New();
      // Bind local storage and session storage areas.
      auto* partition = GetStoragePartition();
      int process_id = GetProcess()->GetID();
      partition->OpenLocalStorageForProcess(
          process_id, commit_params->storage_key,
          storage_info->local_storage_area.InitWithNewPipeAndPassReceiver());

      // Session storage must match the default namespace.
      const std::string& namespace_id =
          navigation_request->frame_tree_node()
              ->frame_tree()
              .controller()
              .GetSessionStorageNamespace(
                  GetSiteInstance()->GetStoragePartitionConfig())
              ->id();
      partition->BindSessionStorageAreaForProcess(
          process_id,
          navigation_request->frame_tree_node()
              ->frame_tree()
              .GetSessionStorageKey(commit_params->storage_key),
          namespace_id,
          storage_info->session_storage_area.InitWithNewPipeAndPassReceiver());
    }
  }

  // Save the last sent NotRestoredReasons value for testing, so that we can
  // verify them in tests.
  // TODO(yuzus): Remove |not_restored_reasons_for_testing_| and modify
  // |FrameNavigateParamsCapturer|.
  not_restored_reasons_for_testing_ =
      commit_params->not_restored_reasons.Clone();

  // If an automatic "top_navigation" beacon is registered in the FencedFrame
  // of the document initiator of the navigation, and the navigation
  // destination is an outermost main frame, send the beacon. We do this at
  // this point because:
  // 1. We need a handle to the initiator.
  // 2. The initiator hasn't been unloaded yet due to this navigation, and
  //    still exists at this point (unless explicitly removed from the DOM
  //    otherwise).
  MaybeSendFencedFrameReportingBeacon(*navigation_request);

  // TODO(crbug.com/1430232): Remove this once we know the cause of the
  // associated CHECK failure.
  if (common_params->initiator_base_url &&
      (!blink::features::IsNewBaseUrlInheritanceBehaviorEnabled() ||
       (!common_params->url.IsAboutBlank() &&
        !common_params->url.IsAboutSrcdoc()))) {
    SCOPED_CRASH_KEY_BOOL(
        "new_base_url", "new_base_url_enabled",
        blink::features::IsNewBaseUrlInheritanceBehaviorEnabled());
    SCOPED_CRASH_KEY_BOOL("new_base_url", "url_is_empty",
                          common_params->initiator_base_url->is_empty());
    base::debug::DumpWithoutCrashing();
  }

  commit_params->commit_sent = base::TimeTicks::Now();
  navigation_client->CommitNavigation(
      std::move(common_params), std::move(commit_params),
      std::move(response_head), std::move(response_body),
      std::move(url_loader_client_endpoints),
      std::move(subresource_loader_factories), std::move(subresource_overrides),
      std::move(controller), std::move(container_info),
      std::move(prefetch_loader_factory), std::move(topics_loader_factory),
      std::move(keep_alive_loader_factory), document_token,
      devtools_navigation_token, permissions_policy,
      std::move(policy_container), std::move(code_cache_host),
      std::move(resource_cache_remote), std::move(cookie_manager_info),
      std::move(storage_info),
      BuildCommitNavigationCallback(navigation_request));
  base::UmaHistogramTimes(
      base::StrCat({"Navigation.SendCommitNavigationTime.",
                    IsOutermostMainFrame() ? "MainFrame" : "Subframe"}),
      timer.Elapsed());
}

void RenderFrameHostImpl::SendCommitFailedNavigation(
    mojom::NavigationClient* navigation_client,
    NavigationRequest* navigation_request,
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    bool has_stale_copy_in_cache,
    int32_t error_code,
    int32_t extended_error_code,
    const absl::optional<std::string>& error_page_content,
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories,
    const blink::DocumentToken& document_token,
    blink::mojom::PolicyContainerPtr policy_container) {
  // `origin_to_commit` must be set on failed navigations.
  DCHECK(commit_params->origin_to_commit);
  DCHECK(navigation_client && navigation_request);
  DCHECK_NE(GURL(), common_params->url);
  DCHECK_NE(net::OK, error_code);
  IncreaseCommitNavigationCounter();
  navigation_client->CommitFailedNavigation(
      std::move(common_params), std::move(commit_params),
      has_stale_copy_in_cache, error_code, extended_error_code,
      navigation_request->GetResolveErrorInfo(), error_page_content,
      std::move(subresource_loader_factories), document_token,
      std::move(policy_container),
      GetContentClient()->browser()->GetAlternativeErrorPageOverrideInfo(
          navigation_request->GetURL(), this, GetBrowserContext(), error_code),
      BuildCommitFailedNavigationCallback(navigation_request));
}

// Called when the renderer navigates. For every frame loaded, we'll get this
// notification containing parameters identifying the navigation.
void RenderFrameHostImpl::DidCommitNavigation(
    NavigationRequest* committing_navigation_request,
    mojom::DidCommitProvisionalLoadParamsPtr params,
    mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
  DCHECK(params);

  // BackForwardCacheImpl::CanStoreRenderFrameHost prevents placing the pages
  // with in-flight navigation requests in the back-forward cache and it's not
  // possible to start/commit a new one after the RenderFrameHost is in the
  // BackForwardCache (see the check IsInactiveAndDisallowActivation in
  // RFH::DidCommitSameDocumentNavigation() and RFH::BeginNavigation()) so it
  // isn't possible to get a DidCommitNavigation IPC from the renderer in
  // kInBackForwardCache state.
  DCHECK(!IsInBackForwardCache());

  std::unique_ptr<NavigationRequest> request;
  // TODO(https://crbug.com/778318): a `committing_navigation_request` is not
  // present if and only if this is a synchronous re-navigation to about:blank
  // initiated by Blink. In all other cases it should be non-null and present in
  // the map of NavigationRequests.
  if (committing_navigation_request) {
    committing_navigation_request->IgnoreCommitInterfaceDisconnection();
    if (!MaybeInterceptCommitCallback(committing_navigation_request, &params,
                                      &interface_params)) {
      return;
    }

    auto find_request =
        navigation_requests_.find(committing_navigation_request);
    CHECK(find_request != navigation_requests_.end());

    request = std::move(find_request->second);
    navigation_requests_.erase(committing_navigation_request);

    if (request->IsNavigationStarted()) {
      main_frame_request_ids_ = {params->request_id,
                                 request->GetGlobalRequestID()};
      if (deferred_main_frame_load_info_)
        ResourceLoadComplete(std::move(deferred_main_frame_load_info_));
    }
  }

  // The commit IPC should be associated with the URL being committed (not with
  // the *last* committed URL that most other IPCs are associated with).
  // TODO(crbug.com/1179502): Investigate where the origin should come from when
  // we remove FrameTree/FrameTreeNode members of this class, and the last
  // committed origin may be incorrect.
  ScopedActiveURL scoped_active_url(params->url,
                                    frame_tree()->root()->current_origin());

  ScopedCommitStateResetter commit_state_resetter(this);
  RenderProcessHost* process = GetProcess();

  TRACE_EVENT("navigation", "RenderFrameHostImpl::DidCommitProvisionalLoad",
              ChromeTrackEvent::kRenderFrameHost, this, "params", params);

  // If we're waiting for a cross-site beforeunload completion callback from
  // this renderer and we receive a Navigate message from the main frame, then
  // the renderer was navigating already and sent it before hearing the
  // blink::mojom::LocalFrame::StopLoading() message. Treat this as an implicit
  // beforeunload completion callback to allow the pending navigation to
  // continue.
  if (is_waiting_for_beforeunload_completion_ &&
      unload_ack_is_for_navigation_ && !GetParent()) {
    base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
    ProcessBeforeUnloadCompleted(
        /*proceed=*/true, /*treat_as_final_completion_callback=*/true,
        approx_renderer_start_time, base::TimeTicks::Now(),
        /*for_legacy=*/false);
  }

  // When a frame enters pending deletion, it waits for itself and its children
  // to properly unload. Receiving DidCommitProvisionalLoad() here while the
  // frame is not active means it comes from a navigation that reached the
  // ReadyToCommit stage just before the frame entered pending deletion.
  //
  // We should ignore this message, because we have already committed to
  // destroying this RenderFrameHost. Note that we intentionally do not ignore
  // commits that happen while the current tab is being closed - see
  // https://crbug.com/805705.
  if (IsPendingDeletion())
    return;

  if (interface_params) {
    if (broker_receiver_.is_bound()) {
      broker_receiver_.reset();
    }
    BindBrowserInterfaceBrokerReceiver(
        std::move(interface_params->browser_interface_broker_receiver));
  } else {
    // If the frame is no longer on the initial empty document, and this is not
    // a same-document navigation, then both the active document as well as the
    // global object was replaced in this browsing context. The RenderFrame
    // should have rebound its BrowserInterfaceBroker to a new pipe, but failed
    // to do so. Kill the renderer, and reset the old receiver to ensure that
    // any pending interface requests originating from the previous document,
    // hence possibly from a different security origin, will no longer be
    // dispatched.
    if (!frame_tree_node_->is_on_initial_empty_document()) {
      broker_receiver_.reset();
      bad_message::ReceivedBadMessage(
          process, bad_message::RFH_INTERFACE_PROVIDER_MISSING);
      return;
    }

    // Otherwise, it is the first real load committed, for which the RenderFrame
    // is allowed to, and will re-use the existing BrowserInterfaceBroker
    // connection if the new document is same-origin with the initial empty
    // document, and therefore the global object is not replaced.
  }

  if (!DidCommitNavigationInternal(std::move(request), std::move(params),
                                   /*same_document_params=*/nullptr)) {
    return;
  }

  // Since we didn't early return, it's safe to keep the commit state.
  commit_state_resetter.disable();

  // For a top-level frame, there are potential security concerns associated
  // with displaying graphics from a previously loaded page after the URL in
  // the omnibar has been changed. It is unappealing to clear the page
  // immediately, but if the renderer is taking a long time to issue any
  // compositor output (possibly because of script deliberately creating this
  // situation) then we clear it after a while anyway.
  // See https://crbug.com/497588.
  if (is_main_frame() && GetView()) {
    RenderWidgetHostImpl::From(GetView()->GetRenderWidgetHost())->DidNavigate();
  }

  // TODO(arthursonzogni): This can be removed when RenderDocument will be
  // implemented. See https://crbug.com/936696.
  EnsureDescendantsAreUnloading();
}

mojom::NavigationClient::CommitNavigationCallback
RenderFrameHostImpl::BuildCommitNavigationCallback(
    NavigationRequest* navigation_request) {
  DCHECK(navigation_request);
  return base::BindOnce(&RenderFrameHostImpl::DidCommitNavigation,
                        weak_ptr_factory_.GetWeakPtr(), navigation_request);
}

mojom::NavigationClient::CommitFailedNavigationCallback
RenderFrameHostImpl::BuildCommitFailedNavigationCallback(
    NavigationRequest* navigation_request) {
  DCHECK(navigation_request);
  return base::BindOnce(&RenderFrameHostImpl::DidCommitNavigation,
                        weak_ptr_factory_.GetWeakPtr(), navigation_request);
}

void RenderFrameHostImpl::SendBeforeUnload(
    bool is_reload,
    base::WeakPtr<RenderFrameHostImpl> rfh,
    bool for_legacy) {
  auto before_unload_closure = base::BindOnce(
      [](base::WeakPtr<RenderFrameHostImpl> impl, bool for_legacy, bool proceed,
         base::TimeTicks renderer_before_unload_start_time,
         base::TimeTicks renderer_before_unload_end_time) {
        if (!impl)
          return;
        impl->ProcessBeforeUnloadCompleted(
            proceed, /*treat_as_final_completion_callback=*/false,
            renderer_before_unload_start_time, renderer_before_unload_end_time,
            for_legacy);
      },
      rfh, for_legacy);
  if (for_legacy) {
    if (frame_tree_node_->navigation_request()) {
      base::UmaHistogramTimes(
          "Navigation.NavigationStartToBeforeUnloadForLegacy",
          base::TimeTicks::Now() - frame_tree_node_->navigation_request()
                                       ->common_params()
                                       .navigation_start);
    }
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(
            [](blink::mojom::LocalFrame::BeforeUnloadCallback callback,
               base::TimeTicks start_time, base::TimeTicks end_time) {
              std::move(callback).Run(/*proceed=*/true, start_time, end_time);
            },
            std::move(before_unload_closure), send_before_unload_start_time_,
            base::TimeTicks::Now()));
    return;
  }
  // Experiment to run beforeunload handlers at a higher priority in the
  // renderer. See crubug.com/1042118.
  if (base::FeatureList::IsEnabled(features::kHighPriorityBeforeUnload)) {
    rfh->GetHighPriorityLocalFrame()->DispatchBeforeUnload(
        is_reload, std::move(before_unload_closure));
  } else {
    rfh->GetAssociatedLocalFrame()->BeforeUnload(
        is_reload, std::move(before_unload_closure));
  }
}

void RenderFrameHostImpl::AddServiceWorkerContainerHost(
    const std::string& uuid,
    base::WeakPtr<content::ServiceWorkerContainerHost> host) {
  if (IsInBackForwardCache()) {
    // RenderFrameHost entered BackForwardCache before adding
    // ServiceWorkerContainerHost. In this case, evict the entry from the cache.
    EvictFromBackForwardCacheWithReason(
        BackForwardCacheMetrics::NotRestoredReason::
            kEnteredBackForwardCacheBeforeServiceWorkerHostAdded);
  }
  DCHECK(!base::Contains(service_worker_container_hosts_, uuid));
  last_committed_service_worker_host_ = host;
  service_worker_container_hosts_[uuid] = std::move(host);
}

void RenderFrameHostImpl::RemoveServiceWorkerContainerHost(
    const std::string& uuid) {
  DCHECK(!service_worker_container_hosts_.empty());
  DCHECK(base::Contains(service_worker_container_hosts_, uuid));
  service_worker_container_hosts_.erase(uuid);
}

base::WeakPtr<ServiceWorkerContainerHost>
RenderFrameHostImpl::GetLastCommittedServiceWorkerHost() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  return last_committed_service_worker_host_;
}

bool RenderFrameHostImpl::MaybeInterceptCommitCallback(
    NavigationRequest* navigation_request,
    mojom::DidCommitProvisionalLoadParamsPtr* params,
    mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params) {
  if (commit_callback_interceptor_) {
    return commit_callback_interceptor_->WillProcessDidCommitNavigation(
        navigation_request, params, interface_params);
  }
  return true;
}

void RenderFrameHostImpl::PostMessageEvent(
    const absl::optional<blink::RemoteFrameToken>& source_token,
    const std::u16string& source_origin,
    const std::u16string& target_origin,
    blink::TransferableMessage message) {
  DCHECK(is_render_frame_created());

  if (message.delegated_capability != blink::mojom::DelegatedCapability::kNone)
    ReceivedDelegatedCapability(message.delegated_capability);

  // This is always called from either another renderer (through RemoteFrame) or
  // from the embedder itself. As such, we nullify the parent task ID here, to
  // prevent this information from leaking between renderers.
  message.parent_task_id = absl::nullopt;

  GetAssociatedLocalFrame()->PostMessageEvent(
      source_token, source_origin, target_origin, std::move(message));
}

bool RenderFrameHostImpl::IsTestRenderFrameHost() const {
  return false;
}

scoped_refptr<PrefetchedSignedExchangeCache>
RenderFrameHostImpl::EnsurePrefetchedSignedExchangeCache() {
  if (!prefetched_signed_exchange_cache_) {
    prefetched_signed_exchange_cache_ =
        base::MakeRefCounted<PrefetchedSignedExchangeCache>();
  }
  return prefetched_signed_exchange_cache_;
}

void RenderFrameHostImpl::ClearPrefetchedSignedExchangeCache() {
  if (prefetched_signed_exchange_cache_)
    prefetched_signed_exchange_cache_->Clear();
}

RenderWidgetHostImpl* RenderFrameHostImpl::GetLocalRenderWidgetHost() const {
  if (is_main_frame())
    return render_view_host_->GetWidget();
  else
    return owned_render_widget_host_.get();
}

void RenderFrameHostImpl::EnsureDescendantsAreUnloading() {
  std::vector<FrameTreeNode*> frame_to_remove;
  std::vector<RenderFrameHostImpl*> rfh_to_be_checked = {this};
  while (!rfh_to_be_checked.empty()) {
    RenderFrameHostImpl* document = rfh_to_be_checked.back();
    rfh_to_be_checked.pop_back();

    // Every child is expected to be pending deletion. If it isn't the case,
    // their FrameTreeNode is immediately removed from the tree.
    for (auto& iframe : document->children_) {
      if (iframe->current_frame_host()->IsPendingDeletion()) {
        rfh_to_be_checked.push_back(iframe->current_frame_host());
      } else {
        frame_to_remove.push_back(iframe.get());
      }
    }
  }
  for (FrameTreeNode* child : frame_to_remove) {
    child->parent()->RemoveChild(child);
  }
}

void RenderFrameHostImpl::AddMessageToConsoleImpl(
    blink::mojom::ConsoleMessageLevel level,
    const std::string& message,
    bool discard_duplicates) {
  GetAssociatedLocalFrame()->AddMessageToConsole(level, message,
                                                 discard_duplicates);
}

void RenderFrameHostImpl::LogCannotCommitUrlCrashKeys(
    const GURL& url,
    bool is_same_document_navigation,
    NavigationRequest* navigation_request) {
  LogRendererKillCrashKeys(GetSiteInstance()->GetSiteInfo());

  // Temporary instrumentation to debug the root cause of renderer process
  // terminations. See https://crbug.com/931895.
  auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };

  static auto* const navigation_url_key = base::debug::AllocateCrashKeyString(
      "navigation_url", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(navigation_url_key, url.spec());

  static auto* const is_same_document_key = base::debug::AllocateCrashKeyString(
      "is_same_document", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(
      is_same_document_key, bool_to_crash_key(is_same_document_navigation));

  static auto* const is_main_frame_key = base::debug::AllocateCrashKeyString(
      "is_main_frame", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_main_frame_key,
                                 bool_to_crash_key(is_main_frame()));

  static auto* const is_outermost_frame_key =
      base::debug::AllocateCrashKeyString("is_outermost_frame",
                                          base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_outermost_frame_key,
                                 bool_to_crash_key(IsOutermostMainFrame()));

  static auto* const is_cross_process_subframe_key =
      base::debug::AllocateCrashKeyString("is_cross_process_subframe",
                                          base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_cross_process_subframe_key,
                                 bool_to_crash_key(IsCrossProcessSubframe()));

  static auto* const is_local_root_key = base::debug::AllocateCrashKeyString(
      "is_local_root", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_local_root_key,
                                 bool_to_crash_key(is_local_root()));

  static auto* const site_lock_key = base::debug::AllocateCrashKeyString(
      "site_lock", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(
      site_lock_key,
      ProcessLock::FromSiteInfo(GetSiteInstance()->GetSiteInfo()).ToString());

  static auto* const process_lock_key = base::debug::AllocateCrashKeyString(
      "process_lock", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(process_lock_key,
                                 GetProcess()->GetProcessLock().ToString());

  if (!GetSiteInstance()->IsDefaultSiteInstance()) {
    static auto* const original_url_origin_key =
        base::debug::AllocateCrashKeyString("original_url_origin",
                                            base::debug::CrashKeySize::Size256);
    base::debug::SetCrashKeyString(
        original_url_origin_key,
        GetSiteInstance()->original_url().DeprecatedGetOriginAsURL().spec());
  }

  static auto* const is_mhtml_document_key =
      base::debug::AllocateCrashKeyString("is_mhtml_document",
                                          base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_mhtml_document_key,
                                 bool_to_crash_key(is_mhtml_document()));

  static auto* const last_committed_url_origin_key =
      base::debug::AllocateCrashKeyString("last_committed_url_origin",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(
      last_committed_url_origin_key,
      GetLastCommittedURL().DeprecatedGetOriginAsURL().spec());

  static auto* const last_successful_url_origin_key =
      base::debug::AllocateCrashKeyString("last_successful_url_origin",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(
      last_successful_url_origin_key,
      last_successful_url().DeprecatedGetOriginAsURL().spec());

  if (navigation_request && navigation_request->IsNavigationStarted()) {
    static auto* const is_renderer_initiated_key =
        base::debug::AllocateCrashKeyString("is_renderer_initiated",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_renderer_initiated_key,
        bool_to_crash_key(navigation_request->IsRendererInitiated()));

    static auto* const is_server_redirect_key =
        base::debug::AllocateCrashKeyString("is_server_redirect",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_server_redirect_key,
        bool_to_crash_key(navigation_request->WasServerRedirect()));

    static auto* const is_form_submission_key =
        base::debug::AllocateCrashKeyString("is_form_submission",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_form_submission_key,
        bool_to_crash_key(navigation_request->IsFormSubmission()));

    static auto* const is_error_page_key = base::debug::AllocateCrashKeyString(
        "is_error_page", base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_error_page_key,
        bool_to_crash_key(navigation_request->IsErrorPage()));

    static auto* const from_begin_navigation_key =
        base::debug::AllocateCrashKeyString("from_begin_navigation",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        from_begin_navigation_key,
        bool_to_crash_key(navigation_request->from_begin_navigation()));

    static auto* const net_error_key = base::debug::AllocateCrashKeyString(
        "net_error", base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        net_error_key,
        base::NumberToString(navigation_request->GetNetErrorCode()));

    static auto* const initiator_origin_key =
        base::debug::AllocateCrashKeyString("initiator_origin",
                                            base::debug::CrashKeySize::Size64);
    base::debug::SetCrashKeyString(
        initiator_origin_key,
        navigation_request->GetInitiatorOrigin()
            ? navigation_request->GetInitiatorOrigin()->GetDebugString()
            : "none");

    static auto* const starting_site_instance_key =
        base::debug::AllocateCrashKeyString("starting_site_instance",
                                            base::debug::CrashKeySize::Size256);
    base::debug::SetCrashKeyString(starting_site_instance_key,
                                   navigation_request->GetStartingSiteInstance()
                                       ->GetSiteInfo()
                                       .GetDebugString());

    // Recompute the target SiteInstance to see if it matches the current
    // one at commit time.
    BrowsingContextGroupSwap ignored_bcg_swap_info =
        BrowsingContextGroupSwap::CreateDefault();
    scoped_refptr<SiteInstance> dest_instance =
        navigation_request->frame_tree_node()
            ->render_manager()
            ->GetSiteInstanceForNavigationRequest(navigation_request,
                                                  &ignored_bcg_swap_info);
    static auto* const does_recomputed_site_instance_match_key =
        base::debug::AllocateCrashKeyString(
            "does_recomputed_site_instance_match",
            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        does_recomputed_site_instance_match_key,
        bool_to_crash_key(dest_instance == GetSiteInstance()));
  }
}

int64_t CalculatePostID(
    const std::string& method,
    const scoped_refptr<network::ResourceRequestBody>& request_body,
    int64_t last_post_id,
    bool is_same_document) {
  if (method != "POST")
    return -1;
  // On same-document navigations that keep the "POST" method, use the POST ID
  // from the last navigation.
  if (is_same_document)
    return last_post_id;
  // Otherwise, this is a cross-document navigation. Use the POST ID from the
  // navigation request.
  return request_body ? request_body->identifier() : -1;
}

const std::string CalculateMethod(
    const std::string& nav_request_method,
    const std::string& last_http_method,
    bool is_same_document,
    bool is_same_document_history_api_navigation) {
  DCHECK(is_same_document || !is_same_document_history_api_navigation);
  // History API navigations are always "GET" navigations. See spec:
  // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
  if (is_same_document_history_api_navigation)
    return "GET";
  // If this is a same-document navigation that isn't triggered by the history
  // API, we should preserve the HTTP method used by the last navigation.
  if (is_same_document)
    return last_http_method;
  // Otherwise, this is a cross-document navigation. Use the method specified in
  // the navigation request.
  return nav_request_method;
}

int CalculateHTTPStatusCode(NavigationRequest* request,
                            int last_http_status_code) {
  // Same-document navigations or prerender activation navigation should retain
  // the HTTP status code from the last committed navigation.
  if (request->IsSameDocument() || request->IsPrerenderedPageActivation())
    return last_http_status_code;

  // Navigations that are served from the back/forward cache will always have
  // the HTTP status code set to 200.
  if (request->IsServedFromBackForwardCache())
    return 200;

  // The HTTP status code is not set if we never received any HTTP response for
  // the navigation.
  const int request_response_code = request->commit_params().http_response_code;
  if (request_response_code == -1)
    return 0;
  // Otherwise, return the status code from |request|.
  return request_response_code;
}

// Tries to simulate WebFrameLoadType in NavigationTypeToLoadType() in
// render_frame_impl.cc and RenderFrameImpl::CommitFailedNavigation().
// TODO(https://crbug.com/1131832): This should only be here temporarily.
// Remove this once the renderer behavior at commit time is more consistent with
// what the browser instructed it to do (e.g. reloads will always be classified
// as kReload).
RendererLoadType CalculateRendererLoadType(NavigationRequest* request,
                                           bool should_replace_current_entry,
                                           const GURL& previous_document_url) {
  const bool is_history =
      NavigationTypeUtils::IsHistory(request->common_params().navigation_type);
  const bool is_reload =
      NavigationTypeUtils::IsReload(request->common_params().navigation_type);
  const bool has_valid_page_state = blink::PageState::CreateFromEncodedData(
                                        request->commit_params().page_state)
                                        .IsValid();
  const bool is_error_document = request->DidEncounterError();

  // Predict if the renderer classified the navigation as a "back/forward"
  // navigation (WebFrameLoadType::kBackForward).
  bool will_be_classified_as_back_forward_navigation = false;
  if (is_error_document) {
    // For error documents, whenever the navigation has a valid PageState, it
    // will be considered as a back/forward navigation. This includes history
    // navigations and restores. See RenderFrameImpl's CommitFailedNavigation().
    will_be_classified_as_back_forward_navigation = has_valid_page_state;
  } else {
    // For normal navigations, RenderFrameImpl's NavigationTypeToLoadType()
    // will classify a navigation as kBackForward if it's a history navigation.
    will_be_classified_as_back_forward_navigation = is_history;
  }

  if (will_be_classified_as_back_forward_navigation) {
    // If the navigation is classified as kBackForward, it can't be changed to
    // another RendererLoadType below, so we can immediately return here.
    return RendererLoadType::kBackForward;
  }

  if (!is_error_document && is_reload) {
    // For non-error documents, if the NavigationType given by the browser is
    // a reload, then the navigation will be classified as a reload.
    return RendererLoadType::kReload;
  }

  return should_replace_current_entry ? RendererLoadType::kReplaceCurrentItem
                                      : RendererLoadType::kStandard;
}

bool CalculateDidCreateNewEntry(NavigationRequest* request,
                                bool should_replace_current_entry,
                                RendererLoadType renderer_load_type) {
  // This function tries to simulate the calculation of |did_create_new_entry|
  // in RenderFrameImpl::MakeDidCommitProvisionalLoadParams().
  // Standard navigations will always create a new entry.
  if (renderer_load_type == RendererLoadType::kStandard)
    return true;

  // Back/Forward navigations won't create a new entry.
  if (renderer_load_type == RendererLoadType::kBackForward)
    return false;

  // Otherwise, |did_create_new_entry| is true only for main frame
  // cross-document replacements.
  return request->IsInMainFrame() && should_replace_current_entry &&
         !request->IsSameDocument();
}

ui::PageTransition CalculateTransition(
    NavigationRequest* request,
    RendererLoadType renderer_load_type,
    const mojom::DidCommitProvisionalLoadParams& params,
    bool is_in_fenced_frame_tree) {
  if (is_in_fenced_frame_tree) {
    // Navigations inside fenced frame trees do not add session history items
    // and must be marked with PAGE_TRANSITION_AUTO_SUBFRAME. This is set
    // regardless of the `is_main_frame` value since this is inside a fenced
    // frame tree and should behave the same as iframes. Also preserve client
    // redirects if they were set.
    ui::PageTransition transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
    if (request->IsInMainFrame() && !request->DidEncounterError() &&
        request->common_params().transition &
            ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
      transition =
          ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_SUBFRAME |
                                    ui::PAGE_TRANSITION_CLIENT_REDIRECT);
    }

    return transition;
  }
  if (request->IsSameDocument())
    return params.transition;
  if (request->IsInMainFrame()) {
    // This follows GetTransitionType() in render_frame_impl.cc.
    ui::PageTransition supplied_transition =
        ui::PageTransitionFromInt(request->common_params().transition);
    if (ui::PageTransitionCoreTypeIs(supplied_transition,
                                     ui::PAGE_TRANSITION_LINK) &&
        request->common_params().post_data) {
      return ui::PAGE_TRANSITION_FORM_SUBMIT;
    }
    return supplied_transition;
  }
  // This follows RenderFrameImpl::MakeDidCommitProvisionalLoadParams().
  if (renderer_load_type == RendererLoadType::kStandard)
    return ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
  return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
}

// Calculates the "loading" URL for a given navigation. This tries to replicate
// RenderFrameImpl::GetLoadingUrl() and is used to predict the value of "url" in
// DidCommitProvisionalLoadParams.
GURL CalculateLoadingURL(
    NavigationRequest* request,
    const mojom::DidCommitProvisionalLoadParams& params,
    const RenderFrameHostImpl::RendererURLInfo& last_renderer_url_info,
    bool last_document_is_error_document,
    const GURL& last_committed_url) {
  if (params.url.IsAboutBlank() && params.url.ref_piece() == "blocked") {
    // Some navigations can still be blocked by the renderer during the commit,
    // changing the URL to "about:blank#blocked". Currently we have no way of
    // predicting this in the browser, so just return the URL given by the
    // renderer in this case.
    // TODO(https://crbug.com/1131832): Block the navigations in the browser
    // instead and remove |params| as a parameter to this function.
    return params.url;
  }

  if (!request->common_params().url.is_valid()) {
    // Empty URL (and invalid URLs, which are converted to the empty URL due
    // to IPC URL reparsing) will be rewritten to "about:blank" in the renderer.
    // TODO(https://crbug.com/1131832): Do the rewrite in the browser.
    return GURL(url::kAboutBlankURL);
  }

  if (request->IsSameDocument() &&
      (last_document_is_error_document ||
       last_renderer_url_info.was_loaded_from_load_data_with_base_url)) {
    // Documents that have an "override" URL (loadDataWithBaseURL navigations,
    // error documents) will continue using that URL even after same-document
    // navigations.
    return last_committed_url;
  }

  // For all other navigations, the returned URL should be the same as the URL
  // in CommonNavigationParams.
  return request->common_params().url;
}

bool ShouldVerify(const std::string& param) {
#if DCHECK_IS_ON()
  return true;
#else
  if (param != "should_replace_current_entry" &&
      param != "url_is_unreachable") {
    // All params except the two above have no known complications and should be
    // compared by default.
    return true;
  }
  return GetFieldTrialParamByFeatureAsBool(features::kVerifyDidCommitParams,
                                           param, false);
#endif
}

void LogVerifyDidCommitParamsDifference(
    VerifyDidCommitParamsDifference difference) {
  UMA_HISTOGRAM_ENUMERATION("Navigation.VerifyDidCommitParams", difference);
}

std::string GetURLTypeForCrashKey(const GURL& url) {
  if (url == kUnreachableWebDataURL)
    return "error";
  if (url == kBlockedURL)
    return "blocked";
  if (url.IsAboutBlank())
    return "about:blank";
  if (url.IsAboutSrcdoc())
    return "about:srcdoc";
  if (url.is_empty())
    return "empty";
  if (!url.is_valid())
    return "invalid";
  return url.scheme();
}

std::string GetURLRelationForCrashKey(
    const GURL& actual_url,
    const GURL& predicted_url,
    const blink::mojom::CommonNavigationParams& common_params,
    const GURL& last_committed_url,
    const RenderFrameHostImpl::RendererURLInfo& renderer_url_info) {
  if (actual_url == predicted_url)
    return "as predicted";
  if (actual_url == last_committed_url)
    return "last committed";
  if (actual_url == common_params.url)
    return "common params URL";
  if (actual_url == common_params.base_url_for_data_url)
    return "base URL";
  if (actual_url == renderer_url_info.last_document_url)
    return "last document URL";
  return "unknown";
}

void RenderFrameHostImpl::
    VerifyThatBrowserAndRendererCalculatedDidCommitParamsMatch(
        NavigationRequest* request,
        const mojom::DidCommitProvisionalLoadParams& params,
        const mojom::DidCommitSameDocumentNavigationParamsPtr
            same_document_params) {
#if !DCHECK_IS_ON()
  // Only check for the flag if DCHECK is not enabled, so that we will always
  // verify the params for tests.
  if (!base::FeatureList::IsEnabled(features::kVerifyDidCommitParams))
    return;
#endif
  // Check if these values from DidCommitProvisionalLoadParams sent by the
  // renderer can be calculated entirely in the browser side:
  // - method
  // - url_is_unreachable
  // - post_id
  // - is_overriding_user_agent
  // - http_status_code
  // - should_update_history
  // - url
  // - did_create_new_entry
  // - transition
  // - history_list_was_cleared
  // - origin
  // TODO(crbug.com/1131832): Verify more params.
  // We can know if we're going to be in an error document after this navigation
  // if the net error code is not net::OK, or if we're doing a same-document
  // navigation on an error document (only possible for renderer-initiated
  // navigations).
  const bool is_error_document =
      (request->DidEncounterError() ||
       (is_error_document_ && request->IsSameDocument()));

  const bool browser_url_is_unreachable = is_error_document;

  const bool is_same_document_navigation = !!same_document_params;
  const bool is_same_document_history_api_navigation =
      same_document_params &&
      same_document_params->same_document_navigation_type ==
          blink::mojom::SameDocumentNavigationType::kHistoryApi;
  DCHECK_EQ(is_same_document_navigation, request->IsSameDocument());

  const int64_t browser_post_id =
      CalculatePostID(params.method, request->common_params().post_data,
                      last_post_id_, is_same_document_navigation);

  const std::string& browser_method = CalculateMethod(
      request->common_params().method, last_http_method_,
      is_same_document_navigation, is_same_document_history_api_navigation);

  const bool browser_is_overriding_user_agent =
      is_same_document_navigation
          ? is_overriding_user_agent_
          : (request->commit_params().is_overriding_user_agent &&
             request->frame_tree_node()->IsMainFrame());

  const int browser_http_status_code =
      CalculateHTTPStatusCode(request, last_http_status_code_);

  // Note that this follows the calculation of should_update_history in
  // RenderFrameImpl::MakeDidCommitProvisionalLoadParams().
  // TODO(https://crbug.com/1158101): Reconsider how we calculate
  // should_update_history.
  const bool browser_should_update_history =
      !browser_url_is_unreachable && browser_http_status_code != 404;

  const bool should_replace_current_entry =
      request->common_params().should_replace_current_entry;

  const GURL browser_url =
      CalculateLoadingURL(request, params, renderer_url_info_,
                          is_error_document_, last_committed_url_);

  const RendererLoadType renderer_load_type =
      CalculateRendererLoadType(request, should_replace_current_entry,
                                renderer_url_info_.last_document_url);

  const bool browser_did_create_new_entry =
      request->is_synchronous_renderer_commit()
          ? params.did_create_new_entry
          : CalculateDidCreateNewEntry(request, should_replace_current_entry,
                                       renderer_load_type);

  const ui::PageTransition browser_transition = CalculateTransition(
      request, renderer_load_type, params, IsNestedWithinFencedFrame());

  const bool browser_history_list_was_cleared =
      request->commit_params().should_clear_history_list;

  const bool everything_except_origin_matches =
      ((!ShouldVerify("method") || browser_method == params.method) &&
       (!ShouldVerify("url_is_unreachable") ||
        browser_url_is_unreachable == params.url_is_unreachable) &&
       (!ShouldVerify("post_id") || browser_post_id == params.post_id) &&
       (!ShouldVerify("is_overriding_user_agent") ||
        browser_is_overriding_user_agent == params.is_overriding_user_agent) &&
       (!ShouldVerify("http_status_code") ||
        browser_http_status_code == params.http_status_code) &&
       (!ShouldVerify("should_update_history") ||
        browser_should_update_history == params.should_update_history) &&
       (!ShouldVerify("url") || browser_url == params.url) &&
       (!ShouldVerify("did_create_new_entry") ||
        browser_did_create_new_entry == params.did_create_new_entry) &&
       (!ShouldVerify("transition") ||
        ui::PageTransitionTypeIncludingQualifiersIs(browser_transition,
                                                    params.transition)) &&
       (!ShouldVerify("history_list_was_cleared") ||
        browser_history_list_was_cleared == params.history_list_was_cleared));
  if (everything_except_origin_matches &&
      (!ShouldVerify("origin") ||
       request->state() < NavigationRequest::WILL_PROCESS_RESPONSE ||
       request->GetOriginToCommit() == params.origin)) {
    // Don't do a DumpWithoutCrashing if everything matches. Note that we save
    // `everything_except_origin_matches` separately so that we can skip doing
    // a DumpWithoutCrashing at the end of this function if the origin turns
    // out to match (actual origin match checking is done below as it does its
    // own DumpWithoutCrashing).
    return;
  }

  SCOPED_CRASH_KEY_BOOL(
      "VerifyDidCommit", "prev_ldwb",
      renderer_url_info_.was_loaded_from_load_data_with_base_url);
  SCOPED_CRASH_KEY_STRING32(
      "VerifyDidCommit", "base_url_fdu_type",
      GetURLTypeForCrashKey(request->common_params().base_url_for_data_url));
#if BUILDFLAG(IS_ANDROID)
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "data_url_empty",
                        request->commit_params().data_url_as_string.empty());
#endif

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "intended_as_new_entry",
                        request->commit_params().intended_as_new_entry);

  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "method_browser",
                            browser_method);
  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "method_renderer",
                            params.method);
  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "original_method",
                            request->commit_params().original_method);
  // For WebView, since we don't want to log potential PIIs.
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "method_post_browser",
                        browser_method == "POST");
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "method_post_renderer",
                        params.method == "POST");
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "original_method_post",
                        request->commit_params().original_method == "POST");

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "unreachable_browser",
                        browser_url_is_unreachable);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "unreachable_renderer",
                        params.url_is_unreachable);

  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "post_id_browser",
                          browser_post_id);
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "post_id_renderer",
                          params.post_id);
  // For WebView, since we don't want to log IDs.
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "post_id_matches",
                        browser_post_id == params.post_id);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "post_id_-1_browser",
                        browser_post_id == -1);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "post_id_-1_renderer",
                        params.post_id == -1);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "override_ua_browser",
                        browser_is_overriding_user_agent);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "override_ua_renderer",
                        params.is_overriding_user_agent);

  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "code_browser",
                          browser_http_status_code);
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "code_renderer",
                          params.http_status_code);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "suh_browser",
                        browser_should_update_history);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "suh_renderer",
                        params.should_update_history);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "gesture",
                        request->common_params().has_user_gesture);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "replace",
                        should_replace_current_entry);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "create_browser",
                        browser_did_create_new_entry);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "create_renderer",
                        params.did_create_new_entry);

  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "transition_browser",
                          browser_transition);
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "transition_renderer",
                          params.transition);

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "cleared_browser",
                        browser_history_list_was_cleared);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "cleared_renderer",
                        params.history_list_was_cleared);

  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "url_browser",
                             browser_url.possibly_invalid_spec());
  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "url_renderer",
                             params.url.possibly_invalid_spec());

  SCOPED_CRASH_KEY_STRING32(
      "VerifyDidCommit", "url_relation",
      GetURLRelationForCrashKey(params.url, browser_url,
                                request->common_params(), GetLastCommittedURL(),
                                renderer_url_info_));
  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "url_browser_type",
                            GetURLTypeForCrashKey(browser_url));
  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "url_renderer_type",
                            GetURLTypeForCrashKey(params.url));

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_same_document",
                        is_same_document_navigation);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_history_api",
                        is_same_document_history_api_navigation);
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "renderer_initiated",
                        request->IsRendererInitiated());
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_subframe",
                        !request->frame_tree_node()->IsMainFrame());
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_form_submission",
                        request->IsFormSubmission());
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_error_document",
                        is_error_document);
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "net_error",
                          request->GetNetErrorCode());

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "is_server_redirect",
                        request->WasServerRedirect());
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "redirects_size",
                          request->GetRedirectChain().size());

  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "entry_offset",
                          request->GetNavigationEntryOffset());
  SCOPED_CRASH_KEY_NUMBER(
      "VerifyDidCommit", "entry_count",
      request->frame_tree_node()->frame_tree().controller().GetEntryCount());
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "last_committed_index",
                          request->frame_tree_node()
                              ->frame_tree()
                              .controller()
                              .GetLastCommittedEntryIndex());

  SCOPED_CRASH_KEY_BOOL(
      "VerifyDidCommit", "is_reload",
      NavigationTypeUtils::IsReload(request->common_params().navigation_type));
  SCOPED_CRASH_KEY_BOOL(
      "VerifyDidCommit", "is_restore",
      NavigationTypeUtils::IsRestore(request->common_params().navigation_type));
  SCOPED_CRASH_KEY_BOOL(
      "VerifyDidCommit", "is_history",
      NavigationTypeUtils::IsHistory(request->common_params().navigation_type));
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "has_valid_page_state",
                        blink::PageState::CreateFromEncodedData(
                            request->commit_params().page_state)
                            .IsValid());

  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "has_gesture",
                        request->HasUserGesture());
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "was_click",
                        request->WasInitiatedByLinkClick());

  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "original_req_url",
                             request->commit_params().original_url.spec());
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "original_same_doc",
                        request->commit_params().original_url.EqualsIgnoringRef(
                            GetLastCommittedURL()));

  SCOPED_CRASH_KEY_BOOL(
      "VerifyDidCommit", "on_initial_empty_doc",
      request->frame_tree_node()->is_on_initial_empty_document());

  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "last_committed_url",
                             GetLastCommittedURL().spec());
  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "last_document_url",
                             renderer_url_info_.last_document_url.spec());
  SCOPED_CRASH_KEY_STRING32("VerifyDidCommit", "last_url_type",
                            GetURLTypeForCrashKey(GetLastCommittedURL()));

  SCOPED_CRASH_KEY_STRING256("VerifyDidCommit", "last_method",
                             last_http_method_);
  SCOPED_CRASH_KEY_NUMBER("VerifyDidCommit", "last_code",
                          last_http_status_code_);

  bool has_original_url =
      GetSiteInstance() && !GetSiteInstance()->IsDefaultSiteInstance();
  SCOPED_CRASH_KEY_STRING256(
      "VerifyDidCommit", "si_url",
      has_original_url
          ? GetSiteInstance()->original_url().possibly_invalid_spec()
          : "");
  SCOPED_CRASH_KEY_BOOL("VerifyDidCommit", "has_si_url", has_original_url);

  // These DCHECKs ensure that tests will fail if we got here, as
  // DumpWithoutCrashing won't fail tests.
  DCHECK_EQ(browser_method, params.method);
  DCHECK_EQ(browser_url_is_unreachable, params.url_is_unreachable);
  DCHECK_EQ(browser_post_id, params.post_id);
  DCHECK_EQ(browser_is_overriding_user_agent, params.is_overriding_user_agent);
  DCHECK_EQ(browser_http_status_code, params.http_status_code);
  DCHECK_EQ(browser_should_update_history, params.should_update_history);
  DCHECK_EQ(browser_url, params.url);
  DCHECK_EQ(browser_did_create_new_entry, params.did_create_new_entry);
  DCHECK(ui::PageTransitionTypeIncludingQualifiersIs(browser_transition,
                                                     params.transition));
  DCHECK_EQ(browser_history_list_was_cleared, params.history_list_was_cleared);

  // Log histograms to trigger Chrometto slow reports, allowing us to see traces
  // to analyze what happened in these navigations.
  if (browser_method != params.method) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kMethod);
  }
  if (browser_url_is_unreachable != params.url_is_unreachable) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kURLIsUnreachable);
  }
  if (browser_post_id != params.post_id) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kPostID);
  }
  if (browser_is_overriding_user_agent != params.is_overriding_user_agent) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kIsOverridingUserAgent);
  }
  if (browser_http_status_code != params.http_status_code) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kHTTPStatusCode);
  }
  if (browser_should_update_history != params.should_update_history) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kShouldUpdateHistory);
  }
  if (browser_url != params.url) {
    LogVerifyDidCommitParamsDifference(VerifyDidCommitParamsDifference::kURL);
  }
  if (browser_did_create_new_entry != params.did_create_new_entry) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kDidCreateNewEntry);
  }
  if (!ui::PageTransitionTypeIncludingQualifiersIs(browser_transition,
                                                   params.transition)) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kTransition);
  }
  if (browser_history_list_was_cleared != params.history_list_was_cleared) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kHistoryListWasCleared);
  }
  // TODO(https://crbug.com/888079): The origin computed from the browser must
  // match the one reported from the renderer process.
  if (!VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(request,
                                                                  params)) {
    LogVerifyDidCommitParamsDifference(
        VerifyDidCommitParamsDifference::kOrigin);
  }

  if (!everything_except_origin_matches) {
    // It's possible to get here when everything except the origin matches.
    // If the origin doesn't match, we would do a DumpWithoutCrashing above.
    // So, don't do a DumpWithoutCrashing unless there's another param that
    // doesn't match.
    // Note: This is temporarily disabled on Android as there has been a recent
    // spike of reports on Android WebView.
#if !BUILDFLAG(IS_ANDROID)
    base::debug::DumpWithoutCrashing();
#endif  // !BUILDFLAG(IS_ANDROID)
  }
}

BackForwardCacheImpl& RenderFrameHostImpl::GetBackForwardCache() {
  return GetOutermostMainFrame()
      ->frame_tree()
      ->controller()
      .GetBackForwardCache();
}

FrameTreeNode* RenderFrameHostImpl::GetFrameTreeNodeForUnload() {
  DCHECK(IsPendingDeletion());
  return frame_tree_node_;
}

void RenderFrameHostImpl::MaybeEvictFromBackForwardCache() {
  if (!IsInBackForwardCache())
    return;

  RenderFrameHostImpl* outermost_main_frame = GetOutermostMainFrame();
  BackForwardCacheCanStoreDocumentResultWithTree bfcache_eligibility =
      GetBackForwardCache().GetCurrentBackForwardCacheEligibility(
          outermost_main_frame);

  TRACE_EVENT("navigation",
              "RenderFrameHostImpl::MaybeEvictFromBackForwardCache",
              "render_frame_host", this, "bfcache_eligibility",
              bfcache_eligibility.flattened_reasons.ToString());

  if (bfcache_eligibility.CanStore())
    return;
  EvictFromBackForwardCacheWithFlattenedAndTreeReasons(bfcache_eligibility);
}

void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
    const GURL& url,
    const url::Origin& origin,
    const ProcessLock& process_lock,
    bool is_same_document_navigation,
    NavigationRequest* navigation_request) {
  LogRendererKillCrashKeys(GetSiteInstance()->GetSiteInfo());

  // Temporary instrumentation to debug the root cause of
  // https://crbug.com/923144.
  auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };

  static auto* const target_url_key = base::debug::AllocateCrashKeyString(
      "target_url", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(target_url_key, url.spec());

  static auto* const target_origin_key = base::debug::AllocateCrashKeyString(
      "target_origin", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(target_origin_key, origin.GetDebugString());

  const url::Origin url_origin = url::Origin::Resolve(url, origin);
  const auto target_url_origin_tuple_or_precursor_tuple =
      url_origin.GetTupleOrPrecursorTupleIfOpaque();
  static auto* const target_url_origin_tuple_key =
      base::debug::AllocateCrashKeyString("target_url_origin_tuple",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(
      target_url_origin_tuple_key,
      target_url_origin_tuple_or_precursor_tuple.Serialize());

  const auto target_origin_tuple_or_precursor_tuple =
      origin.GetTupleOrPrecursorTupleIfOpaque();
  static auto* const target_origin_tuple_key =
      base::debug::AllocateCrashKeyString("target_origin_tuple",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(
      target_origin_tuple_key,
      target_origin_tuple_or_precursor_tuple.Serialize());

  static auto* const last_committed_url_key =
      base::debug::AllocateCrashKeyString("last_committed_url",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(last_committed_url_key,
                                 GetLastCommittedURL().spec());

  static auto* const last_committed_origin_key =
      base::debug::AllocateCrashKeyString("last_committed_origin",
                                          base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(last_committed_origin_key,
                                 GetLastCommittedOrigin().GetDebugString());

  static auto* const process_lock_key = base::debug::AllocateCrashKeyString(
      "process_lock", base::debug::CrashKeySize::Size256);
  base::debug::SetCrashKeyString(process_lock_key, process_lock.ToString());

  static auto* const is_same_document_key = base::debug::AllocateCrashKeyString(
      "is_same_document", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(
      is_same_document_key, bool_to_crash_key(is_same_document_navigation));

  static auto* const is_subframe_key = base::debug::AllocateCrashKeyString(
      "is_subframe", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_subframe_key,
                                 bool_to_crash_key(GetMainFrame() != this));

  static auto* const lifecycle_state_key = base::debug::AllocateCrashKeyString(
      "lifecycle_state", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(lifecycle_state_key,
                                 LifecycleStateImplToString(lifecycle_state()));

  static auto* const is_active_key = base::debug::AllocateCrashKeyString(
      "is_active", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_active_key, bool_to_crash_key(IsActive()));

  static auto* const is_cross_process_subframe_key =
      base::debug::AllocateCrashKeyString("is_cross_process_subframe",
                                          base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_cross_process_subframe_key,
                                 bool_to_crash_key(IsCrossProcessSubframe()));

  static auto* const is_local_root_key = base::debug::AllocateCrashKeyString(
      "is_local_root", base::debug::CrashKeySize::Size32);
  base::debug::SetCrashKeyString(is_local_root_key,
                                 bool_to_crash_key(is_local_root()));

  if (navigation_request && navigation_request->IsNavigationStarted()) {
    static auto* const is_renderer_initiated_key =
        base::debug::AllocateCrashKeyString("is_renderer_initiated",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_renderer_initiated_key,
        bool_to_crash_key(navigation_request->IsRendererInitiated()));

    static auto* const is_server_redirect_key =
        base::debug::AllocateCrashKeyString("is_server_redirect",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_server_redirect_key,
        bool_to_crash_key(navigation_request->WasServerRedirect()));

    static auto* const is_form_submission_key =
        base::debug::AllocateCrashKeyString("is_form_submission",
                                            base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_form_submission_key,
        bool_to_crash_key(navigation_request->IsFormSubmission()));

    static auto* const is_error_page_key = base::debug::AllocateCrashKeyString(
        "is_error_page", base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(
        is_error_page_key,
        bool_to_crash_key(navigation_request->IsErrorPage()));

    static auto* const initiator_origin_key =
        base::debug::AllocateCrashKeyString("initiator_origin",
                                            base::debug::CrashKeySize::Size64);
    base::debug::SetCrashKeyString(
        initiator_origin_key,
        navigation_request->GetInitiatorOrigin()
            ? navigation_request->GetInitiatorOrigin()->GetDebugString()
            : "none");

    static auto* const starting_site_instance_key =
        base::debug::AllocateCrashKeyString("starting_site_instance",
                                            base::debug::CrashKeySize::Size256);
    base::debug::SetCrashKeyString(starting_site_instance_key,
                                   navigation_request->GetStartingSiteInstance()
                                       ->GetSiteInfo()
                                       .GetDebugString());
  }
}

void RenderFrameHostImpl::EnableMojoJsBindings(
    content::mojom::ExtraMojoJsFeaturesPtr features) {
  // This method should only be called on RenderFrameHost which is for a WebUI.
  DCHECK_NE(WebUI::kNoWebUI,
            WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
                GetSiteInstance()->GetBrowserContext(),
                site_instance_->GetSiteInfo().site_url()));

  GetFrameBindingsControl()->EnableMojoJsBindings(std::move(features));
}

void RenderFrameHostImpl::EnableMojoJsBindingsWithBroker(
    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker) {
  // This method should only be called on RenderFrameHost that has an associated
  // WebUI, because it needs to transfer the broker's ownership to its
  // WebUIController. EnableMojoJsBindings does this differently and can be
  // called before the WebUI object is created.
  DCHECK(GetWebUI());
  GetFrameBindingsControl()->EnableMojoJsBindingsWithBroker(std::move(broker));
}

bool RenderFrameHostImpl::IsOutermostMainFrame() const {
  return !GetParentOrOuterDocument();
}

void RenderFrameHostImpl::SetIsLoadingForRendererDebugURL() {
  LoadingState previous_frame_tree_loading_state =
      frame_tree()->LoadingTree()->GetLoadingState();
  loading_state_ = LoadingState::LOADING_UI_REQUESTED;
  owner_->DidStartLoading(previous_frame_tree_loading_state);
}

BackForwardCacheMetrics* RenderFrameHostImpl::GetBackForwardCacheMetrics() {
  NavigationEntryImpl* navigation_entry =
      frame_tree()->controller().GetEntryWithUniqueID(nav_entry_id());
  if (!navigation_entry)
    return nullptr;
  return navigation_entry->back_forward_cache_metrics();
}

bool RenderFrameHostImpl::IsBackForwardCacheDisabled() const {
  return back_forward_cache_disabled_reasons_.size();
}

bool RenderFrameHostImpl::IsDOMContentLoaded() {
  return document_associated_data_->dom_content_loaded();
}

void RenderFrameHostImpl::UpdateIsAdFrame(bool is_ad_frame) {
  browsing_context_state_->SetIsAdFrame(is_ad_frame);
}

#if BUILDFLAG(IS_ANDROID)
std::pair<blink::mojom::AuthenticatorStatus, bool>
RenderFrameHostImpl::PerformGetAssertionWebAuthSecurityChecks(
    const std::string& relying_party_id,
    const url::Origin& effective_origin,
    bool is_payment_credential_get_assertion,
    const blink::mojom::RemoteDesktopClientOverridePtr&
        remote_desktop_client_override) {
  bool is_cross_origin = true;  // Will be reset in ValidateAncestorOrigins().

  WebAuthRequestSecurityChecker::RequestType request_type =
      is_payment_credential_get_assertion
          ? WebAuthRequestSecurityChecker::RequestType::
                kGetPaymentCredentialAssertion
          : WebAuthRequestSecurityChecker::RequestType::kGetAssertion;
  blink::mojom::AuthenticatorStatus status =
      GetWebAuthRequestSecurityChecker()->ValidateAncestorOrigins(
          effective_origin, request_type, &is_cross_origin);
  if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
    return std::make_pair(status, is_cross_origin);
  }

  if (!GetContentClient()
           ->browser()
           ->GetWebAuthenticationDelegate()
           ->IsSecurityLevelAcceptableForWebAuthn(this, effective_origin)) {
    return std::make_pair(blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR,
                          is_cross_origin);
  }

  status = GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(
      effective_origin, relying_party_id, request_type,
      remote_desktop_client_override);
  return std::make_pair(status, is_cross_origin);
}

blink::mojom::AuthenticatorStatus
RenderFrameHostImpl::PerformMakeCredentialWebAuthSecurityChecks(
    const std::string& relying_party_id,
    const url::Origin& effective_origin,
    bool is_payment_credential_creation,
    const blink::mojom::RemoteDesktopClientOverridePtr&
        remote_desktop_client_override) {
  bool is_cross_origin;

  WebAuthRequestSecurityChecker::RequestType request_type =
      is_payment_credential_creation
          ? WebAuthRequestSecurityChecker::RequestType::kMakePaymentCredential
          : WebAuthRequestSecurityChecker::RequestType::kMakeCredential;
  blink::mojom::AuthenticatorStatus status =
      GetWebAuthRequestSecurityChecker()->ValidateAncestorOrigins(
          effective_origin, request_type, &is_cross_origin);
  if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
    return status;
  }

  if (!GetContentClient()
           ->browser()
           ->GetWebAuthenticationDelegate()
           ->IsSecurityLevelAcceptableForWebAuthn(this, effective_origin)) {
    return blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR;
  }

  status = GetWebAuthRequestSecurityChecker()->ValidateDomainAndRelyingPartyID(
      effective_origin, relying_party_id, request_type,
      remote_desktop_client_override);
  if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
    return status;
  }

  return blink::mojom::AuthenticatorStatus::SUCCESS;
}
#endif

const blink::DocumentToken& RenderFrameHostImpl::GetDocumentToken() const {
  DCHECK_NE(LifecycleStateImpl::kPendingCommit, lifecycle_state());
  DCHECK_NE(LifecycleStateImpl::kSpeculative, lifecycle_state());

  return GetDocumentTokenIgnoringSafetyRestrictions();
}

const blink::DocumentToken*
RenderFrameHostImpl::GetDocumentTokenForCrossDocumentNavigationReuse(
    base::PassKey<NavigationRequest>) {
  if (ShouldResetDocumentAssociatedDataAtCommit()) {
    return nullptr;
  }

  return &document_associated_data_->token();
}

void RenderFrameHostImpl::ReinitializeDocumentAssociatedDataForReuseAfterCrash(
    base::PassKey<RenderFrameHostManager>) {
  DCHECK(is_main_frame());
  DCHECK_EQ(RenderFrameState::kDeleted, render_frame_state_);

  // Clear all the document-associated data for this RenderFrameHost when its
  // RenderFrame is recreated after a crash. Note that the user data is
  // intentionally not cleared at the time of crash. Please refer to
  // https://crbug.com/1099237 for more details.
  //
  // Clearing of user data should be called before RenderFrameCreated to
  // ensure:
  // - a) the new state set in RenderFrameCreated doesn't get deleted.
  // - b) the old state is not leaked to a new RenderFrameHost.
  document_associated_data_.emplace(*this, blink::DocumentToken());
}

void RenderFrameHostImpl::ReinitializeDocumentAssociatedDataForTesting() {
  document_associated_data_.emplace(*this, blink::DocumentToken());
}

void RenderFrameHostImpl::IsClipboardPasteContentAllowed(
    const ui::ClipboardFormatType& data_type,
    ClipboardPasteData clipboard_paste_data,
    IsClipboardPasteContentAllowedCallback callback) {
  delegate_->IsClipboardPasteContentAllowed(GetLastCommittedURL(), data_type,
                                            std::move(clipboard_paste_data),
                                            std::move(callback));
}

RenderFrameHostImpl* RenderFrameHostImpl::GetParentOrOuterDocument() const {
  return frame_tree_node()->GetParentOrOuterDocumentHelper(
      /*escape_guest_view=*/false, /*include_prospective=*/true);
}

RenderFrameHostImpl* RenderFrameHostImpl::GetParentOrOuterDocumentOrEmbedder()
    const {
  return frame_tree_node()->GetParentOrOuterDocumentHelper(
      /*escape_guest_view=*/true, /*include_prospective=*/true);
}

RenderFrameHostImpl* RenderFrameHostImpl::GetOutermostMainFrameOrEmbedder() {
  RenderFrameHostImpl* current = this;
  while (RenderFrameHostImpl* parent =
             current->GetParentOrOuterDocumentOrEmbedder()) {
    current = parent;
  }
  return current;
}

RenderFrameHostImpl* RenderFrameHostImpl::
    GetParentOrOuterDocumentOrEmbedderExcludingProspectiveOwners() const {
  return frame_tree_node()->GetParentOrOuterDocumentHelper(
      /*escape_guest_view=*/true, /*include_prospective=*/false);
}

RenderFrameHostImpl* RenderFrameHostImpl::
    GetOutermostMainFrameOrEmbedderExcludingProspectiveOwners() {
  RenderFrameHostImpl* current = this;
  while (
      RenderFrameHostImpl* parent =
          current
              ->GetParentOrOuterDocumentOrEmbedderExcludingProspectiveOwners()) {
    current = parent;
  }
  return current;
}

scoped_refptr<WebAuthRequestSecurityChecker>
RenderFrameHostImpl::GetWebAuthRequestSecurityChecker() {
  if (!webauth_request_security_checker_)
    webauth_request_security_checker_ =
        base::MakeRefCounted<WebAuthRequestSecurityChecker>(this);

  return webauth_request_security_checker_;
}

bool RenderFrameHostImpl::IsInBackForwardCache() const {
  return lifecycle_state() == LifecycleStateImpl::kInBackForwardCache;
}

bool RenderFrameHostImpl::IsPendingDeletion() const {
  return lifecycle_state() == LifecycleStateImpl::kRunningUnloadHandlers ||
         lifecycle_state() == LifecycleStateImpl::kReadyToBeDeleted;
}

void RenderFrameHostImpl::SetLifecycleState(LifecycleStateImpl new_state) {
  TRACE_EVENT2("content", "RenderFrameHostImpl::SetLifecycleState",
               "render_frame_host", this, "new_state",
               LifecycleStateImplToString(new_state));
  // Finish the slice corresponding to the old lifecycle state and begin a new
  // slice for the lifecycle state we are transitioning to.
  TRACE_EVENT_END("navigation", perfetto::Track::FromPointer(this));
  TRACE_EVENT_BEGIN(
      "navigation",
      perfetto::StaticString{LifecycleStateImplToString(new_state)},
      perfetto::Track::FromPointer(this));
// TODO(crbug.com/1256898): Consider associating expectations with each
// transitions.
#if DCHECK_IS_ON()
  static const base::NoDestructor<base::StateTransitions<LifecycleStateImpl>>
      allowed_transitions(
          // For a graph of state transitions, see
          // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/render-frame-host-lifecycle-state.png
          // To update the graph, see the corresponding .gv file.

          // RenderFrameHost is only set speculative during its creation and no
          // transitions happen to this state during its lifetime.
          base::StateTransitions<LifecycleStateImpl>({
              {LifecycleStateImpl::kSpeculative,
               {LifecycleStateImpl::kActive,
                LifecycleStateImpl::kPendingCommit}},

              {LifecycleStateImpl::kPendingCommit,
               {LifecycleStateImpl::kPrerendering, LifecycleStateImpl::kActive,
                LifecycleStateImpl::kReadyToBeDeleted}},

              {LifecycleStateImpl::kPrerendering,
               {LifecycleStateImpl::kActive,
                LifecycleStateImpl::kRunningUnloadHandlers,
                LifecycleStateImpl::kReadyToBeDeleted}},

              {LifecycleStateImpl::kActive,
               {LifecycleStateImpl::kInBackForwardCache,
                LifecycleStateImpl::kRunningUnloadHandlers,
                LifecycleStateImpl::kReadyToBeDeleted}},

              {LifecycleStateImpl::kInBackForwardCache,
               {LifecycleStateImpl::kActive,
                LifecycleStateImpl::kReadyToBeDeleted}},

              {LifecycleStateImpl::kRunningUnloadHandlers,
               {LifecycleStateImpl::kReadyToBeDeleted}},

              {LifecycleStateImpl::kReadyToBeDeleted, {}},
          }));
  DCHECK_STATE_TRANSITION(allowed_transitions,
                          /*old_state=*/lifecycle_state_,
                          /*new_state=*/new_state);
#endif  // DCHECK_IS_ON()

  // TODO(crbug.com/1256896): Use switch-case to make this more readable.
  // If the RenderFrameHost is restored from BackForwardCache or is part of a
  // prerender activation, update states of all the children to kActive.
  if (new_state == LifecycleStateImpl::kActive) {
    if (lifecycle_state_ == LifecycleStateImpl::kPendingCommit ||
        lifecycle_state_ == LifecycleStateImpl::kSpeculative) {
      // Newly-created documents shouldn't have children, as child creation
      // happens after commit.
      DCHECK(children_.empty());
    }
    if (GetOutermostMainFrameOrEmbedder() == this &&
        (lifecycle_state() == LifecycleStateImpl::kInBackForwardCache ||
         lifecycle_state() == LifecycleStateImpl::kPrerendering)) {
      // We mark all the children, including inner FrameTrees and delegate
      // nodes to have the same lifecycle state.
      FrameTree::NodeRange node_range = FrameTree::SubtreeAndInnerTreeNodes(
          this,
          /*include_delegate_nodes_for_inner_frame_trees=*/true);
      FrameTree::NodeIterator node_iter = node_range.begin();
      while (node_iter != node_range.end()) {
        FrameTreeNode* ftn = *node_iter;
        // TODO(crbug.com/1232528): Inner WebContents do not know about
        // prerendering state so skip them for now. This check does not
        // impact back/forward cache since inner WebContents aren't allowed.
        if (ftn->IsOutermostMainFrame()) {
          DCHECK(ftn->current_frame_host()->lifecycle_state() !=
                 LifecycleStateImpl::kInBackForwardCache);
          node_iter.AdvanceSkippingChildren();
        } else {
          RenderFrameHostImpl* rfh = ftn->current_frame_host();
          DCHECK_EQ(rfh->lifecycle_state(), lifecycle_state_);
          rfh->SetLifecycleState(new_state);
          ++node_iter;
        }
      }
    }
  }

  if (lifecycle_state() == LifecycleStateImpl::kInBackForwardCache)
    was_restored_from_back_forward_cache_for_debugging_ = true;

  if (new_state == LifecycleStateImpl::kPendingCommit ||
      new_state == LifecycleStateImpl::kPrerendering) {
    DCHECK(children_.empty());
  }

  LifecycleStateImpl old_state = lifecycle_state_;
  lifecycle_state_ = new_state;

  // Unset the |has_pending_lifecycle_state_update_| value once the
  // LifecycleStateImpl is updated.
  if (has_pending_lifecycle_state_update_) {
    DCHECK(lifecycle_state() == LifecycleStateImpl::kInBackForwardCache ||
           IsPendingDeletion() ||
           old_state == LifecycleStateImpl::kPrerendering)
        << "Transitioned to unexpected state with resetting "
           "|has_pending_lifecycle_state_update_|\n ";
    has_pending_lifecycle_state_update_ = false;
  }

  // As kSpeculative state is not exposed to embedders, we can ignore the
  // transitions out of kSpeculative state while notifying delegate.
  if (old_state != LifecycleStateImpl::kSpeculative) {
    LifecycleState old_lifecycle_state = GetLifecycleStateFromImpl(old_state);
    LifecycleState new_lifecycle_state = GetLifecycleState();

    // Old and new lifecycle states can be equal due to the same LifecycleState
    // representing multiple LifecycleStateImpls, for example the
    // kPendingDeletion state. Don't notify the observers in such cases.
    if (old_lifecycle_state != new_lifecycle_state) {
      delegate_->RenderFrameHostStateChanged(this, old_lifecycle_state,
                                             new_lifecycle_state);
    }
  }

  if (new_state == LifecycleStateImpl::kActive &&
      old_state == LifecycleStateImpl::kInBackForwardCache) {
    if (auto* permission_service_context =
            PermissionServiceContext::GetForCurrentDocument(this)) {
      permission_service_context->NotifyPermissionStatusChangedIfNeeded();
    }
  }
}

void RenderFrameHostImpl::RecordDocumentCreatedUkmEvent(
    const url::Origin& origin,
    const ukm::SourceId document_ukm_source_id,
    ukm::UkmRecorder* ukm_recorder,
    bool only_record_identifiability_metric) {
  DCHECK(ukm_recorder);
  if (document_ukm_source_id == ukm::kInvalidSourceId)
    return;

  // Compares the subframe origin with the main frame origin. In the case of
  // nested subframes such as A(B(A)), the bottom-most frame A is expected to
  // have |is_cross_origin_frame| set to false, even though this frame is cross-
  // origin from its parent frame B. This value is only used in manual analysis.
  bool is_cross_origin_frame =
      !IsOutermostMainFrame() &&
      !GetOutermostMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
          origin);

  // Compares the subframe site with the main frame site. In the case of
  // nested subframes such as A(B(A)), the bottom-most frame A is expected to
  // have |is_cross_site_frame| set to false, even though this frame is cross-
  // site from its parent frame B. This value is only used in manual analysis.
  bool is_cross_site_frame =
      !IsOutermostMainFrame() &&
      (net::SchemefulSite(origin) !=
       net::SchemefulSite(GetOutermostMainFrame()->GetLastCommittedOrigin()));

  bool is_main_frame = IsOutermostMainFrame();

  ukm::SourceId navigation_ukm_source_id = GetPageUkmSourceId();

  RecordIdentifiabilityDocumentCreatedMetrics(
      document_ukm_source_id, ukm_recorder, navigation_ukm_source_id,
      is_cross_origin_frame, is_cross_site_frame, is_main_frame);

  if (only_record_identifiability_metric)
    return;

  ukm::builders::DocumentCreated(document_ukm_source_id)
      .SetNavigationSourceId(navigation_ukm_source_id)
      .SetIsMainFrame(is_main_frame)
      .SetIsCrossOriginFrame(is_cross_origin_frame)
      .SetIsCrossSiteFrame(is_cross_site_frame)
      .Record(ukm_recorder);
}

void RenderFrameHostImpl::BindReportingObserver(
    mojo::PendingReceiver<blink::mojom::ReportingObserver> receiver) {
  GetAssociatedLocalFrame()->BindReportingObserver(std::move(receiver));
}

mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
RenderFrameHostImpl::CreateURLLoaderNetworkObserver() {
  return GetStoragePartition()->CreateURLLoaderNetworkObserverForFrame(
      GetProcess()->GetID(), GetRoutingID());
}

PeerConnectionTrackerHost& RenderFrameHostImpl::GetPeerConnectionTrackerHost() {
  return *PeerConnectionTrackerHost::GetOrCreateForCurrentDocument(this);
}

void RenderFrameHostImpl::BindPeerConnectionTrackerHost(
    mojo::PendingReceiver<blink::mojom::PeerConnectionTrackerHost> receiver) {
  GetPeerConnectionTrackerHost().BindReceiver(std::move(receiver));
}

void RenderFrameHostImpl::EnableWebRtcEventLogOutput(int lid,
                                                     int output_period_ms) {
  GetPeerConnectionTrackerHost().StartEventLog(lid, output_period_ms);
}

void RenderFrameHostImpl::DisableWebRtcEventLogOutput(int lid) {
  GetPeerConnectionTrackerHost().StopEventLog(lid);
}

bool RenderFrameHostImpl::IsDocumentOnLoadCompletedInMainFrame() {
  return GetPage().is_on_load_completed_in_main_document();
}

// TODO(crbug.com/1192003): Move this method to content::Page when available.
const std::vector<blink::mojom::FaviconURLPtr>&
RenderFrameHostImpl::FaviconURLs() {
  return GetPage().favicon_urls();
}

mojo::PendingRemote<network::mojom::CookieAccessObserver>
RenderFrameHostImpl::CreateCookieAccessObserver() {
  mojo::PendingRemote<network::mojom::CookieAccessObserver> remote;
  cookie_observers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

mojo::PendingRemote<network::mojom::TrustTokenAccessObserver>
RenderFrameHostImpl::CreateTrustTokenAccessObserver() {
  mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> remote;
  trust_token_observers_.Add(this, remote.InitWithNewPipeAndPassReceiver());
  return remote;
}

#if BUILDFLAG(ENABLE_MDNS)
void RenderFrameHostImpl::CreateMdnsResponder(
    mojo::PendingReceiver<network::mojom::MdnsResponder> receiver) {
  GetStoragePartition()->GetNetworkContext()->CreateMdnsResponder(
      std::move(receiver));
}
#endif  // BUILDFLAG(ENABLE_MDNS)

void RenderFrameHostImpl::Clone(
    mojo::PendingReceiver<network::mojom::CookieAccessObserver> observer) {
  cookie_observers_.Add(this, std::move(observer));
}

void RenderFrameHostImpl::Clone(
    mojo::PendingReceiver<network::mojom::TrustTokenAccessObserver> observer) {
  trust_token_observers_.Add(this, std::move(observer));
}

void RenderFrameHostImpl::OnCookiesAccessed(
    std::vector<network::mojom::CookieAccessDetailsPtr> details_vector) {
  for (auto& details : details_vector) {
    EmitCookieWarningsAndMetrics(this, details);

    CookieAccessDetails allowed;
    CookieAccessDetails blocked;
    SplitCookiesIntoAllowedAndBlocked(details, &allowed, &blocked);
    if (!allowed.cookie_list.empty()) {
      delegate_->OnCookiesAccessed(this, allowed);
    }
    if (!blocked.cookie_list.empty()) {
      delegate_->OnCookiesAccessed(this, blocked);
    }
  }
}

void RenderFrameHostImpl::OnTrustTokensAccessed(
    network::mojom::TrustTokenAccessDetailsPtr details) {
  delegate_->OnTrustTokensAccessed(this, TrustTokenAccessDetails(details));
}

void RenderFrameHostImpl::SetEmbeddingToken(
    const base::UnguessableToken& embedding_token) {
  // Everything in this method depends on whether the embedding token has
  // actually changed, including setting the AXTreeID (backed by the token).
  if (embedding_token_ == embedding_token)
    return;
  embedding_token_ = embedding_token;

  // The AXTreeID of a frame is backed by its embedding token, so we need to
  // update its AXTreeID, as well as the associated mapping in
  // AXActionHandlerRegistry.
  const ui::AXTreeID old_id = GetAXTreeID();
  ui::AXTreeID ax_tree_id = ui::AXTreeID::FromToken(embedding_token);
  DCHECK_NE(old_id, ax_tree_id);
  SetAXTreeID(ax_tree_id);
  needs_ax_root_id_ = true;
  ui::AXActionHandlerRegistry::GetInstance()->SetFrameIDForAXTreeID(
      ui::AXActionHandlerRegistry::FrameID(GetProcess()->GetID(), routing_id_),
      ax_tree_id);

  // Also important to notify the delegate so that the relevant observers can
  // adapt to the fact that the AXTreeID has changed for the primary main frame
  // (e.g. the WebView needs to update the ID tracking its child accessibility
  // tree).
  if (IsInPrimaryMainFrame())
    delegate_->AXTreeIDForMainFrameHasChanged();

  // Propagate the embedding token to the RenderFrameProxyHost representing the
  // parent frame if needed, that is, if either this is a cross-process subframe
  // or the main frame of an inner web contents (i.e. would need to send it to
  // the RenderFrameProxyHost for the outer web contents owning the inner one).
  PropagateEmbeddingTokenToParentFrame();

  // The accessibility tree for the outermost root frame contains references
  // to the focused frame via its AXTreeID, so ensure that we update that.
  // For frames in a prerendering frame tree, they should never have focus, so
  // the outermost frame does not need to update the references.
  RenderFrameHostImpl* outermost = GetOutermostMainFrameOrEmbedder();
  DCHECK(outermost);
  DCHECK(lifecycle_state_ != LifecycleStateImpl::kPrerendering ||
         outermost->GetFocusedAXTreeID() != GetAXTreeID());

  if (outermost != this &&
      lifecycle_state_ != LifecycleStateImpl::kPrerendering) {
    outermost->UpdateAXTreeData();
  }

  // Finally, since the AXTreeID changed, we have to ensure the
  // BrowserAccessibilityManager gets a new tree as well.
  if (browser_accessibility_manager_) {
    browser_accessibility_manager_->DetachFromParentManager();
    browser_accessibility_manager_.reset();
  }
}

bool RenderFrameHostImpl::DocumentUsedWebOTP() {
  return document_used_web_otp_;
}

void RenderFrameHostImpl::SetFrameTreeNode(FrameTreeNode& frame_tree_node) {
  devtools_instrumentation::WillSwapFrameTreeNode(*frame_tree_node_,
                                                  frame_tree_node);
  frame_tree_node_ = &frame_tree_node;
  SetFrameTree(frame_tree_node_->frame_tree());
  // Setting the FrameTreeNode is only done for FrameTree/FrameTreeNode swaps
  // in MPArch (specifically prerender activation). This is to ensure that
  // fields such as proxies and ReplicationState are copied over correctly. In
  // the new functionality for swapping BrowsingContext on cross
  // BrowsingInstance navigations, the BrowsingContextState is the only field
  // that will need to be swapped.
  switch (features::GetBrowsingContextMode()) {
    case (features::BrowsingContextStateImplementationType::
              kLegacyOneToOneWithFrameTreeNode):
      browsing_context_state_ = frame_tree_node_->render_manager()
                                    ->current_frame_host()
                                    ->browsing_context_state();
      break;
    case (features::BrowsingContextStateImplementationType::
              kSwapForCrossBrowsingInstanceNavigations):
      // TODO(crbug.com/1270671): implement functionality for swapping on cross
      // browsing instance navigations as needed. This will likely be removed
      // once BrowsingContextState is decoupled from FrameTreeNode.
      break;
  }
}

void RenderFrameHostImpl::SetFrameTree(FrameTree& frame_tree) {
  DCHECK_EQ(&frame_tree_node_->frame_tree(), &frame_tree);
  frame_tree_ = &frame_tree;
  render_view_host()->SetFrameTree(frame_tree);
  if (owned_render_widget_host_) {
    owned_render_widget_host_->SetFrameTree(frame_tree);
  }
}

void RenderFrameHostImpl::SetPolicyContainerForEarlyCommitAfterCrash(
    scoped_refptr<PolicyContainerHost> policy_container_host) {
  DCHECK_EQ(lifecycle_state(), LifecycleStateImpl::kSpeculative);
  DCHECK(!policy_container_host_);
  SetPolicyContainerHost(std::move(policy_container_host));
}

void RenderFrameHostImpl::OnDidRunInsecureContent(const GURL& security_origin,
                                                  const GURL& target_url) {
  OPTIONAL_TRACE_EVENT2("content", "RenderFrameHostImpl::DidRunInsecureContent",
                        "security_origin", security_origin, "target_url",
                        target_url);

  RecordAction(base::UserMetricsAction("SSL.RanInsecureContent"));
  if (base::EndsWith(security_origin.spec(), kDotGoogleDotCom,
                     base::CompareCase::INSENSITIVE_ASCII)) {
    RecordAction(base::UserMetricsAction("SSL.RanInsecureContentGoogle"));
  }
  frame_tree_->controller().ssl_manager()->DidRunMixedContent(security_origin);
}

void RenderFrameHostImpl::OnDidRunContentWithCertificateErrors() {
  OPTIONAL_TRACE_EVENT0(
      "content", "RenderFrameHostImpl::OnDidRunContentWithCertificateErrors");
  // For RenderFrameHosts that are inactive and going to be discarded, we can
  // disregard this message; there's no need to update the UI if the UI will
  // never be shown again.
  //
  // We still process this message for pending-commit RenderFrameHosts. This can
  // happen when a subframe's main resource has a certificate error. The
  // origin for the last committed navigation entry will get marked as having
  // run insecure content and that will carry over to the navigation entry for
  // the pending-commit RenderFrameHost when it commits.
  //
  // Generally our approach for active content with certificate errors follows
  // our approach for mixed content (DidRunInsecureContent): when a page loads
  // active insecure content, such as a script or iframe, the top-level origin
  // gets marked as insecure and that applies to any navigation entry using the
  // same renderer process with that same top-level origin.
  //
  // We shouldn't be receiving this message for speculative RenderFrameHosts
  // i.e., before the renderer is told to commit the navigation.
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
  if (lifecycle_state() != LifecycleStateImpl::kPendingCommit &&
      IsInactiveAndDisallowActivation(
          DisallowActivationReasonId::kCertificateErrors)) {
    return;
  }
  // To update mixed content status in a fenced frame, we should call
  // an outer frame's OnDidRunContentWithCertificateErrors.
  // Otherwise, no update can be processed from fenced frames since they have
  // their own NavigationController"
  if (IsNestedWithinFencedFrame()) {
    GetParentOrOuterDocument()->OnDidRunContentWithCertificateErrors();
    return;
  }
  frame_tree_->controller().ssl_manager()->DidRunContentWithCertErrors(
      GetMainFrame()->GetLastCommittedOrigin().GetURL());
}

void RenderFrameHostImpl::OnDidDisplayContentWithCertificateErrors() {
  OPTIONAL_TRACE_EVENT0(
      "content",
      "RenderFrameHostImpl::OnDidDisplayContentWithCertificateErrors");
  frame_tree_->controller().ssl_manager()->DidDisplayContentWithCertErrors();
}

void RenderFrameHostImpl::IncreaseCommitNavigationCounter() {
  if (commit_navigation_sent_counter_ < std::numeric_limits<int>::max())
    ++commit_navigation_sent_counter_;
  else
    commit_navigation_sent_counter_ = 0;
}

bool RenderFrameHostImpl::ShouldWaitForUnloadHandlers() const {
  return has_unload_handlers() && !IsInBackForwardCache();
}

void RenderFrameHostImpl::AssertNonSpeculativeFrame() const {
  if (LIKELY(lifecycle_state() != LifecycleStateImpl::kSpeculative))
    return;

  NOTREACHED();
  base::debug::DumpWithoutCrashing();
}

void RenderFrameHostImpl::AssertBrowserContextShutdownHasntStarted() {
  if (LIKELY(!GetBrowserContext()->ShutdownStarted()))
    return;

  std::string debug_string = ToDebugString();
  SCOPED_CRASH_KEY_STRING256("shutdown", "frame->ToDebugString", debug_string);
  NOTREACHED() << "BrowserContext->ShutdownStarted() without first closing all "
               << "WebContents; debug_string = " << debug_string;
  base::debug::DumpWithoutCrashing();
}

blink::StorageKey RenderFrameHostImpl::GetBucketStorageKey() {
  return storage_key_;
}

blink::mojom::PermissionStatus RenderFrameHostImpl::GetPermissionStatus(
    blink::PermissionType permission_type) {
  return GetBrowserContext()
      ->GetPermissionController()
      ->GetPermissionStatusForCurrentDocument(permission_type, this);
}

void RenderFrameHostImpl::BindCacheStorageForBucket(
    const storage::BucketInfo& bucket,
    mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
  BindCacheStorageInternal(std::move(receiver), bucket.ToBucketLocator());
}

void RenderFrameHostImpl::GetSandboxedFileSystemForBucket(
    const storage::BucketInfo& bucket,
    blink::mojom::BucketHost::GetDirectoryCallback callback) {
  GetStoragePartition()->GetFileSystemAccessManager()->GetSandboxedFileSystem(
      FileSystemAccessManagerImpl::BindingContext(
          storage_key(), GetLastCommittedURL(), GetGlobalId()),
      bucket.ToBucketLocator(), std::move(callback));
}

GlobalRenderFrameHostId RenderFrameHostImpl::GetAssociatedRenderFrameHostId()
    const {
  return GetGlobalId();
}

RenderFrameHostImpl::DocumentAssociatedData::DocumentAssociatedData(
    RenderFrameHostImpl& document,
    const blink::DocumentToken& token)
    : token_(token), weak_factory_(&document) {
  auto [_, inserted] = GetDocumentTokenMap().insert({token_, &document});
  CHECK(inserted);

  // Only create page object for the main document as the PageImpl is 1:1 with
  // main document.
  if (!document.GetParent()) {
    PageDelegate* page_delegate = document.frame_tree()->page_delegate();
    DCHECK(page_delegate);
    owned_page_ = PageFactory::Create(document, *page_delegate);
  }
}

RenderFrameHostImpl::DocumentAssociatedData::~DocumentAssociatedData() {
  while (!services_.empty()) {
    // DocumentServiceBase unregisters itself at destruction time.
    services_.back()->WillBeDestroyed(
        DocumentServiceDestructionReason::kEndOfDocumentLifetime);
    services_.back()->ResetAndDeleteThis();
  }

  // Explicitly clear all user data here, so that the other fields of
  // DocumentAssociatedData are still valid while user data is being destroyed.
  ClearAllUserData();

  // Last in case any DocumentService / DocumentUserData service destructors try
  // to look up RenderFrameHosts by DocumentToken.
  CHECK_EQ(1u, GetDocumentTokenMap().erase(token_));
}

void RenderFrameHostImpl::DocumentAssociatedData::
    set_navigation_or_document_handle(
        scoped_refptr<NavigationOrDocumentHandle> handle) {
  navigation_or_document_handle_ = std::move(handle);
}

std::ostream& operator<<(std::ostream& o,
                         const RenderFrameHostImpl::LifecycleStateImpl& s) {
  return o << RenderFrameHostImpl::LifecycleStateImplToString(s);
}

net::CookieSettingOverrides RenderFrameHostImpl::GetCookieSettingOverrides() {
  // This shouldn't be called before committing the document.
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
  DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kPendingCommit);
  auto subresource_loader_factories_config =
      SubresourceLoaderFactoriesConfig::ForLastCommittedNavigation(*this);
  return subresource_loader_factories_config.cookie_setting_overrides();
}

RenderFrameHostImpl::CookieChangeListener::CookieChangeListener(
    StoragePartition* storage_partition,
    GURL& url) {
  DCHECK(storage_partition);
  auto* cookie_manager = storage_partition->GetCookieManagerForBrowserProcess();
  cookie_manager->AddCookieChangeListener(
      url, absl::nullopt,
      cookie_change_listener_receiver_.BindNewPipeAndPassRemote());
}

RenderFrameHostImpl::CookieChangeListener::~CookieChangeListener() = default;

void RenderFrameHostImpl::CookieChangeListener::OnCookieChange(
    const net::CookieChangeInfo& change) {
  // TODO (https://crbug.com/1399741): After adding the invalidation signals
  // API, we could mark the page as ineligible for BFCache as soon as the cookie
  // change event is received after the navigation is committed.
  cookie_change_info_.cookie_modification_count_++;
  if (change.cookie.IsHttpOnly()) {
    cookie_change_info_.http_only_cookie_modification_count_++;
  }
}

RenderFrameHostImpl::CookieChangeListener::CookieChangeInfo
RenderFrameHostImpl::GetCookieChangeInfo() {
  return cookie_change_listener_ ? cookie_change_listener_->cookie_change_info()
                                 : CookieChangeListener::CookieChangeInfo{};
}

// TODO(crbug.com/1386190): Follow up on whether any inner page scenarios
// (eg. portals, guestview) require escaping out using GetOutermostMainFrame
// or GetOutermostMainFrameOrEmbedder.
bool RenderFrameHostImpl::GetIsThirdPartyCookiesUserBypassEnabled() {
  auto* document_data =
      RuntimeFeatureStateDocumentData::GetForCurrentDocument(GetMainFrame());
  if (!document_data) {
    return false;
  }
  blink::RuntimeFeatureStateReadContext read_context =
      document_data->runtime_feature_state_read_context();
  return read_context.IsThirdPartyCookiesUserBypassEnabled();
}

void RenderFrameHostImpl::SetResourceCacheRemote(
    mojo::PendingRemote<blink::mojom::ResourceCache> pending_remote) {
  GetMojomFrameInRenderer()->SetResourceCache(std::move(pending_remote));
}

#ifdef OHOS_DRAG_DROP
void RenderFrameHostImpl::OnClearContextMenu() {
  if (IsInactiveAndDisallowActivation(DisallowActivationReasonId::kShowContextMenu))
    return;
  delegate_->ClearContextMenu();
}
#endif // OHOS_DRAG_DROP

#ifdef OHOS_ARKWEB_ADBLOCK
void RenderFrameHostImpl::UpdateAdBlockEnabledToRender(
    bool site_adblock_enabled) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  FrameTreeNode* tree_node = frame_tree_node();
  if (!tree_node->IsMainFrame()) {
    return;
  }
  // Update the adblock site switch of local_frame_root and
  // activation_state.activation_level of subresource_filter_agent
  // in the rendering process only by the main frame.
  RenderFrameHostImpl* pending_frame_host =
      tree_node->render_manager()->speculative_frame_host();
  if (pending_frame_host && pending_frame_host->frame_) {
    LOG(INFO) << "[AdBlock] Speculative update adblock site switch:"
              << site_adblock_enabled;
    pending_frame_host->frame_->OnUpdateAdBlockEnabledToRender(
        site_adblock_enabled);
  }

  RenderFrameHostImpl* current_frame_host = tree_node->current_frame_host();
  if (current_frame_host && current_frame_host->frame_) {
    LOG(INFO) << "[AdBlock] Update adblock site switch:"
              << site_adblock_enabled;
    current_frame_host->frame_->OnUpdateAdBlockEnabledToRender(
        site_adblock_enabled);
  }
}
#endif
}  // namespace content