// 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/renderer/render_frame_impl.h"

#include <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/to_vector.h"
#include "base/debug/alias.h"
#include "base/debug/asan_invalid_access.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/trace_event.h"
#include "base/types/optional_util.h"
#include "base/unguessable_token.h"
#include "base/uuid.h"
#include "base/values.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/content_switches_internal.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/main_frame_counter.h"
#include "content/common/navigation_client.mojom.h"
#include "content/common/navigation_gesture.h"
#include "content/common/navigation_params_utils.h"
#include "content/common/renderer_host.mojom.h"
#include "content/common/web_package/signed_exchange_utils.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_constants.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/url_constants.h"
#include "content/public/common/url_utils.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_visitor.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "content/public/renderer/window_features_converter.h"
#include "content/renderer/accessibility/ax_tree_snapshotter_impl.h"
#include "content/renderer/accessibility/render_accessibility_impl.h"
#include "content/renderer/accessibility/render_accessibility_manager.h"
#include "content/renderer/agent_scheduling_group.h"
#include "content/renderer/background_resource_fetch_assets.h"
#include "content/renderer/content_security_policy_util.h"
#include "content/renderer/document_state.h"
#include "content/renderer/dom_automation_controller.h"
#include "content/renderer/effective_connection_type_helper.h"
#include "content/renderer/frame_owner_properties_converter.h"
#include "content/renderer/gpu_benchmarking_extension.h"
#include "content/renderer/local_resource_url_loader_factory.h"
#include "content/renderer/media/media_permission_dispatcher.h"
#include "content/renderer/mhtml_handle_writer.h"
#include "content/renderer/mojo/blink_interface_registry_impl.h"
#include "content/renderer/navigation_client.h"
#include "content/renderer/navigation_state.h"
#include "content/renderer/pepper/pepper_audio_controller.h"
#include "content/renderer/policy_container_util.h"
#include "content/renderer/render_process.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/renderer_blink_platform_impl.h"
#include "content/renderer/service_worker/service_worker_network_provider_for_frame.h"
#include "content/renderer/service_worker/web_service_worker_provider_impl.h"
#include "content/renderer/skia_benchmarking_extension.h"
#include "content/renderer/stats_collection_controller.h"
#include "content/renderer/v8_value_converter_impl.h"
#include "content/renderer/web_ui_extension.h"
#include "content/renderer/web_ui_extension_data.h"
#include "content/renderer/worker/dedicated_worker_host_factory_client.h"
#include "crypto/sha2.h"
#include "ipc/ipc_message.h"
#include "media/mojo/mojom/audio_processing.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/data_url.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/not_implemented_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/context_menu_data/context_menu_data.h"
#include "third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/interest_group/ad_auction_constants.h"
#include "third_party/blink/public/common/loader/loader_constants.h"
#include "third_party/blink/public/common/loader/record_load_histograms.h"
#include "third_party/blink/public/common/loader/resource_type_util.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/common/navigation/impression.h"
#include "third_party/blink/public/common/navigation/navigation_params.h"
#include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
#include "third_party/blink/public/common/navigation/navigation_policy.h"
#include "third_party/blink/public/common/page_state/page_state.h"
#include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/blob/blob.mojom.h"
#include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.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/user_activation_notification_type.mojom.h"
#include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
#include "third_party/blink/public/mojom/frame/view_transition_state.mojom.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-shared.h"
#include "third_party/blink/public/mojom/loader/fetch_later.mojom.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom.h"
#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
#include "third_party/blink/public/mojom/page/widget.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission.mojom.h"
#include "third_party/blink/public/mojom/render_accessibility.mojom.h"
#include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
#include "third_party/blink/public/mojom/widget/platform_widget.mojom.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/platform/tracked_child_url_loader_factory_bundle.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h"
#include "third_party/blink/public/platform/web_data.h"
#include "third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h"
#include "third_party/blink/public/platform/web_http_body.h"
#include "third_party/blink/public/platform/web_media_player.h"
#include "third_party/blink/public/platform/web_media_player_source.h"
#include "third_party/blink/public/platform/web_navigation_body_loader.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_request_extra_data.h"
#include "third_party/blink/public/platform/web_url_request_util.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/public/web/modules/media/audio/audio_device_factory.h"
#include "third_party/blink/public/web/modules/media/audio/audio_output_ipc_factory.h"
#include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h"
#include "third_party/blink/public/web/web_autofill_client.h"
#include "third_party/blink/public/web/web_console_message.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element_collection.h"
#include "third_party/blink/public/web/web_frame_owner_properties.h"
#include "third_party/blink/public/web/web_frame_serializer.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_input_method_controller.h"
#include "third_party/blink/public/web/web_link_preview_triggerer.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_navigation_control.h"
#include "third_party/blink/public/web/web_navigation_policy.h"
#include "third_party/blink/public/web/web_navigation_timings.h"
#include "third_party/blink/public/web/web_navigation_type.h"
#include "third_party/blink/public/web/web_performance_metrics_for_nested_contexts.h"
#include "third_party/blink/public/web/web_picture_in_picture_window_options.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_plugin_container.h"
#include "third_party/blink/public/web/web_plugin_document.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#include "third_party/blink/public/web/web_range.h"
#include "third_party/blink/public/web/web_remote_frame.h"
#include "third_party/blink/public/web/web_savable_resources_test_support.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_searchable_form_data.h"
#include "third_party/blink/public/web/web_security_policy.h"
#include "third_party/blink/public/web/web_serialized_script_value.h"
#include "third_party/blink/public/web/web_v8_features.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_widget.h"
#include "third_party/blink/public/web/web_window_features.h"
#include "ui/accessibility/ax_tree_update.h"
#include "ui/events/base_event_utils.h"
#include "url/origin.h"
#include "url/url_constants.h"
#include "url/url_util.h"
#include "v8/include/v8-isolate.h"
#include "v8/include/v8-local-handle.h"
#include "v8/include/v8-microtask-queue.h"

#if BUILDFLAG(ENABLE_PPAPI)
#include "content/renderer/pepper/pepper_browser_connection.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "content/renderer/pepper/pepper_plugin_registry.h"
#include "content/renderer/pepper/pepper_webplugin_impl.h"
#include "content/renderer/pepper/plugin_module.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include <cpu-features.h>

#include "content/renderer/java/gin_java_bridge_dispatcher.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#endif

using base::Time;
using blink::ContextMenuData;
using blink::WebContentDecryptionModule;
using blink::WebData;
using blink::WebDocument;
using blink::WebDocumentLoader;
using blink::WebDOMMessageEvent;
using blink::WebElement;
using blink::WebElementCollection;
using blink::WebFrame;
using blink::WebFrameLoadType;
using blink::WebFrameSerializer;
using blink::WebFrameSerializerClient;
using blink::WebHistoryItem;
using blink::WebHTTPBody;
using blink::WebLocalFrame;
using blink::WebMediaPlayer;
using blink::WebMediaPlayerClient;
using blink::WebMediaPlayerEncryptedMediaClient;
using blink::WebNavigationParams;
using blink::WebNavigationPolicy;
using blink::WebNavigationType;
using blink::WebNode;
using blink::WebPluginDocument;
using blink::WebPluginParams;
using blink::WebRange;
using blink::WebScriptSource;
using blink::WebSearchableFormData;
using blink::WebSecurityOrigin;
using blink::WebSecurityPolicy;
using blink::WebSerializedScriptValue;
using blink::WebServiceWorkerProvider;
using blink::WebString;
using blink::WebThreadSafeData;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLRequest;
using blink::WebURLResponse;
using blink::WebView;
using blink::mojom::SelectionMenuBehavior;
using network::mojom::ReferrerPolicy;

namespace content {

namespace {

const int kExtraCharsBeforeAndAfterSelection = 100;
const size_t kMaxURLLogChars = 1024;
const char kCommitRenderFrame[] = "Navigation.CommitRenderFrame";

// Time, in seconds, we delay before sending content state changes (such as form
// state and scroll position) to the browser. We delay sending changes to avoid
// spamming the browser.
// To avoid having tab/session restore require sending a message to get the
// current content state during tab closing we use a shorter timeout for the
// foreground renderer. This means there is a small window of time from which
// content state is modified and not sent to session restore, but this is
// better than having to wake up all renderers during shutdown.
constexpr base::TimeDelta kDelaySecondsForContentStateSyncHidden =
    base::Seconds(5);
constexpr base::TimeDelta kDelaySecondsForContentStateSync = base::Seconds(1);

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap;
static base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit
    g_routing_id_frame_map = LAZY_INSTANCE_INITIALIZER;
#endif

typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap;
base::LazyInstance<FrameMap>::DestructorAtExit g_frame_map =
    LAZY_INSTANCE_INITIALIZER;

// Please keep in sync with "RendererBlockedURLReason" in
// tools/metrics/histograms/metadata/navigation/enums.xml. These values are
// persisted to logs. Entries should not be renumbered and numeric values should
// never be reused.
enum class RendererBlockedURLReason {
  kInvalidURL = 0,
  kTooLongURL = 1,
  kBadAboutURL = 2,

  kMaxValue = kBadAboutURL
};

int64_t ExtractPostId(const WebHistoryItem& item) {
  if (item.IsNull() || item.HttpBody().IsNull())
    return -1;

  return item.HttpBody().Identifier();
}

std::string TrimURL(const std::string& url) {
  if (url.length() <= kMaxURLLogChars)
    return url;
  return url.substr(0, kMaxURLLogChars - 3) + "...";
}

// Calculates transition type based on navigation parameters. Used
// during navigation, before WebDocumentLoader is available.
ui::PageTransition GetTransitionType(ui::PageTransition default_transition,
                                     bool replaces_current_item,
                                     bool is_main_frame,
                                     bool is_in_fenced_frame_tree,
                                     WebNavigationType navigation_type) {
  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.
    return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
  }
  if (replaces_current_item && !is_main_frame) {
    // Subframe navigations that don't add session history items must be
    // marked with AUTO_SUBFRAME. See also DidFailProvisionalLoad for how we
    // handle loading of error pages.
    return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
  }
  bool is_form_submit =
      navigation_type == blink::kWebNavigationTypeFormSubmitted ||
      navigation_type == blink::kWebNavigationTypeFormResubmittedBackForward ||
      navigation_type == blink::kWebNavigationTypeFormResubmittedReload;
  if (ui::PageTransitionCoreTypeIs(default_transition,
                                   ui::PAGE_TRANSITION_LINK) &&
      is_form_submit) {
    return ui::PAGE_TRANSITION_FORM_SUBMIT;
  }
  return default_transition;
}

// Calculates transition type for the specific document loaded using
// WebDocumentLoader. Used while loading subresources.
ui::PageTransition GetTransitionType(blink::WebDocumentLoader* document_loader,
                                     bool is_main_frame,
                                     bool is_in_fenced_frame_tree) {
  NavigationState* navigation_state =
      DocumentState::FromDocumentLoader(document_loader)->navigation_state();
  ui::PageTransition default_transition =
      navigation_state->IsForSynchronousCommit()
          ? ui::PAGE_TRANSITION_LINK
          : ui::PageTransitionFromInt(
                navigation_state->common_params().transition);
  if (!is_in_fenced_frame_tree && navigation_state->WasWithinSameDocument())
    return default_transition;
  return GetTransitionType(default_transition,
                           document_loader->ReplacesCurrentHistoryItem(),
                           is_main_frame, is_in_fenced_frame_tree,
                           document_loader->GetNavigationType());
}

// Ensure that the renderer does not send commit URLs to the browser process
// that are known to be unsupported. Ideally these would be caught earlier in
// Blink and not get this far. Histograms are reported (similar to those in
// RenderProcessHostImpl::FilterURL) to track the cases that should be handled
// earlier. See https://crbug.com/40066983.
bool IsValidCommitUrl(const GURL& url) {
  // Invalid URLs are not accepted by the browser process.
  if (!url.is_valid()) {
    base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
                                  RendererBlockedURLReason::kInvalidURL);
    return false;
  }

  // Do not send a URL longer than Mojo will serialize.
  if (url.possibly_invalid_spec().length() > url::kMaxURLChars) {
    base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
                                  RendererBlockedURLReason::kTooLongURL);
    return false;
  }

  // Any about: URLs must be either about:blank or about:srcdoc (optionally with
  // fragments).
  if (url.SchemeIs(url::kAboutScheme) &&
      (!url.IsAboutBlank() && !url.IsAboutSrcdoc())) {
    base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
                                  RendererBlockedURLReason::kBadAboutURL);
    return false;
  }

  return true;
}

// Gets URL that should override the default getter for this data source
// (if any), storing it in |output|. Returns true if there is an override URL.
bool MaybeGetOverriddenURL(WebDocumentLoader* document_loader, GURL* output) {
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(document_loader);
  // `document_state` may be null if it was taken from the loader, e.g. when
  // committing the result of evaluating a javascript: URL,
  // `FrameLoader::CommitNavigation()` takes the `DocumentState`. Early
  // returning here means the answer may be inaccurate, but this can only
  // happen when the replaced `Document` is being detached and about to go
  // away.
  if (!document_state) {
    return false;
  }

  // If this document is loaded by a loadDataWithBaseURL request, then the URLs
  // saved in the DocumentLoader will be the user-supplied base URL (used as the
  // "document URL") and history URL (used as the "unreachable URL"/"URL for
  // history"). However, we want to return the data: URL (the URL originally
  // sent by the browser to commit the navigation) here.
  // TODO(crbug.com/40187600): Since the DocumentState stays as long as
  // the Document stays the same, this means the data: URL will be returned even
  // after same-document navigations. Investigate whether this is intended or
  // not.
  if (document_state->was_load_data_with_base_url_request()) {
    *output = document_state->data_url();
    return true;
  }

  // The "unreachable URL" is only set in two cases:
  // - An error page, where the "unreachable URL" is set to the URL that failed
  // to load. We want the URL bar to show that URL, and the session history
  // entry should also use that URL (instead of "chrome-error://chromewebdata"
  // which is used as the "document URL" for the DocumentLoader).
  // - A loadDataWithBaseURL, where the "unreachable URL" is set to the "history
  // URL". This case should never reach this point as it's handled above, where
  // we return the original data: URL instead.
  if (document_loader->HasUnreachableURL()) {
    *output = document_loader->UnreachableWebURL();
    return true;
  }

  return false;
}

// Returns false unless this is a top-level navigation.
bool IsTopLevelNavigation(WebFrame* frame) {
  return frame->Parent() == nullptr && !frame->View()->IsFencedFrameRoot();
}

void FillNavigationParamsRequest(
    const blink::mojom::CommonNavigationParams& common_params,
    const blink::mojom::CommitNavigationParams& commit_params,
    blink::WebNavigationParams* navigation_params) {
  // Use the original navigation url to start with. We'll replay the redirects
  // afterwards and will eventually arrive to the final url.
  navigation_params->url = !commit_params.original_url.is_empty()
                               ? commit_params.original_url
                               : common_params.url;
  navigation_params->http_method = WebString::FromASCII(
      !commit_params.original_method.empty() ? commit_params.original_method
                                             : common_params.method);

  if (common_params.referrer->url.is_valid()) {
    WebString referrer = WebSecurityPolicy::GenerateReferrerHeader(
        common_params.referrer->policy, common_params.url,
        WebString::FromUTF8(common_params.referrer->url.spec()));
    navigation_params->referrer = referrer;
    navigation_params->referrer_policy = common_params.referrer->policy;
  }
  if (common_params.referrer->policy !=
      network::mojom::ReferrerPolicy::kDefault) {
    navigation_params->referrer_policy = common_params.referrer->policy;
  }

  if (common_params.post_data) {
    navigation_params->http_body =
        blink::GetWebHTTPBodyForRequestBody(*common_params.post_data);
    if (!commit_params.post_content_type.empty()) {
      navigation_params->http_content_type =
          WebString::FromASCII(commit_params.post_content_type);
    }
  }

  // Set the request initiator origin, which is supplied by the browser
  // process. It is present in cases such as navigating a frame in a different
  // process, which is routed through `blink::RemoteFrame` and the origin is
  // required to correctly compute the effective origin in which the
  // navigation will commit.
  if (common_params.initiator_origin) {
    navigation_params->requestor_origin =
        common_params.initiator_origin.value();
  }

  navigation_params->initiator_origin_trial_features = {
      common_params.initiator_origin_trial_features.begin(),
      common_params.initiator_origin_trial_features.end()};

  navigation_params->was_discarded = commit_params.was_discarded;
  navigation_params->document_ukm_source_id =
      commit_params.document_ukm_source_id;

  navigation_params->prefetched_signed_exchanges = base::ToVector(
      commit_params.prefetched_signed_exchanges, [](const auto& exchange) {
        blink::WebURLResponse web_response = blink::WebURLResponse::Create(
            exchange->inner_url, *exchange->inner_response,
            false /* report_security_info*/, -1 /* request_id */);
        return std::make_unique<
            blink::WebNavigationParams::PrefetchedSignedExchange>(
            exchange->outer_url,
            WebString::FromLatin1(
                signed_exchange_utils::CreateHeaderIntegrityHashString(
                    exchange->header_integrity)),
            exchange->inner_url, web_response,
            std::move(exchange->loader_factory_handle));
      });

  navigation_params->had_transient_user_activation =
      common_params.has_user_gesture;

  navigation_params->force_enabled_origin_trials = base::ToVector(
      commit_params.force_enabled_origin_trials, &WebString::FromASCII);

  navigation_params->early_hints_preloaded_resources = base::ToVector(
      commit_params.early_hints_preloaded_resources, blink::ToWebURL);

  // Pass on the `initiator_base_url` sent via the common_params for srcdoc and
  // about:blank documents. This will be picked up in DocumentLoader.
  // Note: It's possible for initiator_base_url to be empty if this is an
  // error srcdoc page. See test
  // NavigationRequestBrowserTest.OriginForSrcdocErrorPageInSubframe.
  if (common_params.initiator_base_url) {
    CHECK(common_params.url.IsAboutSrcdoc() ||
          common_params.url.IsAboutBlank());
    navigation_params->fallback_base_url =
        common_params.initiator_base_url.value();
  } else {
    navigation_params->fallback_base_url = WebURL();
  }
}

blink::mojom::CommonNavigationParamsPtr MakeCommonNavigationParams(
    const WebSecurityOrigin& current_origin,
    std::unique_ptr<blink::WebNavigationInfo> info,
    int load_flags,
    bool has_download_sandbox_flag,
    bool from_ad,
    bool is_history_navigation_in_new_child_frame,
    network::mojom::RequestDestination request_destination) {
  // A valid RequestorOrigin is always expected to be present.
  DCHECK(!info->url_request.RequestorOrigin().IsNull());

  blink::mojom::ReferrerPtr referrer = blink::mojom::Referrer::New(
      blink::WebStringToGURL(info->url_request.ReferrerString()),
      info->url_request.GetReferrerPolicy());

  // No history-navigation is expected to happen.
  DCHECK(info->navigation_type != blink::kWebNavigationTypeBackForward);

  // Determine the navigation type. No same-document navigation is expected
  // because it is loaded immediately by the FrameLoader.
  blink::mojom::NavigationType navigation_type =
      blink::mojom::NavigationType::DIFFERENT_DOCUMENT;
  if (info->navigation_type == blink::kWebNavigationTypeReload) {
    if (load_flags & net::LOAD_BYPASS_CACHE)
      navigation_type = blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE;
    else
      navigation_type = blink::mojom::NavigationType::RELOAD;
  }

  auto source_location = network::mojom::SourceLocation::New(
      info->source_location.url.Latin1(), info->source_location.line_number,
      info->source_location.column_number);

  const blink::WebURLRequestExtraData* url_request_extra_data =
      static_cast<blink::WebURLRequestExtraData*>(
          info->url_request.GetURLRequestExtraData().get());
  DCHECK(url_request_extra_data);

  blink::NavigationDownloadPolicy download_policy;
  download_policy.ApplyDownloadFramePolicy(
      info->is_opener_navigation, info->url_request.HasUserGesture(),
      info->url_request.RequestorOrigin().CanAccess(current_origin),
      has_download_sandbox_flag, from_ad);

  std::optional<GURL> initiator_base_url;
  GURL requestor_base_url(info->requestor_base_url);
  // Make sure the url length doesn't exceed the limit enforced by Mojo when
  // it's sent to the browser process.
  if (requestor_base_url.is_valid() &&
      requestor_base_url.possibly_invalid_spec().length() <=
          url::kMaxURLChars) {
    initiator_base_url = requestor_base_url;
  }
  return blink::mojom::CommonNavigationParams::New(
      info->url_request.Url(), info->url_request.RequestorOrigin(),
      initiator_base_url, std::move(referrer),
      url_request_extra_data->transition_type(), navigation_type,
      download_policy,
      info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(),
      base::TimeTicks::Now(), info->url_request.HttpMethod().Latin1(),
      blink::GetRequestBodyForWebURLRequest(info->url_request),
      std::move(source_location), false /* started_from_context_menu */,
      info->url_request.HasUserGesture(),
      info->url_request.HasTextFragmentToken(),
      info->should_check_main_world_content_security_policy,
      info->initiator_origin_trial_features, info->href_translate.Latin1(),
      is_history_navigation_in_new_child_frame, info->input_start,
      request_destination);
}

WebFrameLoadType NavigationTypeToLoadType(
    blink::mojom::NavigationType navigation_type,
    bool should_replace_current_entry) {
  switch (navigation_type) {
    case blink::mojom::NavigationType::RELOAD:
      return WebFrameLoadType::kReload;

    case blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE:
      return WebFrameLoadType::kReloadBypassingCache;

    case blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT:
    case blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT:
      return WebFrameLoadType::kBackForward;

    case blink::mojom::NavigationType::RESTORE:
    case blink::mojom::NavigationType::RESTORE_WITH_POST:
      return WebFrameLoadType::kRestore;

    case blink::mojom::NavigationType::SAME_DOCUMENT:
    case blink::mojom::NavigationType::DIFFERENT_DOCUMENT:
      return should_replace_current_entry
                 ? WebFrameLoadType::kReplaceCurrentItem
                 : WebFrameLoadType::kStandard;

    default:
      NOTREACHED();
  }
}

RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl =
    nullptr;

WebString ConvertRelativePathToHtmlAttribute(const base::FilePath& path) {
  DCHECK(!path.IsAbsolute());
  return WebString::FromUTF8(
      std::string("./") +
      path.NormalizePathSeparatorsTo(FILE_PATH_LITERAL('/')).AsUTF8Unsafe());
}

class RenderFrameWebFrameSerializerClient
    : public blink::WebFrameSerializerClient {
 public:
  explicit RenderFrameWebFrameSerializerClient(
      mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> handler_remote)
      : handler_remote_(std::move(handler_remote)) {}

  // WebFrameSerializerClient implementation:
  void DidSerializeDataForFrame(
      const std::vector<char>& data,
      WebFrameSerializerClient::FrameSerializationStatus status) override {
    DCHECK(handler_remote_.is_bound());
    handler_remote_->DidReceiveData(std::string(data.data(), data.size()));

    // Make sure to report Done() to the browser process when receiving the last
    // chunk of data, and reset the mojo remote so that the DCHECK above ensures
    // this method won't be called anymore after this point.
    if (status == WebFrameSerializerClient::kCurrentFrameIsFinished) {
      handler_remote_->Done();
      handler_remote_.reset();
    }
  }

 private:
  mojo::Remote<mojom::FrameHTMLSerializerHandler> handler_remote_;
};

// Implementation of WebFrameSerializer::LinkRewritingDelegate that responds
// based on the payload of mojom::Frame::GetSerializedHtmlWithLocalLinks().
class LinkRewritingDelegate : public WebFrameSerializer::LinkRewritingDelegate {
 public:
  LinkRewritingDelegate(
      const base::flat_map<GURL, base::FilePath>& url_to_local_path,
      const base::flat_map<blink::FrameToken, base::FilePath>&
          frame_token_to_local_path)
      : url_to_local_path_(url_to_local_path),
        frame_token_to_local_path_(frame_token_to_local_path) {}

  bool RewriteFrameSource(WebFrame* frame, WebString* rewritten_link) override {
    const blink::FrameToken frame_token = frame->GetFrameToken();
    auto it = frame_token_to_local_path_->find(frame_token);
    if (it == frame_token_to_local_path_->end()) {
      return false;  // This can happen because of https://crbug.com/541354.
    }

    const base::FilePath& local_path = it->second;
    *rewritten_link = ConvertRelativePathToHtmlAttribute(local_path);
    return true;
  }

  bool RewriteLink(const WebURL& url, WebString* rewritten_link) override {
    auto it = url_to_local_path_->find(GURL(url));
    if (it == url_to_local_path_->end()) {
      return false;
    }

    const base::FilePath& local_path = it->second;
    *rewritten_link = ConvertRelativePathToHtmlAttribute(local_path);
    return true;
  }

 private:
  const raw_ref<const base::flat_map<GURL, base::FilePath>> url_to_local_path_;
  const raw_ref<const base::flat_map<blink::FrameToken, base::FilePath>>
      frame_token_to_local_path_;
};

bool IsHttpPost(const blink::WebURLRequest& request) {
  return request.HttpMethod().Utf8() == "POST";
}

// Delegate responsible for determining the handle writing implementation by
// instantiating an MHTMLHandleWriter on the heap respective to the passed in
// MHTMLSerializationParams. This transfers ownership of the handle to the
// new MHTMLHandleWriter.
class MHTMLHandleWriterDelegate {
 public:
  MHTMLHandleWriterDelegate(
      const mojom::SerializeAsMHTMLParams& params,
      MHTMLHandleWriter::MHTMLWriteCompleteCallback callback,
      scoped_refptr<base::TaskRunner> main_thread_task_runner) {
    // Handle must be instantiated.
    DCHECK(params.output_handle);

    if (params.output_handle->is_file_handle()) {
      handle_ = std::make_unique<MHTMLFileHandleWriter>(
          std::move(main_thread_task_runner), std::move(callback),
          std::move(params.output_handle->get_file_handle()));
    } else {
      handle_ = std::make_unique<MHTMLProducerHandleWriter>(
          std::move(main_thread_task_runner), std::move(callback),
          std::move(params.output_handle->get_producer_handle()));
    }
  }

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

  void WriteContents(std::vector<WebThreadSafeData> mhtml_contents) {
    // MHTMLHandleWriter::WriteContents calls MHTMLHandleWriter::Finish
    // eventually.
    base::ThreadPool::PostTask(
        FROM_HERE, {base::MayBlock()},
        base::BindOnce(&MHTMLHandleWriter::WriteContents, std::move(handle_),
                       std::move(mhtml_contents)));
  }

  // Within the context of the delegate, only for premature write finish.
  void Finish(mojom::MhtmlSaveStatus save_status) {
    base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()},
                               base::BindOnce(&MHTMLHandleWriter::Finish,
                                              std::move(handle_), save_status));
  }

 private:
  std::unique_ptr<MHTMLHandleWriter> handle_;
};

mojo::PendingRemote<blink::mojom::BlobURLToken> CloneBlobURLToken(
    blink::CrossVariantMojoRemote<blink::mojom::BlobURLTokenInterfaceBase>&
        blob_url_token) {
  if (!blob_url_token)
    return mojo::NullRemote();
  mojo::PendingRemote<blink::mojom::BlobURLToken> cloned_token;
  mojo::Remote<blink::mojom::BlobURLToken> token(std::move(blob_url_token));
  token->Clone(cloned_token.InitWithNewPipeAndPassReceiver());
  blob_url_token = token.Unbind();
  return cloned_token;
}

// Creates a fully functional DocumentState in the case where we do not have
// navigation parameters available.
std::unique_ptr<DocumentState> BuildDocumentState() {
  std::unique_ptr<DocumentState> document_state =
      std::make_unique<DocumentState>();
  document_state->set_navigation_state(
      NavigationState::CreateForSynchronousCommit());
  return document_state;
}

// Creates a fully functional DocumentState in the case where we have
// navigation parameters available in the RenderFrameImpl.
std::unique_ptr<DocumentState> BuildDocumentStateFromParams(
    const blink::mojom::CommonNavigationParams& common_params,
    const blink::mojom::CommitNavigationParams& commit_params,
    mojom::NavigationClient::CommitNavigationCallback commit_callback,
    std::unique_ptr<NavigationClient> navigation_client,
    int request_id,
    bool was_initiated_in_this_frame) {
  std::unique_ptr<DocumentState> document_state(new DocumentState());

  DCHECK(!common_params.navigation_start.is_null());
  DCHECK(!common_params.url.SchemeIs(url::kJavaScriptScheme));

  document_state->set_is_overriding_user_agent(
      commit_params.is_overriding_user_agent);
  document_state->set_request_id(request_id);

  // If this is a loadDataWithBaseURL request, save the commit URL so that we
  // can send a DidCommit message with the URL that was originally sent by the
  // browser in CommonNavigationParams (See MaybeGetOverriddenURL()).
  document_state->set_was_load_data_with_base_url_request(
      commit_params.is_load_data_with_base_url);
  if (commit_params.is_load_data_with_base_url)
    document_state->set_data_url(common_params.url);

  document_state->set_navigation_state(
      NavigationState::CreateForCrossDocumentCommit(
          common_params.Clone(), commit_params.Clone(),
          std::move(commit_callback), std::move(navigation_client),
          was_initiated_in_this_frame));
  return document_state;
}

std::optional<WebURL> ApplyFilePathAlias(const WebURL& target) {
  const base::CommandLine::StringType file_url_path_alias =
      base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
          switches::kFileUrlPathAlias);
  if (file_url_path_alias.empty()) {
    return std::nullopt;
  }

  const auto alias_mapping =
      base::SplitString(file_url_path_alias, FILE_PATH_LITERAL("="),
                        base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  if (alias_mapping.size() != 2) {
    LOG(ERROR) << "Invalid file path alias format.";
    return std::nullopt;
  }

#if BUILDFLAG(IS_WIN)
  std::wstring path = base::UTF16ToWide(target.GetString().Utf16());
  const std::wstring file_prefix =
      base::ASCIIToWide(url::kFileScheme) +
      base::ASCIIToWide(url::kStandardSchemeSeparator);
#else
  std::string path = target.GetString().Utf8();
  const std::string file_prefix =
      std::string(url::kFileScheme) + url::kStandardSchemeSeparator;
#endif
  if (!base::StartsWith(path, file_prefix + alias_mapping[0],
                        base::CompareCase::SENSITIVE)) {
    return std::nullopt;
  }

  base::ReplaceFirstSubstringAfterOffset(&path, 0, alias_mapping[0],
                                         alias_mapping[1]);
#if BUILDFLAG(IS_WIN)
  return blink::WebURL(GURL(base::WideToUTF8(path)));
#else
  return blink::WebURL(GURL(path));
#endif
}

// Packs all navigation timings sent by the browser to a blink understandable
// format, blink::WebNavigationTimings.
blink::WebNavigationTimings BuildNavigationTimings(
    base::TimeTicks navigation_start,
    const blink::mojom::NavigationTiming& browser_navigation_timings,
    base::TimeTicks input_start) {
  blink::WebNavigationTimings renderer_navigation_timings;

  // Sanitizes the navigation_start timestamp for browser-initiated navigations,
  // where the browser possibly has a better notion of start time than the
  // renderer. In the case of cross-process navigations, this carries over the
  // time of finishing the onbeforeunload handler of the previous page.
  // TimeTicks is sometimes not monotonic across processes, and because
  // |browser_navigation_start| is likely before this process existed,
  // InterProcessTimeTicksConverter won't help. The timestamp is sanitized by
  // clamping it to now.
  DCHECK(!navigation_start.is_null());
  renderer_navigation_timings.navigation_start =
      std::min(navigation_start, base::TimeTicks::Now());

  renderer_navigation_timings.redirect_start =
      browser_navigation_timings.redirect_start;
  renderer_navigation_timings.redirect_end =
      browser_navigation_timings.redirect_end;
  renderer_navigation_timings.fetch_start =
      browser_navigation_timings.fetch_start;

  renderer_navigation_timings.input_start = input_start;
  renderer_navigation_timings.parent_resource_timing_access =
      browser_navigation_timings.parent_resource_timing_access;

  renderer_navigation_timings.system_entropy_at_navigation_start =
      browser_navigation_timings.system_entropy_at_navigation_start;

  return renderer_navigation_timings;
}

WebHistoryItem NavigationApiHistoryEntryPtrToWebHistoryItem(
    const blink::mojom::NavigationApiHistoryEntry& entry) {
  return WebHistoryItem(
      WebString::FromUTF16(entry.url), WebString::FromUTF16(entry.key),
      WebString::FromUTF16(entry.id), entry.item_sequence_number,
      entry.document_sequence_number, WebString::FromUTF16(entry.state));
}

// Fills navigation data sent by the browser to a blink understandable
// format, blink::WebNavigationParams.
void FillMiscNavigationParams(
    const blink::mojom::CommonNavigationParams& common_params,
    blink::mojom::CommitNavigationParams& commit_params,
    blink::WebNavigationParams* navigation_params) {
  navigation_params->navigation_timings = BuildNavigationTimings(
      common_params.navigation_start, *commit_params.navigation_timing,
      common_params.input_start);
  if (!commit_params.redirect_infos.empty()) {
    navigation_params->navigation_timings.critical_ch_restart =
        commit_params.redirect_infos.back().critical_ch_restart_time;
  }

  navigation_params->is_user_activated =
      commit_params.was_activated == blink::mojom::WasActivatedOption::kYes;

  navigation_params->has_text_fragment_token =
      common_params.text_fragment_token;

  navigation_params->is_browser_initiated = commit_params.is_browser_initiated;

  navigation_params->is_cross_site_cross_browsing_context_group =
      commit_params.is_cross_site_cross_browsing_context_group;

  navigation_params->should_have_sticky_user_activation =
      commit_params.should_have_sticky_user_activation;

#if BUILDFLAG(IS_ANDROID)
  // Only android webview uses this.
  navigation_params->grant_load_local_resources =
      commit_params.can_load_local_resources;
#else
  DCHECK(!commit_params.can_load_local_resources);
#endif

  if (commit_params.origin_to_commit) {
    navigation_params->origin_to_commit =
        commit_params.origin_to_commit.value();
  }
  navigation_params->storage_key = std::move(commit_params.storage_key);

  navigation_params->frame_policy = commit_params.frame_policy;

  if (common_params.navigation_type == blink::mojom::NavigationType::RESTORE) {
    // We're doing a load of a page that was restored from the last session.
    // By default this prefers the cache over loading
    // (LOAD_SKIP_CACHE_VALIDATION) which can result in stale data for pages
    // that are set to expire. We explicitly override that by setting the
    // policy here so that as necessary we load from the network.
    //
    // TODO(davidben): Remove this in favor of passing a cache policy to the
    // loadHistoryItem call in OnNavigate. That requires not overloading
    // UseProtocolCachePolicy to mean both "normal load" and "determine cache
    // policy based on load type, etc".
    navigation_params->force_fetch_cache_mode =
        blink::mojom::FetchCacheMode::kDefault;
  }

  navigation_params->origin_agent_cluster = commit_params.origin_agent_cluster;
  navigation_params->origin_agent_cluster_left_as_default =
      commit_params.origin_agent_cluster_left_as_default;

  navigation_params->reduced_accept_language =
      WebString::FromASCII(commit_params.reduced_accept_language);
  navigation_params->enabled_client_hints.reserve(
      commit_params.enabled_client_hints.size());
  for (auto enabled_hint : commit_params.enabled_client_hints)
    navigation_params->enabled_client_hints.emplace_back(enabled_hint);

  if (commit_params.http_response_code != -1)
    navigation_params->http_status_code = commit_params.http_response_code;

  // Copy the modified runtime features from `commit_params` to send to the
  // Blink renderer class WebLocalFrameImpl.
  navigation_params->modified_runtime_features =
      commit_params.modified_runtime_features;

  // Populate the arrays of non-current entries for the window.navigation API.
  auto& entry_arrays = commit_params.navigation_api_history_entry_arrays;
  navigation_params->navigation_api_back_entries.reserve(
      entry_arrays->back_entries.size());
  for (const auto& entry : entry_arrays->back_entries) {
    navigation_params->navigation_api_back_entries.emplace_back(
        NavigationApiHistoryEntryPtrToWebHistoryItem(*entry));
  }
  navigation_params->navigation_api_forward_entries.reserve(
      entry_arrays->forward_entries.size());
  for (const auto& entry : entry_arrays->forward_entries) {
    navigation_params->navigation_api_forward_entries.emplace_back(
        NavigationApiHistoryEntryPtrToWebHistoryItem(*entry));
  }
  if (entry_arrays->previous_entry) {
    navigation_params->navigation_api_previous_entry =
        NavigationApiHistoryEntryPtrToWebHistoryItem(
            *entry_arrays->previous_entry);
  }

  if (commit_params.fenced_frame_properties) {
    navigation_params->fenced_frame_properties =
        commit_params.fenced_frame_properties;

    if (commit_params.fenced_frame_properties->nested_urn_config_pairs() &&
        commit_params.fenced_frame_properties->nested_urn_config_pairs()
            ->potentially_opaque_value.has_value()) {
      const auto& nested_urn_config_pairs_value =
          commit_params.fenced_frame_properties->nested_urn_config_pairs()
              ->potentially_opaque_value.value();
      DCHECK_EQ(blink::MaxAdAuctionAdComponents(),
                nested_urn_config_pairs_value.size());
      navigation_params->ad_auction_components.emplace();
      for (const auto& nested_urn_config_pair : nested_urn_config_pairs_value) {
        const GURL& urn = nested_urn_config_pair.first;
        DCHECK(urn.SchemeIs(url::kUrnScheme));
        navigation_params->ad_auction_components->push_back(blink::WebURL(urn));
      }
    }
  }

  navigation_params->ancestor_or_self_has_cspee =
      commit_params.ancestor_or_self_has_cspee;

  navigation_params->browsing_context_group_info =
      commit_params.browsing_context_group_info;

  navigation_params->content_settings =
      std::move(commit_params.content_settings);

  if (commit_params.cookie_deprecation_label.has_value()) {
    navigation_params->cookie_deprecation_label =
        WebString::FromASCII(*commit_params.cookie_deprecation_label);
  }
  navigation_params->initial_permission_statuses =
      std::move(commit_params.initial_permission_statuses);

  navigation_params->force_new_document_sequence_number =
      commit_params.force_new_document_sequence_number;
}

std::string GetUniqueNameOfWebFrame(WebFrame* web_frame) {
  if (web_frame->IsWebLocalFrame())
    return RenderFrameImpl::FromWebFrame(web_frame)->unique_name();
  return web_frame->ToWebRemoteFrame()->UniqueName().Utf8();
}

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

void CallClientDeferMediaLoad(base::WeakPtr<RenderFrameImpl> frame,
                              bool has_played_media_before,
                              base::OnceClosure closure) {
  if (!frame)
    return;
  GetContentClient()->renderer()->DeferMediaLoad(
      frame.get(), has_played_media_before, std::move(closure));
}

void LogCommitHistograms(base::TimeTicks commit_sent,
                         bool is_main_frame,
                         const GURL& new_page_url) {
  if (!base::TimeTicks::IsConsistentAcrossProcesses())
    return;

  const char* frame_type = is_main_frame ? "MainFrame" : "Subframe";
  auto now = base::TimeTicks::Now();
  base::UmaHistogramTimes(
      base::StrCat({"Navigation.RendererCommitDelay.", frame_type}),
      now - commit_sent);
  if (auto* task = base::TaskAnnotator::CurrentTaskForThread()) {
    base::UmaHistogramTimes(
        base::StrCat({"Navigation.RendererCommitQueueTime.", frame_type}),
        now - task->queue_time);
  }

  // Some tests don't set the render thread.
  if (!RenderThreadImpl::current())
    return;

  base::TimeTicks run_loop_start_time =
      RenderThreadImpl::current()->run_loop_start_time();
  // If the commit was sent before the run loop was started for this process,
  // the navigation was likely delayed while waiting for the process to start.
  if (commit_sent < run_loop_start_time) {
    base::UmaHistogramTimes(
        base::StrCat({"Navigation.RendererCommitProcessWaitTime.", frame_type}),
        run_loop_start_time - commit_sent);
  }

  // We want to record the following metric just one time per process.
  static bool is_first_commit = true;
  if (is_first_commit) {
    is_first_commit = false;
    if (run_loop_start_time <= now && new_page_url.is_valid() &&
        new_page_url.SchemeIsHTTPOrHTTPS()) {
      const char* const name =
          is_main_frame
              ? "Navigation.RendererRunLoopStartToFirstCommitNavigation2."
                "MainFrame"
              : "Navigation.RendererRunLoopStartToFirstCommitNavigation2."
                "Subframe";
      const auto trace_id = TRACE_ID_WITH_SCOPE(
          name, TRACE_ID_LOCAL(RenderThreadImpl::current()));
      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
          "navigation", name, trace_id, run_loop_start_time, "url",
          new_page_url);
      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("navigation", name,
                                                     trace_id, now);
      base::UmaHistogramLongTimes(name, now - run_loop_start_time);
    }
  }
}

content::mojom::WindowContainerType WindowFeaturesToContainerType(
    const blink::WebWindowFeatures& window_features) {
  if (window_features.background) {
    if (window_features.persistent)
      return content::mojom::WindowContainerType::PERSISTENT;
    else
      return content::mojom::WindowContainerType::BACKGROUND;
  } else {
    return content::mojom::WindowContainerType::NORMAL;
  }
}

WindowOpenDisposition NavigationPolicyToDisposition(
    WebNavigationPolicy policy) {
  switch (policy) {
    case blink::kWebNavigationPolicyDownload:
      return WindowOpenDisposition::SAVE_TO_DISK;
    case blink::kWebNavigationPolicyCurrentTab:
      return WindowOpenDisposition::CURRENT_TAB;
    case blink::kWebNavigationPolicyNewBackgroundTab:
      return WindowOpenDisposition::NEW_BACKGROUND_TAB;
    case blink::kWebNavigationPolicyNewForegroundTab:
      return WindowOpenDisposition::NEW_FOREGROUND_TAB;
    case blink::kWebNavigationPolicyNewWindow:
      return WindowOpenDisposition::NEW_WINDOW;
    case blink::kWebNavigationPolicyNewPopup:
      return WindowOpenDisposition::NEW_POPUP;
    case blink::kWebNavigationPolicyPictureInPicture:
      return WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
  }
  NOTREACHED() << "Unexpected WebNavigationPolicy";
}

bool ShouldNotifySubresourceResponseStarted(
    const blink::RendererPreferences& pref) {
  if (!base::FeatureList::IsEnabled(
          features::kReduceSubresourceResponseStartedIPC)) {
    return true;
  }
  return pref.send_subresource_notification;
}

// Initialize the WebFrameWidget with compositing. Only local root frames
// create a widget.
// `previous_widget` indicates whether the compositor for the frame which
// is being replaced by this frame should be used instead of creating a new
// compositor instance.
void InitializeFrameWidgetForFrame(
    WebLocalFrame& frame,
    blink::WebFrameWidget* previous_widget,
    mojom::CreateFrameWidgetParamsPtr widget_params,
    const bool is_for_nested_main_frame) {
  CHECK(widget_params);

  const bool is_main_frame = !frame.Parent();
  CHECK(is_main_frame || !is_for_nested_main_frame);

  const bool is_for_scalable_page =
      is_main_frame && !frame.View()->IsFencedFrameRoot();

  const auto frame_sink_id =
      previous_widget ? previous_widget->GetFrameSinkId()
                      : viz::FrameSinkId(RenderThread::Get()->GetClientId(),
                                         widget_params->routing_id);
  auto* web_frame_widget = frame.InitializeFrameWidget(
      std::move(widget_params->frame_widget_host),
      std::move(widget_params->frame_widget),
      std::move(widget_params->widget_host), std::move(widget_params->widget),
      frame_sink_id, is_for_nested_main_frame, is_for_scalable_page,
      /*hidden=*/true);

  if (previous_widget) {
    web_frame_widget->InitializeCompositingFromPreviousWidget(
        widget_params->visual_properties.screen_infos,
        /*settings=*/nullptr, *previous_widget);
  } else {
    web_frame_widget->InitializeCompositing(
        widget_params->visual_properties.screen_infos,
        /*settings=*/nullptr);
  }

  // The WebFrameWidget should start with valid VisualProperties, including a
  // non-zero size. While WebFrameWidget would not normally receive IPCs and
  // thus would not get VisualProperty updates while the frame is provisional,
  // we need at least one update to them in order to meet expectations in the
  // renderer, and that update comes as part of the CreateFrame message.
  // TODO(crbug.com/40387047): This could become part of WebFrameWidget Init.
  web_frame_widget->ApplyVisualProperties(widget_params->visual_properties);
}

}  // namespace

// Implementation of WebFrameSerializer::MHTMLPartsGenerationDelegate that
// 1. Bases shouldSkipResource and getContentID responses on contents of
//    SerializeAsMHTMLParams.
// 2. Stores digests of urls of serialized resources (i.e. urls reported via
//    shouldSkipResource) into |serialized_resources_uri_digests| passed
//    to the constructor.
class MHTMLPartsGenerationDelegateImpl final
    : public WebFrameSerializer::MHTMLPartsGenerationDelegate {
 public:
  explicit MHTMLPartsGenerationDelegateImpl(
      mojom::SerializeAsMHTMLParamsPtr params)
      : params_(std::move(params)) {
    // Digests must be sorted for binary search.
    DCHECK(std::is_sorted(params_->digests_of_uris_to_skip.begin(),
                          params_->digests_of_uris_to_skip.end()));
    // URLs are not duplicated.
    DCHECK(std::ranges::adjacent_find(params_->digests_of_uris_to_skip) ==
           params_->digests_of_uris_to_skip.end());
  }

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

  bool ShouldSkipResource(const WebURL& url) override {
    std::string digest =
        crypto::SHA256HashString(params_->salt + GURL(url).spec());

    // Skip if the |url| already covered by serialization of an *earlier* frame.
    if (std::binary_search(params_->digests_of_uris_to_skip.begin(),
                           params_->digests_of_uris_to_skip.end(), digest)) {
      return true;
    }

    // Let's record |url| as being serialized for the *current* frame.
    auto pair = serialized_resources_uri_digests_.insert(digest);
    bool insertion_took_place = pair.second;
    DCHECK(insertion_took_place);  // Blink should dedupe within a frame.

    return false;
  }

  bool UseBinaryEncoding() override { return params_->mhtml_binary_encoding; }

  bool RemovePopupOverlay() override {
    return params_->mhtml_popup_overlay_removal;
  }

  std::unordered_set<std::string> TakeSerializedResourcesUriDigests() {
    return std::move(serialized_resources_uri_digests_);
  }
  mojom::SerializeAsMHTMLParamsPtr TakeParams() { return std::move(params_); }

 private:
  std::vector<std::string> digests_of_uris_to_skip_;
  mojom::SerializeAsMHTMLParamsPtr params_;
  std::unordered_set<std::string> serialized_resources_uri_digests_;
};

RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
    RenderFrameImpl* frame)
    : AssertNavigationCommits(frame, false) {}

RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
    RenderFrameImpl* frame,
    MayReplaceInitialEmptyDocumentTag)
    : AssertNavigationCommits(frame, true) {}

RenderFrameImpl::AssertNavigationCommits::~AssertNavigationCommits() {
  // Frame might have been synchronously detached when dispatching JS events.
  if (frame_) {
    CHECK_EQ(NavigationCommitState::kDidCommit,
             frame_->navigation_commit_state_);
    frame_->navigation_commit_state_ = NavigationCommitState::kNone;
  }
}

RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
    RenderFrameImpl* frame,
    bool allow_transition_from_initial_empty_document)
    : frame_(frame->weak_factory_.GetWeakPtr()) {
  if (NavigationCommitState::kNone != frame->navigation_commit_state_) {
    CHECK(allow_transition_from_initial_empty_document);
    CHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
             frame->navigation_commit_state_);
  }
  frame->navigation_commit_state_ = NavigationCommitState::kWillCommit;
}

// This class uses existing WebNavigationBodyLoader to read the whole response
// body into in-memory buffer, and then creates another body loader with static
// data so that we can parse mhtml archive synchronously. This is a workaround
// for the fact that we need the whole archive to determine the document's mime
// type and construct a right document instance.
class RenderFrameImpl::MHTMLBodyLoaderClient
    : public blink::WebNavigationBodyLoader::Client {
 public:
  // Once the body is read, fills |navigation_params| with the new body loader
  // and calls |done_callbcak|.
  MHTMLBodyLoaderClient(
      RenderFrameImpl* frame,
      std::unique_ptr<blink::WebNavigationParams> navigation_params,
      base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)>
          done_callback)
      : frame_(frame),
        navigation_params_(std::move(navigation_params)),
        body_loader_(std::move(navigation_params_->body_loader)),
        done_callback_(std::move(done_callback)) {
    body_loader_->StartLoadingBody(this);
  }

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

  ~MHTMLBodyLoaderClient() override {
    // MHTMLBodyLoaderClient is reset in several different places. Either:
    CHECK(
        // - the body load finished and the result is being committed, so
        //   |BodyLoadingFinished| (see below) should still be on the stack, or
        committing_ ||
        // - MHTMLBodyLoaderClient is abandoned, either because:
        //   - a new renderer-initiated navigation began, which explicitly
        //     detaches any existing MHTMLBodyLoaderClient
        //   - this renderer began the navigation and cancelled it with
        //     |AbortClientNavigation|, e.g. JS called window.stop(), which
        //     explicitly detaches any existing MHTMLBodyLoaderClient
        //   - the frame is detached and self-deleted, which also explicitly
        //     detaches any existing MHTMLBodyLoaderClient or,
        !frame_ ||
        //   - the browser requested a different navigation be committed in this
        //     frame, i.e. the navigation commit state should be |kWillCommit|
        NavigationCommitState::kWillCommit == frame_->navigation_commit_state_);
  }

  // Marks |this|'s pending load as abandoned. There are a number of reasons
  // this can happen; see the destructor for more information.
  void Detach() {
    // Note that the MHTMLBodyLoaderClient might be associated with a
    // provisional frame, so this does not assert that `frame_->in_frame_tree_`
    // is true.
    frame_ = nullptr;
  }

  // blink::WebNavigationBodyLoader::Client overrides:
  void BodyDataReceived(base::span<const char> data) override {
    data_.Append(base::as_bytes(data));
  }

  void BodyLoadingFinished(base::TimeTicks completion_time,
                           int64_t total_encoded_data_length,
                           int64_t total_encoded_body_length,
                           int64_t total_decoded_body_length,
                           const std::optional<WebURLError>& error) override {
    committing_ = true;
    AssertNavigationCommits assert_navigation_commits(frame_);
    if (!error.has_value()) {
      WebNavigationParams::FillBodyLoader(navigation_params_.get(), data_);
      // Clear |is_static_data| flag to avoid the special behavior it triggers,
      // e.g. skipping content disposition check. We want this load to be
      // regular, just like with an original body loader.
      navigation_params_->is_static_data = false;
    }
    std::move(done_callback_).Run(std::move(navigation_params_));
  }

 private:
  // |RenderFrameImpl| owns |this|, so |frame_| is guaranteed to outlive |this|.
  // Will be nulled if |Detach()| has been called.
  raw_ptr<RenderFrameImpl> frame_;
  bool committing_ = false;
  WebData data_;
  std::unique_ptr<blink::WebNavigationParams> navigation_params_;
  std::unique_ptr<blink::WebNavigationBodyLoader> body_loader_;
  base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)>
      done_callback_;
};

RenderFrameImpl::UniqueNameFrameAdapter::UniqueNameFrameAdapter(
    RenderFrameImpl* render_frame)
    : render_frame_(render_frame) {}

RenderFrameImpl::UniqueNameFrameAdapter::~UniqueNameFrameAdapter() {}

bool RenderFrameImpl::UniqueNameFrameAdapter::IsMainFrame() const {
  return render_frame_->IsMainFrame();
}

bool RenderFrameImpl::UniqueNameFrameAdapter::IsCandidateUnique(
    std::string_view name) const {
  // This method is currently O(N), where N = number of frames in the tree.
  DCHECK(!name.empty());

  for (blink::WebFrame* frame = GetWebFrame()->Top(); frame;
       frame = frame->TraverseNext()) {
    if (GetUniqueNameOfWebFrame(frame) == name)
      return false;
  }

  return true;
}

int RenderFrameImpl::UniqueNameFrameAdapter::GetSiblingCount() const {
  int sibling_count = 0;
  for (blink::WebFrame* frame = GetWebFrame()->Parent()->FirstChild(); frame;
       frame = frame->NextSibling()) {
    if (frame == GetWebFrame())
      continue;
    ++sibling_count;
  }
  return sibling_count;
}

int RenderFrameImpl::UniqueNameFrameAdapter::GetChildCount() const {
  int child_count = 0;
  for (blink::WebFrame* frame = GetWebFrame()->FirstChild(); frame;
       frame = frame->NextSibling()) {
    ++child_count;
  }
  return child_count;
}

std::vector<std::string>
RenderFrameImpl::UniqueNameFrameAdapter::CollectAncestorNames(
    BeginPoint begin_point,
    bool (*should_stop)(std::string_view)) const {
  std::vector<std::string> result;
  for (blink::WebFrame* frame = begin_point == BeginPoint::kParentFrame
                                    ? GetWebFrame()->Parent()
                                    : GetWebFrame();
       frame; frame = frame->Parent()) {
    result.push_back(GetUniqueNameOfWebFrame(frame));
    if (should_stop(result.back()))
      break;
  }
  return result;
}

std::vector<int> RenderFrameImpl::UniqueNameFrameAdapter::GetFramePosition(
    BeginPoint begin_point) const {
  std::vector<int> result;
  blink::WebFrame* parent = begin_point == BeginPoint::kParentFrame
                                ? GetWebFrame()->Parent()
                                : GetWebFrame();
  blink::WebFrame* child =
      begin_point == BeginPoint::kParentFrame ? GetWebFrame() : nullptr;
  while (parent) {
    int position_in_parent = 0;
    blink::WebFrame* sibling = parent->FirstChild();
    while (sibling != child) {
      sibling = sibling->NextSibling();
      ++position_in_parent;
    }
    result.push_back(position_in_parent);

    child = parent;
    parent = parent->Parent();
  }
  return result;
}

blink::WebLocalFrame* RenderFrameImpl::UniqueNameFrameAdapter::GetWebFrame()
    const {
  return render_frame_->frame_;
}

// static
RenderFrameImpl* RenderFrameImpl::Create(
    AgentSchedulingGroup& agent_scheduling_group,
    const blink::LocalFrameToken& frame_token,
    int32_t routing_id,
    mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
    mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
        associated_interface_provider,
    const base::UnguessableToken& devtools_frame_token,
    bool is_for_nested_main_frame) {
  DCHECK(routing_id != MSG_ROUTING_NONE);
  CreateParams params(agent_scheduling_group, frame_token, routing_id,
                      std::move(frame_receiver),
                      std::move(associated_interface_provider),
                      devtools_frame_token, is_for_nested_main_frame);

  if (g_create_render_frame_impl)
    return g_create_render_frame_impl(std::move(params));
  else
    return new RenderFrameImpl(std::move(params));
}

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
// static
RenderFrame* RenderFrame::FromRoutingID(int routing_id) {
  return RenderFrameImpl::FromRoutingID(routing_id);
}

// static
RenderFrameImpl* RenderFrameImpl::FromRoutingID(int routing_id) {
  DCHECK(RenderThread::IsMainThread());
  auto iter = g_routing_id_frame_map.Get().find(routing_id);
  if (iter != g_routing_id_frame_map.Get().end())
    return iter->second;
  return nullptr;
}
#endif

// static
RenderFrameImpl* RenderFrameImpl::CreateMainFrame(
    AgentSchedulingGroup& agent_scheduling_group,
    blink::WebView* web_view,
    blink::WebFrame* opener,
    bool is_for_nested_main_frame,
    bool is_for_scalable_page,
    blink::mojom::FrameReplicationStatePtr replication_state,
    const base::UnguessableToken& devtools_frame_token,
    mojom::CreateLocalMainFrameParamsPtr params,
    const blink::WebURL& base_url) {
  // A main frame RenderFrame must have a RenderWidget.
  DCHECK_NE(MSG_ROUTING_NONE, params->widget_params->routing_id);

  RenderFrameImpl* render_frame = RenderFrameImpl::Create(
      agent_scheduling_group, params->frame_token, params->routing_id,
      std::move(params->frame),
      std::move(params->associated_interface_provider_remote),
      devtools_frame_token, is_for_nested_main_frame);

  WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
      web_view, render_frame, render_frame->blink_interface_registry_.get(),
      std::move(params->interface_broker), params->frame_token,
      params->document_token,
      ToWebPolicyContainer(std::move(params->policy_container)), opener,
      // This conversion is a little sad, as this often comes from a
      // WebString...
      WebString::FromUTF8(replication_state->name),
      replication_state->frame_policy.sandbox_flags, base_url);
  if (!params->is_on_initial_empty_document)
    render_frame->frame_->SetIsNotOnInitialEmptyDocument();

  CHECK(!params->widget_params->reuse_compositor);

  // Non-owning pointer that is self-referencing and destroyed by calling
  // Close(). The RenderViewImpl has a RenderWidget already, but not a
  // WebFrameWidget, which is now attached here.
  blink::WebFrameWidget* web_frame_widget = web_frame->InitializeFrameWidget(
      std::move(params->widget_params->frame_widget_host),
      std::move(params->widget_params->frame_widget),
      std::move(params->widget_params->widget_host),
      std::move(params->widget_params->widget),
      viz::FrameSinkId(RenderThread::Get()->GetClientId(),
                       params->widget_params->routing_id),
      is_for_nested_main_frame, is_for_scalable_page,
      /*hidden=*/true);
  web_frame_widget->InitializeCompositing(
      params->widget_params->visual_properties.screen_infos,
      /*settings=*/nullptr);

  // The WebFrame created here was already attached to the Page as its main
  // frame, and the WebFrameWidget has been initialized, so we can call
  // WebView's DidAttachLocalMainFrame().
  render_frame->GetWebView()->DidAttachLocalMainFrame();

  // The WebFrameWidget should start with valid VisualProperties, including a
  // non-zero size. While WebFrameWidget would not normally receive IPCs and
  // thus would not get VisualProperty updates while the frame is provisional,
  // we need at least one update to them in order to meet expectations in the
  // renderer, and that update comes as part of the CreateFrame message.
  // TODO(crbug.com/40387047): This could become part of WebFrameWidget Init.
  web_frame_widget->ApplyVisualProperties(
      params->widget_params->visual_properties);

  render_frame->in_frame_tree_ = true;
  render_frame->Initialize(nullptr);

  if (params->subresource_loader_factories
          ->IsTrackedChildPendingURLLoaderFactoryBundle()) {
    // In renderer-initiated creation of a new main frame (e.g. popup without
    // rel=noopener), `params->subresource_loader_factories` are inherited from
    // the creator frame (e.g. TrackedChildURLLoaderFactoryBundle will be
    // updated when the creator's bundle recovers from a NetworkService crash).
    // See also https://crbug.com/1194763#c5.
    render_frame->SetLoaderFactoryBundle(
        base::MakeRefCounted<blink::TrackedChildURLLoaderFactoryBundle>(
            base::WrapUnique(
                static_cast<blink::TrackedChildPendingURLLoaderFactoryBundle*>(
                    params->subresource_loader_factories.release()))));
  } else {
    // In browser-initiated creation of a new main frame (e.g. popup with
    // rel=noopener, or when creating a new tab) the Browser process provides
    // `params->subresource_loader_factories`.
    render_frame->SetLoaderFactoryBundle(
        render_frame->CreateLoaderFactoryBundle(
            std::move(params->subresource_loader_factories),
            /*subresource_overrides=*/std::nullopt,
            /*subresource_proxying_loader_factory=*/mojo::NullRemote(),
            /*keep_alive_loader_factory=*/mojo::NullRemote(),
            /*fetch_later_loader_factory=*/mojo::NullAssociatedRemote()));
  }

  return render_frame;
}

// static
void RenderFrameImpl::CreateFrame(
    AgentSchedulingGroup& agent_scheduling_group,
    const blink::LocalFrameToken& frame_token,
    int routing_id,
    mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
        browser_interface_broker,
    mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
        associated_interface_provider,
    blink::WebView* web_view,
    base::optional_ref<const blink::FrameToken> previous_frame_token,
    base::optional_ref<const blink::FrameToken> opener_frame_token,
    base::optional_ref<const blink::FrameToken> parent_frame_token,
    base::optional_ref<const blink::FrameToken> previous_sibling_frame_token,
    const base::UnguessableToken& devtools_frame_token,
    blink::mojom::TreeScopeType tree_scope_type,
    blink::mojom::FrameReplicationStatePtr replicated_state,
    mojom::CreateFrameWidgetParamsPtr widget_params,
    blink::mojom::FrameOwnerPropertiesPtr frame_owner_properties,
    bool is_on_initial_empty_document,
    const blink::DocumentToken& document_token,
    blink::mojom::PolicyContainerPtr policy_container,
    bool is_for_nested_main_frame) {
  // TODO(danakj): Split this method into two pieces. The first block makes a
  // WebLocalFrame and collects the `blink::WebView` and RenderFrame for it. The
  // second block uses that to make a RenderWidget, if needed.
  RenderFrameImpl* render_frame = nullptr;
  blink::WebLocalFrame* web_frame = nullptr;
  if (!previous_frame_token) {
    // TODO(alexmos): This path is currently used only:
    // 1) When recreating a non-main RenderFrame after a crash.
    // 2) In tests that issue this IPC directly.
    // These two cases should be cleaned up to also pass a previous_frame_token,
    // which would allow removing this branch altogether.  See
    // https://crbug.com/756790.

    CHECK(parent_frame_token);
    WebFrame* parent_web_frame =
        WebFrame::FromFrameToken(parent_frame_token.value());

    // If the browser is sending a valid parent routing id, it should already
    // be created and registered.
    CHECK(parent_web_frame);
    CHECK(parent_web_frame->IsWebRemoteFrame());

    blink::WebFrame* previous_sibling_web_frame = nullptr;
    if (previous_sibling_frame_token) {
      previous_sibling_web_frame =
          blink::WebFrame::FromFrameToken(previous_sibling_frame_token.value());
    }

    // `web_view` would only be set by the function caller when creating a
    // provisional local main frame for a new WebView, which is handled in the
    // other branch of this if clause. Meanwhile, this branch handles subframe
    // creation case only, and we should use the parent's WebView in that case.
    CHECK(!web_view);
    web_view = parent_web_frame->View();

    // Create the RenderFrame and WebLocalFrame, linking the two.
    render_frame = RenderFrameImpl::Create(
        agent_scheduling_group, frame_token, routing_id,
        std::move(frame_receiver), std::move(associated_interface_provider),
        devtools_frame_token, is_for_nested_main_frame);
    render_frame->unique_name_helper_.set_propagated_name(
        replicated_state->unique_name);
    WebFrame* opener = nullptr;
    if (opener_frame_token)
      opener = WebFrame::FromFrameToken(opener_frame_token.value());
    web_frame = parent_web_frame->ToWebRemoteFrame()->CreateLocalChild(
        tree_scope_type, WebString::FromUTF8(replicated_state->name),
        replicated_state->frame_policy, render_frame,
        render_frame->blink_interface_registry_.get(),
        previous_sibling_web_frame,
        frame_owner_properties->To<blink::WebFrameOwnerProperties>(),
        frame_token, opener, document_token,
        std::move(browser_interface_broker),
        ToWebPolicyContainer(std::move(policy_container)));

    // The RenderFrame is created and inserted into the frame tree in the above
    // call to createLocalChild.
    render_frame->in_frame_tree_ = true;
  } else {
    WebFrame* previous_web_frame =
        WebFrame::FromFrameToken(previous_frame_token.value());
    // The previous frame could've been detached while the navigation was being
    // initiated in the browser process. Drop the navigation and don't create
    // the frame in that case.
    // See https://crbug.com/526304.
    if (!previous_web_frame)
      return;

    // This path is creating a local frame. It may or may not be a local root,
    // depending on whether the frame's parent is local or remote. It may also
    // be the main frame, which will be a provisional frame that can either
    // replace a remote main frame in the same WebView or a local main frame in
    // a different WebView.
    if (web_view) {
      // When a `web_view` is set by the caller, it must be for a provisional
      // main frame that will do a local frame swap. In this case, the WebView
      // must be different from the previous frame's WebView.
      CHECK(!previous_web_frame->Parent());
      CHECK_NE(web_view, previous_web_frame->View());
    } else {
      // When not set explicitly, reuse the previous frame's WebView.
      web_view = previous_web_frame->View();
    }
    render_frame = RenderFrameImpl::Create(
        agent_scheduling_group, frame_token, routing_id,
        std::move(frame_receiver), std::move(associated_interface_provider),
        devtools_frame_token, is_for_nested_main_frame);
    web_frame = blink::WebLocalFrame::CreateProvisional(
        render_frame, render_frame->blink_interface_registry_.get(),
        std::move(browser_interface_broker), frame_token, previous_web_frame,
        replicated_state->frame_policy,
        WebString::FromUTF8(replicated_state->name), web_view);
    // The new |web_frame| is a main frame iff the previous frame was.
    DCHECK_EQ(!previous_web_frame->Parent(), !web_frame->Parent());
    // Clone the current unique name so web tests that log frame unique names
    // output something meaningful. At `SwapIn()` time, the unique name will be
    // updated to the latest value.
    render_frame->unique_name_helper_.set_propagated_name(
        GetUniqueNameOfWebFrame(previous_web_frame));
  }

  CHECK(web_view);
  CHECK(render_frame);
  CHECK(web_frame);

  bool is_main_frame = !web_frame->Parent();

  // Child frames require there to be a |parent_routing_id| present, for the
  // remote parent frame. Though it is only used if the |previous_frame_token|
  // is not given, which happens in some corner cases.
  if (!is_main_frame)
    DCHECK(parent_frame_token);

  // We now have a WebLocalFrame for the new frame. The next step is to make
  // a RenderWidget (aka WebWidgetClient) for it, if it is a local root.
  // TODO(crbug.com/40387047): Can we merge this `is_main_frame` block with
  // RenderFrameImpl::CreateMainFrame()?
  if (is_main_frame) {
    // Main frames are always local roots, so they should always have a
    // |widget_params| (and it always comes with a routing id).
    DCHECK(widget_params);
    DCHECK_NE(widget_params->routing_id, MSG_ROUTING_NONE);

    render_frame->MaybeInitializeWidget(std::move(widget_params));

    // Note that we do *not* call WebView's DidAttachLocalMainFrame() here yet
    // because this frame is provisional and not attached to the Page yet. We
    // will tell WebViewImpl about it once it is swapped in.
  } else if (widget_params) {
    DCHECK(widget_params->routing_id != MSG_ROUTING_NONE);

    // This frame is a child local root, so we require a separate RenderWidget
    // for it from any other frames in the frame tree. Each local root defines
    // a separate context/coordinate space/world for compositing, painting,
    // input, etc. And each local root has a RenderWidget which provides
    // such services independent from other RenderWidgets.
    //
    // Notably, we do not attempt to reuse the main frame's RenderWidget (if the
    // main frame in this frame tree is local) as that RenderWidget is
    // functioning in a different local root. Because this is a child local
    // root, it implies there is some remote frame ancestor between this frame
    // and the main frame, thus its coordinate space etc is not known relative
    // to the main frame.
    render_frame->MaybeInitializeWidget(std::move(widget_params));
  }

  if (!is_on_initial_empty_document) {
    render_frame->frame_->SetIsNotOnInitialEmptyDocument();
  }

  render_frame->Initialize(web_frame->Parent());
}

// static
RenderFrame* RenderFrame::FromWebFrame(blink::WebLocalFrame* web_frame) {
  return RenderFrameImpl::FromWebFrame(web_frame);
}

// static
void RenderFrame::ForEach(RenderFrameVisitor* visitor) {
  DCHECK(RenderThread::IsMainThread());
  FrameMap* frames = g_frame_map.Pointer();
  for (auto it = frames->begin(); it != frames->end(); ++it) {
    if (!visitor->Visit(it->second))
      return;
  }
}

// static
RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) {
  DCHECK(RenderThread::IsMainThread());
  auto iter = g_frame_map.Get().find(web_frame);
  if (iter != g_frame_map.Get().end())
    return iter->second;
  return nullptr;
}

// static
void RenderFrameImpl::InstallCreateHook(
    CreateRenderFrameImplFunction create_frame) {
  DCHECK(!g_create_render_frame_impl);
  g_create_render_frame_impl = create_frame;
}

blink::WebURL RenderFrameImpl::OverrideFlashEmbedWithHTML(
    const blink::WebURL& url) {
  return GetContentClient()->renderer()->OverrideFlashEmbedWithHTML(url);
}

// RenderFrameImpl::CreateParams --------------------------------------------

RenderFrameImpl::CreateParams::CreateParams(
    AgentSchedulingGroup& agent_scheduling_group,
    const blink::LocalFrameToken& frame_token,
    int32_t routing_id,
    mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
    mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
        associated_interface_provider,
    const base::UnguessableToken& devtools_frame_token,
    bool is_for_nested_main_frame)
    : agent_scheduling_group(&agent_scheduling_group),
      frame_token(frame_token),
      routing_id(routing_id),
      frame_receiver(std::move(frame_receiver)),
      associated_interface_provider(std::move(associated_interface_provider)),
      devtools_frame_token(devtools_frame_token),
      is_for_nested_main_frame(is_for_nested_main_frame) {}
RenderFrameImpl::CreateParams::~CreateParams() = default;
RenderFrameImpl::CreateParams::CreateParams(CreateParams&&) = default;
RenderFrameImpl::CreateParams& RenderFrameImpl::CreateParams::operator=(
    CreateParams&&) = default;

// RenderFrameImpl ----------------------------------------------------------
RenderFrameImpl::RenderFrameImpl(CreateParams params)
    : agent_scheduling_group_(*params.agent_scheduling_group),
      is_main_frame_(true),
      unique_name_frame_adapter_(this),
      unique_name_helper_(&unique_name_frame_adapter_),
      in_frame_tree_(false),
      frame_token_(params.frame_token),
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
      routing_id_(params.routing_id),
#endif
      process_label_id_(
          base::trace_event::TraceLog::GetInstance()->GetNewProcessLabelId()),
      selection_text_offset_(0),
      selection_range_(gfx::Range::InvalidRange()),
      render_accessibility_manager_(
          std::make_unique<RenderAccessibilityManager>(this)),
      weak_wrapper_resource_load_info_notifier_(
          std::make_unique<blink::WeakWrapperResourceLoadInfoNotifier>(this)),
#if BUILDFLAG(ENABLE_PPAPI)
      focused_pepper_plugin_(nullptr),
#endif
      navigation_client_impl_(nullptr),
      media_factory_(
          this,
          base::BindRepeating(&RenderFrameImpl::RequestOverlayRoutingToken,
                              base::Unretained(this))),
      devtools_frame_token_(params.devtools_frame_token),
      is_for_nested_main_frame_(params.is_for_nested_main_frame) {
  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::RenderFrameImpl",
                         TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT);
  DCHECK(RenderThread::IsMainThread());
  blink_interface_registry_ = std::make_unique<BlinkInterfaceRegistryImpl>(
      registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr());

  DCHECK(params.frame_receiver.is_valid());
  pending_frame_receiver_ = std::move(params.frame_receiver);

  // Save the pending remote for lazy binding in
  // `GetRemoteAssociatedInterfaces().
  DCHECK(params.associated_interface_provider.is_valid());
  pending_associated_interface_provider_remote_ =
      std::move(params.associated_interface_provider);

  delayed_state_sync_timer_.SetTaskRunner(
      agent_scheduling_group_->agent_group_scheduler().DefaultTaskRunner());

  // Must call after binding our own remote interfaces.
  media_factory_.SetupMojo();

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  std::pair<RoutingIDFrameMap::iterator, bool> result =
      g_routing_id_frame_map.Get().insert(std::make_pair(routing_id_, this));
  CHECK(result.second) << "Inserting a duplicate item.";
#endif
}

mojom::FrameHost* RenderFrameImpl::GetFrameHost() {
  if (!frame_host_remote_.is_bound())
    GetRemoteAssociatedInterfaces()->GetInterface(&frame_host_remote_);
  return frame_host_remote_.get();
}

RenderFrameImpl::~RenderFrameImpl() {
  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::~RenderFrameImpl",
                         TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
  for (auto& observer : observers_)
    observer.OnDestruct();
  for (auto& observer : observers_)
    observer.RenderFrameGone();

  web_media_stream_device_observer_.reset();

  if (initialized_ && is_main_frame_)
    MainFrameCounter::DecrementCount();

  base::trace_event::TraceLog::GetInstance()->RemoveProcessLabel(
      process_label_id_);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  g_routing_id_frame_map.Get().erase(routing_id_);
#endif
  agent_scheduling_group_->RemoveFrameRoute(frame_token_
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
                                            ,
                                            routing_id_
#endif
  );
}

void RenderFrameImpl::Initialize(blink::WebFrame* parent) {
  initialized_ = true;
  is_main_frame_ = !parent;
  if (is_main_frame_)
    MainFrameCounter::IncrementCount();

  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::Initialize", "frame_token",
               frame_token_);

#if BUILDFLAG(ENABLE_PPAPI)
  new PepperBrowserConnection(this);
#endif

  RegisterMojoInterfaces();

  {
    TRACE_EVENT("navigation", "ContentRendererClient::RenderFrameCreated");
    // We delay calling this until we have the WebFrame so that any observer or
    // embedder can call GetWebFrame on any RenderFrame.
    GetContentClient()->renderer()->RenderFrameCreated(this);
  }

  // blink::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
  auto& factory = blink::AudioOutputIPCFactory::GetInstance();
  if (factory.io_task_runner()) {
    factory.RegisterRemoteFactory(GetWebFrame()->GetLocalFrameToken(),
                                  GetBrowserInterfaceBroker());
  }

  // Bind this class to mojom::Frame and to the message router for legacy IPC.
  // These must be called after |frame_| is set since binding requires a
  // per-frame task runner.
  frame_receiver_.Bind(
      std::move(pending_frame_receiver_),
      GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
  agent_scheduling_group_->AddFrameRoute(
      frame_token_,
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
      routing_id_,
#endif
      this, GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}

void RenderFrameImpl::GetInterface(
    const std::string& interface_name,
    mojo::ScopedMessagePipeHandle interface_pipe) {
  if (registry_.TryBindInterface(interface_name, &interface_pipe))
    return;

  for (auto& observer : observers_) {
    observer.OnInterfaceRequestForFrame(interface_name, &interface_pipe);
    if (!interface_pipe.is_valid())
      return;
  }
}

blink::WebFrameWidget* RenderFrameImpl::GetLocalRootWebFrameWidget() {
  return frame_->LocalRoot()->FrameWidget();
}

#if BUILDFLAG(ENABLE_PPAPI)
void RenderFrameImpl::PepperPluginCreated(RendererPpapiHost* host) {
  for (auto& observer : observers_)
    observer.DidCreatePepperPlugin(host);
}

void RenderFrameImpl::PepperTextInputTypeChanged(
    PepperPluginInstanceImpl* instance) {
  if (instance != focused_pepper_plugin_)
    return;

  GetLocalRootWebFrameWidget()->UpdateTextInputState();
}

void RenderFrameImpl::PepperCaretPositionChanged(
    PepperPluginInstanceImpl* instance) {
  if (instance != focused_pepper_plugin_)
    return;
  GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}

void RenderFrameImpl::PepperCancelComposition(
    PepperPluginInstanceImpl* instance) {
  if (instance != focused_pepper_plugin_)
    return;
  GetLocalRootWebFrameWidget()->CancelCompositionForPepper();
}

void RenderFrameImpl::PepperSelectionChanged(
    PepperPluginInstanceImpl* instance) {
  if (instance != focused_pepper_plugin_)
    return;

  // We have no reason to believe the locally cached last synced selection is
  // invalid so we do not need to force the update if it matches our last synced
  // value.
  SyncSelectionIfRequired(blink::SyncCondition::kNotForced);
}

#endif  // BUILDFLAG(ENABLE_PPAPI)

void RenderFrameImpl::ScriptedPrint() {
  bool user_initiated = GetLocalRootWebFrameWidget()->HandlingInputEvent();
  for (auto& observer : observers_)
    observer.ScriptedPrint(user_initiated);
}

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
bool RenderFrameImpl::Send(IPC::Message* message) {
  return agent_scheduling_group_->Send(message);
}

bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
  // We may get here while detaching, when the WebFrame has been deleted.  Do
  // not process any messages in this state.
  if (!frame_)
    return false;

  DCHECK(!frame_->GetDocument().IsNull());

  GetContentClient()->SetActiveURL(
      frame_->GetDocument().Url(),
      frame_->Top()->GetSecurityOrigin().ToString().Utf8());

  for (auto& observer : observers_) {
    if (observer.OnMessageReceived(msg))
      return true;
  }
  return false;
}
#endif

void RenderFrameImpl::OnAssociatedInterfaceRequest(
    const std::string& interface_name,
    mojo::ScopedInterfaceEndpointHandle handle) {
  if (!associated_interfaces_.TryBindInterface(interface_name, &handle)) {
    for (auto& observer : observers_) {
      if (observer.OnAssociatedInterfaceRequestForFrame(interface_name,
                                                        &handle)) {
        return;
      }
    }
  }
}

void RenderFrameImpl::SetUpSharedMemoryForUkms(
    base::ReadOnlySharedMemoryRegion smoothness_memory,
    base::ReadOnlySharedMemoryRegion dropped_frames_memory) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "RenderFrameImpl::SetUpSharedMemoryForUkms",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  DCHECK(smoothness_memory.IsValid() && dropped_frames_memory.IsValid());
  for (auto& observer : observers_) {
    DCHECK(smoothness_memory.IsValid() && dropped_frames_memory.IsValid());
    if (observer.SetUpUkmReporting(smoothness_memory, dropped_frames_memory)) {
      break;
    }
  }
}

void RenderFrameImpl::BindAutoplayConfiguration(
    mojo::PendingAssociatedReceiver<blink::mojom::AutoplayConfigurationClient>
        receiver) {
  autoplay_configuration_receiver_.reset();
  autoplay_configuration_receiver_.Bind(
      std::move(receiver),
      GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}

void RenderFrameImpl::BindFrameBindingsControl(
    mojo::PendingAssociatedReceiver<mojom::FrameBindingsControl> receiver) {
  frame_bindings_control_receiver_.Bind(
      std::move(receiver),
      GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}

void RenderFrameImpl::BindNavigationClient(
    mojo::PendingAssociatedReceiver<mojom::NavigationClient> receiver) {
  // If the provisional owner frame is a local frame and it has a non-null
  // `navigation_client_impl_`, it began the navigation. Some properties need
  // to be copied over from the original `NavigationClient` to preserve
  // behaviour.
  NavigationClient* initiator_navigation_client = nullptr;
  WebFrame* initiator_frame = GetWebFrame()->GetProvisionalOwnerFrame();
  if (initiator_frame && initiator_frame->IsWebLocalFrame()) {
    initiator_navigation_client = RenderFrameImpl::FromWebFrame(initiator_frame)
                                      ->navigation_client_impl_.get();
  }
  navigation_client_impl_ =
      std::make_unique<NavigationClient>(this, initiator_navigation_client);
  navigation_client_impl_->Bind(std::move(receiver));
}

void RenderFrameImpl::BindNavigationClientWithParams(
    mojo::PendingAssociatedReceiver<mojom::NavigationClient> receiver,
    blink::mojom::BeginNavigationParamsPtr begin_params,
    blink::mojom::CommonNavigationParamsPtr common_params,
    bool is_duplicate_navigation) {
  if (navigation_client_impl_) {
    navigation_client_impl_->ResetForNewNavigation(is_duplicate_navigation);
  }
  navigation_client_impl_ = std::make_unique<NavigationClient>(
      this, std::move(begin_params), std::move(common_params));
  navigation_client_impl_->Bind(std::move(receiver));
}

// Unload this RenderFrame so the frame can navigate to a document rendered by
// a different process. We also allow this process to exit if there are no other
// active RenderFrames in it.
// This executes the unload handlers on this frame and its local descendants.
void RenderFrameImpl::Unload(
    bool is_loading,
    blink::mojom::FrameReplicationStatePtr replicated_frame_state,
    const blink::RemoteFrameToken& proxy_frame_token,
    blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
    blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces,
    const std::optional<base::UnguessableToken>& devtools_frame_token) {
  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::UnloadFrame", "frame_token",
               frame_token_);
  DCHECK(!base::RunLoop::IsNestedOnCurrentThread());

  // Send an UpdateState message before we get deleted.
  // TODO(dcheng): Improve this comment to clarify why it's important to sent
  // state updates.
  SendUpdateState();

  // Before `this` is destroyed, save any fields needed to schedule a call to
  // `AgentSchedulingGroupHost::DidUnloadRenderFrame()`. The acknowledgement
  // itself is asynchronous to ensure that any postMessage calls (which schedule
  // IPCs as well) made from unload handlers are routed to the browser process
  // before the corresponding `RenderFrameHostImpl` is torn down.
  auto& agent_scheduling_group = *agent_scheduling_group_;
  blink::LocalFrameToken frame_token = frame_->GetLocalFrameToken();
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      GetTaskRunner(blink::TaskType::kInternalPostMessageForwarding);

  // Important: |this| is deleted after this call!
  if (!SwapOutAndDeleteThis(
          is_loading, std::move(replicated_frame_state), proxy_frame_token,
          std::move(remote_frame_interfaces),
          std::move(remote_main_frame_interfaces), devtools_frame_token)) {
    // The swap is cancelled because running the unload handlers ended up
    // detaching this frame.
    return;
  }

  // Notify the browser that this frame was swapped out. Use the cached
  // `AgentSchedulingGroup` because |this| is deleted. Post a task to send the
  // ACK, so that any postMessage IPCs scheduled from the unload handler are
  // sent before the ACK (see https://crbug.com/857274).
  auto send_unload_ack = base::BindOnce(
      [](AgentSchedulingGroup* agent_scheduling_group,
         const blink::LocalFrameToken& frame_token) {
        agent_scheduling_group->DidUnloadRenderFrame(frame_token);
      },
      &agent_scheduling_group, frame_token);
  task_runner->PostTask(FROM_HERE, std::move(send_unload_ack));
}

void RenderFrameImpl::Delete(mojom::FrameDeleteIntention intent) {
  TRACE_EVENT(
      "navigation", "RenderFrameImpl::Delete", [&](perfetto::EventContext ctx) {
        auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
        auto* data = event->set_render_frame_impl_deletion();
        data->set_intent(FrameDeleteIntentionToProto(intent));
      });
  base::ScopedUmaHistogramTimer histogram_timer("Navigation.RenderFrameDelete");
  // The main frame (when not provisional) is owned by the renderer's frame tree
  // via WebViewImpl. When a provisional main frame is swapped in, the ownership
  // moves from the browser to the renderer, but this happens in the renderer
  // process and is then the browser is informed.
  // If the provisional main frame is swapped in while the browser is destroying
  // it, the browser may request to delete |this|, thinking it has ownership
  // of it, but the renderer has already taken ownership via SwapIn().
  switch (intent) {
    case mojom::FrameDeleteIntention::kNotMainFrame:
      // The frame was not a main frame, so the browser should always have
      // ownership of it and we can just proceed with deleting it on
      // request.
      DCHECK(!is_main_frame_);
      break;
    case mojom::FrameDeleteIntention::kSpeculativeMainFrameForShutdown:
      // In this case the renderer has taken ownership of the provisional main
      // frame but the browser did not know yet and is shutting down. We can
      // ignore this request as the frame will be destroyed when the RenderView
      // is. This handles the shutdown case of https://crbug.com/957858.
      DCHECK(is_main_frame_);
      if (in_frame_tree_)
        return;
      break;
    case mojom::FrameDeleteIntention::
        kSpeculativeMainFrameForNavigationCancelled:
      // In this case the browser was navigating and cancelled the speculative
      // navigation. The renderer *should* undo the SwapIn() but the old state
      // has already been destroyed. Both ignoring the message or handling it
      // would leave the renderer in an inconsistent state now. If we ignore it
      // then the browser thinks the `blink::WebView` has a remote main frame,
      // but it is incorrect. If we handle it, then we are deleting a local main
      // frame out from under the `blink::WebView` and we will have bad pointers
      // in the renderer. So all we can do is crash. We should instead prevent
      // this scenario by blocking the browser from dropping the speculative
      // main frame when a commit (and ownership transfer) is imminent.
      // TODO(dcheng): This is the case of https://crbug.com/838348.
      DCHECK(is_main_frame_);
#if !BUILDFLAG(IS_ANDROID)
      CHECK(!in_frame_tree_);
#else
      // Previously this CHECK() was disabled on Android because it was much
      // easier to hit the race there.
      CHECK(!in_frame_tree_, base::NotFatalUntil::M135);
#endif  // !BUILDFLAG(IS_ANDROID)
      break;
  }

  // This will result in a call to RenderFrameImpl::FrameDetached, which
  // deletes the object. Do not access |this| after detach.
  frame_->Detach();
}

void RenderFrameImpl::UndoCommitNavigation(
    bool is_loading,
    blink::mojom::FrameReplicationStatePtr replicated_frame_state,
    const blink::RemoteFrameToken& proxy_frame_token,
    blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
    blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
  // The browser process asked `this` to commit a navigation but has now decided
  // to discard the speculative RenderFrameHostImpl instead, since the
  // associated navigation was cancelled or replaced. However, the browser
  // process hasn't heard the `DidCommitNavigation()` yet, so pretend that the
  // commit never happened by immediately swapping `this` back to a proxy.
  //
  // This means that any state changes triggered by the already-swapped in
  // RenderFrame will simply be ignored, but that can't be helped: the
  // browser-side RFH will be gone before any outgoing IPCs from the renderer
  // for this RenderFrame (which by definition, are still in-flight) will be
  // processed by the browser process (as it has not yet seen the
  // `DidCommitNavigation()`).
  SwapOutAndDeleteThis(is_loading, std::move(replicated_frame_state),
                       proxy_frame_token, std::move(remote_frame_interfaces),
                       std::move(remote_main_frame_interfaces),
                       /*devtools_frame_token=*/std::nullopt);
}

void RenderFrameImpl::SnapshotAccessibilityTree(
    mojom::SnapshotAccessibilityTreeParamsPtr params,
    SnapshotAccessibilityTreeCallback callback) {
  ui::AXTreeUpdate response;
  AXTreeSnapshotterImpl snapshotter(this, ui::AXMode(params->ax_mode));
  snapshotter.Snapshot(params->max_nodes, params->timeout, &response);
  std::move(callback).Run(response);
}

void RenderFrameImpl::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> handler_remote) {
  // Convert input to the canonical way of passing a map into a Blink API.
  LinkRewritingDelegate delegate(url_map, frame_token_map);
  RenderFrameWebFrameSerializerClient client(std::move(handler_remote));

  // Serialize the frame (without recursing into subframes).
  WebFrameSerializer::Serialize(GetWebFrame(), &client, &delegate,
                                save_with_empty_url);
}

void RenderFrameImpl::SetWantErrorMessageStackTrace() {
  want_error_message_stack_trace_ = true;
  GetAgentGroupScheduler().Isolate()->SetCaptureStackTraceForUncaughtExceptions(
      true);
}

void RenderFrameImpl::NotifyObserversOfFailedProvisionalLoad() {
  for (auto& observer : observers_)
    observer.DidFailProvisionalLoad();
}

void RenderFrameImpl::DidMeaningfulLayout(
    blink::WebMeaningfulLayout layout_type) {
  for (auto& observer : observers_)
    observer.DidMeaningfulLayout(layout_type);
}

void RenderFrameImpl::DidCommitAndDrawCompositorFrame() {
#if BUILDFLAG(ENABLE_PPAPI)
  // Notify all instances that we painted. The same caveats apply as for
  // ViewFlushedPaint regarding instances closing themselves, so we take
  // similar precautions.
  PepperPluginSet plugins = active_pepper_instances_;
  for (PepperPluginInstanceImpl* plugin : plugins) {
    if (base::Contains(active_pepper_instances_, plugin)) {
      plugin->ViewInitiatedPaint();
    }
  }
#endif
}

RenderFrame* RenderFrameImpl::GetMainRenderFrame() {
  WebFrame* main_frame = GetWebView()->MainFrame();
  DCHECK(main_frame);
  if (!main_frame->IsWebLocalFrame())
    return nullptr;
  return RenderFrame::FromWebFrame(main_frame->ToWebLocalFrame());
}

RenderAccessibility* RenderFrameImpl::GetRenderAccessibility() {
  return render_accessibility_manager_->GetRenderAccessibilityImpl();
}

std::unique_ptr<AXTreeSnapshotter> RenderFrameImpl::CreateAXTreeSnapshotter(
    ui::AXMode ax_mode) {
  return std::make_unique<AXTreeSnapshotterImpl>(this, ax_mode);
}

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
int RenderFrameImpl::GetRoutingID() {
  return routing_id_;
}
#endif

blink::WebLocalFrame* RenderFrameImpl::GetWebFrame() {
  DCHECK(frame_);
  return frame_;
}

const blink::WebLocalFrame* RenderFrameImpl::GetWebFrame() const {
  DCHECK(frame_);
  return frame_;
}

blink::WebView* RenderFrameImpl::GetWebView() {
  blink::WebView* web_view = GetWebFrame()->View();
  DCHECK(web_view);
  return web_view;
}

const blink::WebView* RenderFrameImpl::GetWebView() const {
  const blink::WebView* web_view = GetWebFrame()->View();
  DCHECK(web_view);
  return web_view;
}

const blink::web_pref::WebPreferences& RenderFrameImpl::GetBlinkPreferences() {
  return GetWebView()->GetWebPreferences();
}

const blink::RendererPreferences& RenderFrameImpl::GetRendererPreferences()
    const {
  return GetWebView()->GetRendererPreferences();
}

void RenderFrameImpl::ShowVirtualKeyboard() {
  GetLocalRootWebFrameWidget()->ShowVirtualKeyboard();
}

blink::WebPlugin* RenderFrameImpl::CreatePlugin(
    const WebPluginInfo& info,
    const blink::WebPluginParams& params) {
#if BUILDFLAG(ENABLE_PPAPI)
  std::optional<url::Origin> origin_lock;
  if (GetContentClient()->renderer()->IsOriginIsolatedPepperPlugin(info.path)) {
    origin_lock = url::Origin::Create(GURL(params.url));
  }

  bool pepper_plugin_was_registered = false;
  scoped_refptr<PluginModule> pepper_module(PluginModule::Create(
      this, info, origin_lock, &pepper_plugin_was_registered,
      GetTaskRunner(blink::TaskType::kNetworking)));
  if (pepper_plugin_was_registered) {
    if (pepper_module.get()) {
      return new PepperWebPluginImpl(pepper_module.get(), params, this);
    }
  }
#if BUILDFLAG(IS_CHROMEOS)
  LOG(WARNING) << "Pepper module/plugin creation failed.";
#endif
#endif  // BUILDFLAG(ENABLE_PPAPI)
  return nullptr;
}

void RenderFrameImpl::ExecuteJavaScript(const std::u16string& javascript) {
  v8::HandleScope handle_scope(GetAgentGroupScheduler().Isolate());
  frame_->ExecuteScript(WebScriptSource(WebString::FromUTF16(javascript)));
}

void RenderFrameImpl::BindLocalInterface(
    const std::string& interface_name,
    mojo::ScopedMessagePipeHandle interface_pipe) {
  GetInterface(interface_name, std::move(interface_pipe));
}

blink::AssociatedInterfaceRegistry*
RenderFrameImpl::GetAssociatedInterfaceRegistry() {
  return &associated_interfaces_;
}

blink::AssociatedInterfaceProvider*
RenderFrameImpl::GetRemoteAssociatedInterfaces() {
  if (!remote_associated_interfaces_) {
    DCHECK(pending_associated_interface_provider_remote_);
    remote_associated_interfaces_ =
        std::make_unique<blink::AssociatedInterfaceProvider>(
            std::move(pending_associated_interface_provider_remote_),
            GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
  }
  return remote_associated_interfaces_.get();
}

void RenderFrameImpl::SetSelectedText(const std::u16string& selection_text,
                                      size_t offset,
                                      const gfx::Range& range) {
  GetWebFrame()->TextSelectionChanged(WebString::FromUTF16(selection_text),
                                      static_cast<uint32_t>(offset), range);
}

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

bool RenderFrameImpl::IsPasting() {
  return GetLocalRootWebFrameWidget()->IsPasting();
}

// blink::mojom::AutoplayConfigurationClient implementation
// --------------------------

void RenderFrameImpl::AddAutoplayFlags(const url::Origin& origin,
                                       const int32_t flags) {
  // If the origin is the same as the previously stored flags then we should
  // merge the two sets of flags together.
  if (autoplay_flags_.first == origin) {
    autoplay_flags_.second |= flags;
  } else {
    autoplay_flags_ = std::make_pair(origin, flags);
  }
}

// blink::mojom::ResourceLoadInfoNotifier implementation
// --------------------------

#if BUILDFLAG(IS_ANDROID)
void RenderFrameImpl::NotifyUpdateUserGestureCarryoverInfo() {
  GetFrameHost()->UpdateUserGestureCarryoverInfo();
}
#endif

void RenderFrameImpl::NotifyResourceRedirectReceived(
    const net::RedirectInfo& redirect_info,
    network::mojom::URLResponseHeadPtr redirect_response) {}

void RenderFrameImpl::NotifyResourceResponseReceived(
    int64_t request_id,
    const url::SchemeHostPort& final_response_url,
    network::mojom::URLResponseHeadPtr response_head,
    network::mojom::RequestDestination request_destination,
    bool is_ad_resource) {
  if (!blink::IsRequestDestinationFrame(request_destination)) {
    bool notify = ShouldNotifySubresourceResponseStarted(
        GetWebView()->GetRendererPreferences());
    UMA_HISTOGRAM_BOOLEAN(
        "Renderer.ReduceSubresourceResponseIPC.DidNotifyBrowser", notify);
    if (notify) {
      GetFrameHost()->SubresourceResponseStarted(final_response_url,
                                                 response_head->cert_status);
    }
  }
  DidStartResponse(final_response_url, request_id, std::move(response_head),
                   request_destination, is_ad_resource);
}

void RenderFrameImpl::NotifyResourceTransferSizeUpdated(
    int64_t request_id,
    int32_t transfer_size_diff) {
  DidReceiveTransferSizeUpdate(request_id, transfer_size_diff);
}

void RenderFrameImpl::NotifyResourceLoadCompleted(
    blink::mojom::ResourceLoadInfoPtr resource_load_info,
    const network::URLLoaderCompletionStatus& status) {
  DidCompleteResponse(resource_load_info->request_id, status);
  GetFrameHost()->ResourceLoadComplete(std::move(resource_load_info));
}

void RenderFrameImpl::NotifyResourceLoadCanceled(int64_t request_id) {
  DidCancelResponse(request_id);
}

void RenderFrameImpl::Clone(
    mojo::PendingReceiver<blink::mojom::ResourceLoadInfoNotifier>
        pending_resource_load_info_notifier) {
  resource_load_info_notifier_receivers_.Add(
      this, std::move(pending_resource_load_info_notifier),
      agent_scheduling_group_->agent_group_scheduler().DefaultTaskRunner());
}

void RenderFrameImpl::GetInterfaceProvider(
    mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver) {
  auto task_runner = GetTaskRunner(blink::TaskType::kInternalDefault);
  DCHECK(task_runner);
  interface_provider_receivers_.Add(this, std::move(receiver), task_runner);
}

void RenderFrameImpl::AllowBindings(int64_t enabled_bindings_flags) {
  auto new_bindings =
      BindingsPolicySet::FromEnumBitmask(enabled_bindings_flags);
  enabled_bindings_.PutAll(new_bindings);

  if (new_bindings.Has(BindingsPolicyValue::kMojoWebUi)) {
    // If mojo web UI is being enabled, update the protected memory bool to
    // allow MojoJS binding in this process.
    blink::WebV8Features::AllowMojoJSForProcess();
  }
}

void RenderFrameImpl::EnableMojoJsBindings(
    content::mojom::ExtraMojoJsFeaturesPtr features) {
  enable_mojo_js_bindings_ = true;
  mojo_js_features_ = std::move(features);

  // Update the protected memory bool to allow MojoJS binding in this process.
  blink::WebV8Features::AllowMojoJSForProcess();
}

void RenderFrameImpl::EnableMojoJsBindingsWithBroker(
    mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker) {
  mojo_js_interface_broker_ = std::move(broker);

  // Update the protected memory bool to allow MojoJS binding in this process.
  blink::WebV8Features::AllowMojoJSForProcess();
}

void RenderFrameImpl::BindWebUI(
    mojo::PendingAssociatedReceiver<mojom::WebUI> receiver,
    mojo::PendingAssociatedRemote<mojom::WebUIHost> remote) {
  DCHECK(enabled_bindings_.Has(BindingsPolicyValue::kWebUi));
  WebUIExtensionData::Create(this, std::move(receiver), std::move(remote));
}

void RenderFrameImpl::SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
    const blink::mojom::OldPageInfo* old_page_info,
    const GURL& new_page_url) {
  if (!old_page_info)
    return;

  WebLocalFrame* old_main_web_frame = WebLocalFrame::FromFrameToken(
      old_page_info->frame_token_for_old_main_frame);
  if (!old_main_web_frame) {
    // Even if we sent a valid `frame_token_for_old_main_frame`, it might have
    // already been destroyed by the time we try to get the WebLocalFrame, so
    // we should check if it still exists.
    return;
  }
  RenderFrameImpl* old_main_render_frame =
      RenderFrameImpl::FromWebFrame(old_main_web_frame);
  if (!old_main_render_frame) {
    return;
  }
  if (!IsMainFrame() && !old_main_render_frame->IsMainFrame()) {
    // This shouldn't happen because `old_page_info` should only be set on
    // cross-BrowsingInstance navigations, which can only happen on main frames.
    // However, we got some reports of this happening (see
    // https://crbug.com/1207271).
    SCOPED_CRASH_KEY_BOOL("old_page_info", "new_is_main_frame", IsMainFrame());
    SCOPED_CRASH_KEY_STRING256("old_page_info", "new_url", new_page_url.spec());
    SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_main_frame",
                          old_main_render_frame->IsMainFrame());
    SCOPED_CRASH_KEY_STRING256("old_page_info", "old_url",
                               old_main_render_frame->GetLoadingUrl().spec());
    SCOPED_CRASH_KEY_BOOL(
        "old_page_info", "old_is_frozen",
        old_page_info->new_lifecycle_state_for_old_page->is_frozen);
    SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_in_bfcache",
                          old_page_info->new_lifecycle_state_for_old_page
                              ->is_in_back_forward_cache);
    SCOPED_CRASH_KEY_BOOL(
        "old_page_info", "old_is_hidden",
        old_page_info->new_lifecycle_state_for_old_page->visibility ==
            blink::mojom::PageVisibilityState::kHidden);
    SCOPED_CRASH_KEY_BOOL(
        "old_page_info", "old_pagehide_dispatch",
        old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch ==
            blink::mojom::PagehideDispatch::kNotDispatched);
    CaptureTraceForNavigationDebugScenario(
        DebugScenario::kDebugNonMainFrameWithOldPageInfo);
    return;
  }
  DCHECK_EQ(old_page_info->new_lifecycle_state_for_old_page->visibility,
            blink::mojom::PageVisibilityState::kHidden);
  DCHECK_NE(old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch,
            blink::mojom::PagehideDispatch::kNotDispatched);
  old_main_web_frame->View()->SetPageLifecycleStateFromNewPageCommit(
      old_page_info->new_lifecycle_state_for_old_page->visibility,
      old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch);
}

void RenderFrameImpl::CommitNavigation(
    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,
    std::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
        subresource_overrides,
    blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info,
    blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        subresource_proxying_loader_factory,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        keep_alive_loader_factory,
    mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
        fetch_later_loader_factory,
    const blink::DocumentToken& document_token,
    const base::UnguessableToken& devtools_navigation_token,
    const base::Uuid& base_auction_nonce,
    const std::optional<network::ParsedPermissionsPolicy>& permissions_policy,
    blink::mojom::PolicyContainerPtr policy_container,
    mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host,
    mojo::PendingRemote<blink::mojom::CodeCacheHost>
        code_cache_host_for_background,
    mojom::CookieManagerInfoPtr cookie_manager_info,
    mojom::StorageInfoPtr storage_info,
    mojom::NavigationClient::CommitNavigationCallback commit_callback) {
  base::ElapsedTimer timer;
  base::ScopedUmaHistogramTimer histogram_timer(kCommitRenderFrame);
  base::ScopedUmaHistogramTimer histogram_timer_frame(base::StrCat(
      {kCommitRenderFrame, IsMainFrame() ? ".MainFrame" : ".Subframe"}));
  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::CommitNavigation",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  DCHECK(navigation_client_impl_);
  DCHECK(!blink::IsRendererDebugURL(common_params->url));
  DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
  // `origin_to_commit` must only be set on failed navigations or  data: URL
  // navigations, except when kUseBrowserCalculatedOrigin is enabled.
  CHECK(!commit_params->origin_to_commit ||
        common_params->url.SchemeIs(url::kDataScheme) ||
        base::FeatureList::IsEnabled(features::kUseBrowserCalculatedOrigin));
  LogCommitHistograms(commit_params->commit_sent, is_main_frame_,
                      common_params->url);

  bool is_new_navigation_in_outermost_main_frame_with_http_or_https = false;
  if (frame_->IsOutermostMainFrame() &&
      common_params->url.SchemeIsHTTPOrHTTPS()) {
    switch (common_params->navigation_type) {
      case blink::mojom::NavigationType::DIFFERENT_DOCUMENT:
        is_new_navigation_in_outermost_main_frame_with_http_or_https = true;
        break;
      case blink::mojom::NavigationType::RELOAD:
      case blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE:
      case blink::mojom::NavigationType::RESTORE:
      case blink::mojom::NavigationType::RESTORE_WITH_POST:
      case blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT:
      case blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT:
      case blink::mojom::NavigationType::SAME_DOCUMENT:
        break;
    }
  }

  AssertNavigationCommits assert_navigation_commits(
      this, kMayReplaceInitialEmptyDocument);

  SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
      commit_params->old_page_info.get(), common_params->url);

  bool was_initiated_in_this_frame =
      navigation_client_impl_ &&
      navigation_client_impl_->was_initiated_in_this_frame();

  // Sanity check that the browser always sends us new loader factories on
  // cross-document navigations.
  DCHECK(common_params->url.SchemeIs(url::kJavaScriptScheme) ||
         subresource_loader_factories);

  int request_id = blink::GenerateRequestId();
  std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
      *common_params, *commit_params, std::move(commit_callback),
      std::move(navigation_client_impl_), request_id,
      was_initiated_in_this_frame);

  // Check if the navigation being committed originated as a client redirect.
  bool is_client_redirect =
      !!(common_params->transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT);
  auto navigation_params = std::make_unique<WebNavigationParams>(
      document_token, devtools_navigation_token, base_auction_nonce);
  navigation_params->navigation_delivery_type =
      commit_params->navigation_delivery_type;
  navigation_params->is_client_redirect = is_client_redirect;
  FillMiscNavigationParams(*common_params, *commit_params,
                           navigation_params.get());
  navigation_params->policy_container =
      ToWebPolicyContainer(std::move(policy_container));
  navigation_params->view_transition_state =
      std::move(commit_params->view_transition_state);

  if (frame_->IsOutermostMainFrame() && permissions_policy) {
    navigation_params->permissions_policy_override = permissions_policy;
  }

  auto commit_with_params = base::BindOnce(
      &RenderFrameImpl::CommitNavigationWithParams, weak_factory_.GetWeakPtr(),
      common_params.Clone(), commit_params.Clone(),
      std::move(subresource_loader_factories), std::move(subresource_overrides),
      std::move(controller_service_worker_info), std::move(container_info),
      std::move(subresource_proxying_loader_factory),
      std::move(keep_alive_loader_factory),
      std::move(fetch_later_loader_factory), std::move(code_cache_host),
      std::move(code_cache_host_for_background), std::move(cookie_manager_info),
      std::move(storage_info), std::move(document_state));

  // Handle a navigation that has a non-empty `data_url_as_string`, or perform
  // a "loadDataWithBaseURL" navigation, which is different from a normal data:
  // URL navigation in various ways:
  // - The "document URL" will use the supplied `base_url_for_data_url` if it's
  // not empty (otherwise it will fall back to the data: URL).
  // - The actual data: URL will be saved in the document's DocumentState to
  // later be returned as the `url` in DidCommitProvisionalLoadParams.
  bool should_handle_data_url_as_string = false;
#if BUILDFLAG(IS_ANDROID)
  should_handle_data_url_as_string |=
      is_main_frame_ && !commit_params->data_url_as_string.empty();
#endif

  if (should_handle_data_url_as_string ||
      commit_params->is_load_data_with_base_url) {
    std::string mime_type, charset, data;
    // `base_url` will be set to `base_url_for_data_url` from `common_params`,
    // unless it's empty (the `data_url_as_string` handling case), in which
    // case `url` from `common_params` will be used.
    GURL base_url;
    DecodeDataURL(*common_params, *commit_params, &mime_type, &charset, &data,
                  &base_url);
    // Note that even though we use the term "base URL", `base_url` is also
    // used as the "document URL" (unlike normal "base URL"s set through the
    // <base> element or other means, which would only be used to resolve
    // relative URLs, etc).
    navigation_params->url = base_url;
    WebNavigationParams::FillStaticResponse(navigation_params.get(),
                                            WebString::FromUTF8(mime_type),
                                            WebString::FromUTF8(charset), data);
    std::move(commit_with_params).Run(std::move(navigation_params));
    return;
  }

  FillNavigationParamsRequest(*common_params, *commit_params,
                              navigation_params.get());
  if (!url_loader_client_endpoints &&
      common_params->url.SchemeIs(url::kDataScheme)) {
    // Normally, data urls will have |url_loader_client_endpoints| set.
    // However, tests and interstitial pages pass data urls directly,
    // without creating url loader.
    std::string mime_type, charset, data;
    if (!net::DataURL::Parse(common_params->url, &mime_type, &charset, &data)) {
      NOTREACHED() << "Invalid URL passed: "
                   << common_params->url.possibly_invalid_spec();
    }
    WebNavigationParams::FillStaticResponse(navigation_params.get(),
                                            WebString::FromUTF8(mime_type),
                                            WebString::FromUTF8(charset), data);
  } else {
    blink::WebNavigationBodyLoader::FillNavigationParamsResponseAndBodyLoader(
        std::move(common_params), std::move(commit_params), request_id,
        response_head.Clone(), std::move(response_body),
        std::move(url_loader_client_endpoints),
        GetTaskRunner(blink::TaskType::kInternalLoading),
        CreateResourceLoadInfoNotifierWrapper(), !frame_->Parent(),
        navigation_params.get(), frame_->IsAdFrame());
  }

  // The MHTML mime type should be same as the one we check in the browser
  // process's download_utils::MustDownload.
  bool is_mhtml_archive = base::EqualsCaseInsensitiveASCII(
                              response_head->mime_type, "multipart/related") ||
                          base::EqualsCaseInsensitiveASCII(
                              response_head->mime_type, "message/rfc822");
  if (is_mhtml_archive && navigation_params->body_loader) {
    // Load full mhtml archive before committing navigation.
    // We need this to retrieve the document mime type prior to committing.
    mhtml_body_loader_client_ =
        std::make_unique<RenderFrameImpl::MHTMLBodyLoaderClient>(
            this, std::move(navigation_params), std::move(commit_with_params));
    // The navigation didn't really commit, but lie about it anyway. Why? MHTML
    // is a bit special: the renderer process is responsible for parsing the
    // archive, but at this point, the response body isn't fully loaded yet.
    // Instead, MHTMLBodyLoaderClient will read the entire response body and
    // parse the archive to extract the main resource to be committed.
    //
    // There are two possibilities from this point:
    // - |MHTMLBodyLoaderClient::BodyLoadingFinished()| is called. At that
    //   point, the main resource can be extracted, and the navigation will be
    //   synchronously committed. If |this| is a provisional frame, it will be
    //   swapped in and committed. A separate |AssertNavigationCommits| is
    //   instantiated in |MHTMLBodyLoaderClient::BodyLoadingFinished()| to
    //   assert the commit actually happens. ✔️
    // - Alternatively, the pending archive load may be cancelled. This can only
    //   happen if the renderer initiates a new navigation, or if the browser
    //   requests that this frame commit a different navigation.
    //   - If |this| is already swapped in, the reason for cancelling the
    //     pending archive load does not matter. There will be no state skew
    //     between the browser and the renderer, since |this| has already
    //     committed a navigation. ✔️
    //   - If |this| is provisional, the pending archive load may only be
    //     cancelled by the browser requesting |this| to commit a different
    //     navigation. AssertNavigationCommits ensures the new request ends up
    //     committing and swapping in |this|, so this is OK. ✔️
    navigation_commit_state_ = NavigationCommitState::kDidCommit;
    return;
  }

  // Common case - fill navigation params from provided information and commit.
  std::move(commit_with_params).Run(std::move(navigation_params));

  if (is_new_navigation_in_outermost_main_frame_with_http_or_https) {
    base::UmaHistogramTimes(
        base::StrCat({kCommitRenderFrame,
                      ".OutermostMainFrame.NewNavigation.IsHTTPOrHTTPS"}),
        timer.Elapsed());
  }
}

void RenderFrameImpl::CommitNavigationWithParams(
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories,
    std::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
        subresource_overrides,
    blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info,
    blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        subresource_proxying_loader_factory,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        keep_alive_loader_factory,
    mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
        fetch_later_loader_factory,
    mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host,
    mojo::PendingRemote<blink::mojom::CodeCacheHost>
        code_cache_host_for_background,
    mojom::CookieManagerInfoPtr cookie_manager_info,
    mojom::StorageInfoPtr storage_info,
    std::unique_ptr<DocumentState> document_state,
    std::unique_ptr<WebNavigationParams> navigation_params) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "RenderFrameImpl::CommitNavigationWithParams",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  base::ElapsedTimer timer;
  if (common_params->url.IsAboutSrcdoc()) {
    WebNavigationParams::FillStaticResponse(navigation_params.get(),
                                            "text/html", "UTF-8",
                                            commit_params->srcdoc_value);
  }

  scoped_refptr<blink::ChildURLLoaderFactoryBundle> new_loader_factories =
      CreateLoaderFactoryBundle(std::move(subresource_loader_factories),
                                std::move(subresource_overrides),
                                std::move(subresource_proxying_loader_factory),
                                std::move(keep_alive_loader_factory),
                                std::move(fetch_later_loader_factory));

  DCHECK(new_loader_factories);
  DCHECK(new_loader_factories->HasBoundDefaultFactory());

  // If the navigation is for "view source", the WebLocalFrame needs to be put
  // in a special mode.
  if (commit_params->is_view_source)
    frame_->EnableViewSourceMode(true);

  if (frame_->IsOutermostMainFrame()) {
    // Save the Back/Forward Cache NotRestoredReasons struct to WebLocalFrame to
    // report for PerformanceNavigationTiming API.
    frame_->SetNotRestoredReasons(
        std::move(commit_params->not_restored_reasons));
  } else {
    // NotRestoredReasons are only set for the outermost main frame.
    CHECK(!commit_params->not_restored_reasons);
  }

  // |lcpp_hint| is set only when the frame is eligible (e.g. it's an outer
  // most main frame), which is checked in the browser process. Otherwise
  // nullptr.
  //
  // When there's a pre-existing LCPP hint on frame, we want to remove the
  // existing hint. Hence calling SetLCPPHint() is always required.
  CHECK(!commit_params->lcpp_hint || frame_->IsOutermostMainFrame());
  frame_->SetLCPPHint(std::move(commit_params->lcpp_hint));

  // Note: this intentionally does not call |Detach()| before |reset()|. If
  // there is an active |MHTMLBodyLoaderClient|, the browser-side navigation
  // code is explicitly replacing it with a new navigation commit request.
  // The check for |kWillCommit| in |~MHTMLBodyLoaderClient| covers this case.
  mhtml_body_loader_client_.reset();

  PrepareFrameForCommit(common_params->url, *commit_params);

  blink::WebFrameLoadType load_type =
      NavigationTypeToLoadType(common_params->navigation_type,
                               common_params->should_replace_current_entry);

  WebHistoryItem item_for_history_navigation;
  blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;

  if (load_type == WebFrameLoadType::kBackForward ||
      load_type == WebFrameLoadType::kRestore) {
    // We must know the nav entry ID of the page we are navigating back to,
    // which should be the case because history navigations are routed via the
    // browser.
    DCHECK_NE(0, commit_params->nav_entry_id);

    // Check that the history navigation can commit.
    commit_status = PrepareForHistoryNavigationCommit(
        *common_params, *commit_params, &item_for_history_navigation,
        &load_type);
  }

  if (commit_status != blink::mojom::CommitResult::Ok) {
    // The browser expects the frame to be loading this navigation. Inform it
    // that the load stopped if needed.
    if (frame_ && !frame_->IsLoading())
      GetFrameHost()->DidStopLoading();
    return;
  }

  navigation_params->frame_load_type = load_type;
  navigation_params->history_item = item_for_history_navigation;

  navigation_params->load_with_storage_access =
      commit_params->load_with_storage_access;
  navigation_params->visited_link_salt = commit_params->visited_link_salt;

  if (!container_info) {
    // An empty network provider will always be created since it is expected in
    // a certain number of places.
    navigation_params->service_worker_network_provider =
        ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
  } else {
    navigation_params->service_worker_network_provider =
        ServiceWorkerNetworkProviderForFrame::Create(
            this, std::move(container_info),
            std::move(controller_service_worker_info),
            network::SharedURLLoaderFactory::Create(
                new_loader_factories->Clone()));
  }

  DCHECK(!pending_loader_factories_);
  pending_loader_factories_ = std::move(new_loader_factories);
  pending_code_cache_host_ = std::move(code_cache_host);
  pending_code_cache_host_for_background_ =
      std::move(code_cache_host_for_background);
  pending_cookie_manager_info_ = std::move(cookie_manager_info);
  pending_storage_info_ = std::move(storage_info);
  original_storage_key_ = navigation_params->storage_key;

  base::WeakPtr<RenderFrameImpl> weak_self = weak_factory_.GetWeakPtr();
  frame_->CommitNavigation(std::move(navigation_params),
                           std::move(document_state));
  // The commit can result in this frame being removed.
  if (!weak_self)
    return;

  if (commit_params->local_surface_id) {
    CHECK(frame_->FrameWidget())
        << "Only local roots should get a SurfaceID update";
    frame_->FrameWidget()->ApplyLocalSurfaceIdUpdate(
        *commit_params->local_surface_id);
  }

  if (load_type == WebFrameLoadType::kStandard &&
      common_params->url.SchemeIsHTTPOrHTTPS()) {
    base::UmaHistogramMicrosecondsTimes(
        "Navigation.CommitNavigationWithParams.Time.IsStandardLoadType."
        "IsHTTPOrHTTPS",
        timer.Elapsed());
  }

  ResetMembersUsedForDurationOfCommit();
}

void RenderFrameImpl::CommitFailedNavigation(
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    bool has_stale_copy_in_cache,
    int error_code,
    int extended_error_code,
    net::ResolveErrorInfo resolve_error_info,
    const std::optional<std::string>& error_page_content,
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories,
    const blink::DocumentToken& document_token,
    blink::mojom::PolicyContainerPtr policy_container,
    mojom::AlternativeErrorPageOverrideInfoPtr alternative_error_page_info,
    mojom::NavigationClient::CommitFailedNavigationCallback callback) {
  TRACE_EVENT1("navigation,benchmark,rail",
               "RenderFrameImpl::CommitFailedNavigation", "frame_token",
               frame_token_);
  DCHECK(navigation_client_impl_);
  DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
  // `origin_to_commit` must be set on failed navigations.
  CHECK(commit_params->origin_to_commit);

  // The browser process should not send us an initiator_base_url in a failed
  // navigation.
  DCHECK(!common_params->initiator_base_url);

  AssertNavigationCommits assert_navigation_commits(
      this, kMayReplaceInitialEmptyDocument);

  GetWebView()->SetHistoryListFromNavigation(
      commit_params->current_history_list_index,
      commit_params->current_history_list_length);

  // Note: this intentionally does not call |Detach()| before |reset()|. If
  // there is an active |MHTMLBodyLoaderClient|, the browser-side navigation
  // code is explicitly replacing it with a new navigation commit request.
  // The check for |kWillCommit| in |~MHTMLBodyLoaderClient| covers this case.
  mhtml_body_loader_client_.reset();

  GetContentClient()->SetActiveURL(
      common_params->url, frame_->Top()->GetSecurityOrigin().ToString().Utf8());

  // TODO(lukasza): https://crbug.com/936696: No need to postpone setting the
  // |new_loader_factories| once we start swapping RenderFrame^H^H^H
  // RenderDocument on every cross-document navigation.
  scoped_refptr<blink::ChildURLLoaderFactoryBundle> new_loader_factories =
      CreateLoaderFactoryBundle(
          std::move(subresource_loader_factories),
          std::nullopt /* subresource_overrides */,
          mojo::NullRemote() /* subresource_proxying_loader_factory */,
          mojo::NullRemote() /* keep_alive_loader_factory */,
          mojo::NullAssociatedRemote() /* fetch_later_loader_factory */);
  DCHECK(new_loader_factories->HasBoundDefaultFactory());

  // Send the provisional load failure.
  WebURLError error(
      error_code, extended_error_code, resolve_error_info,
      has_stale_copy_in_cache ? WebURLError::HasCopyInCache::kTrue
                              : WebURLError::HasCopyInCache::kFalse,
      WebURLError::IsWebSecurityViolation::kFalse, common_params->url,
      WebURLError::ShouldCollapseInitiator::kFalse);

  // Since the URL will be set to kUnreachableWebDataURL, use default content
  // settings.
  commit_params->content_settings =
      blink::CreateDefaultRendererContentSettings();
  auto navigation_params = std::make_unique<WebNavigationParams>(
      document_token,
      /*devtools_navigation_token=*/base::UnguessableToken::Create(),
      /*base_auction_nonce=*/base::Uuid::GenerateRandomV4());
  FillNavigationParamsRequest(*common_params, *commit_params,
                              navigation_params.get());
  // Use kUnreachableWebDataURL as the document URL (instead of the URL that
  // failed to load, which is saved separately as the "unreachable URL" below).
  navigation_params->url = GURL(kUnreachableWebDataURL);
  // FillNavigationParamsRequest() sets the |navigation_params->http_method| to
  // the original method of the request. In successful page loads,
  // |navigation_params->redirects| also gets populated and the redirects are
  // later replayed to update the method. However, in the case of an error page
  // load, the redirects are neither populated nor replayed. Hence |http_method|
  // needs to be manually set to the final method.
  navigation_params->http_method = WebString::FromASCII(common_params->method);
  navigation_params->error_code = error_code;

  // This is already checked in `NavigationRequest::OnRequestFailedInternal` and
  // `NavigationRequest::OnFailureChecksCompleted` on the browser side, so the
  // renderer should never see this.
  CHECK_NE(net::ERR_ABORTED, error_code);

  if (commit_params->nav_entry_id == 0) {
    // For renderer initiated navigations, we send out a
    // DidFailProvisionalLoad() notification.
    NotifyObserversOfFailedProvisionalLoad();
  }

  std::string error_html;
  std::string* error_html_ptr = &error_html;
  if (error_code == net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
    DCHECK_NE(commit_params->http_response_code, -1);
    GetContentClient()->renderer()->PrepareErrorPageForHttpStatusError(
        this, error, navigation_params->http_method.Ascii(),
        commit_params->http_response_code,
        std::move(alternative_error_page_info), error_html_ptr);
  } else {
    if (error_page_content) {
      error_html = error_page_content.value();
      error_html_ptr = nullptr;
    }
    // Prepare for the error page. Note that even if |error_html_ptr| is set to
    // null above, PrepareErrorPage might have other side effects e.g. setting
    // some error-related states, so we should still call it.
    GetContentClient()->renderer()->PrepareErrorPage(
        this, error, navigation_params->http_method.Ascii(),
        std::move(alternative_error_page_info), error_html_ptr);
  }

  // Make sure we never show errors in view source mode.
  frame_->EnableViewSourceMode(false);

  auto page_state =
      blink::PageState::CreateFromEncodedData(commit_params->page_state);
  if (page_state.IsValid())
    navigation_params->history_item = WebHistoryItem(page_state);
  if (!navigation_params->history_item.IsNull()) {
    if (common_params->navigation_type ==
            blink::mojom::NavigationType::RESTORE ||
        common_params->navigation_type ==
            blink::mojom::NavigationType::RESTORE_WITH_POST) {
      navigation_params->frame_load_type = WebFrameLoadType::kRestore;
    } else {
      navigation_params->frame_load_type = WebFrameLoadType::kBackForward;
    }
  } else if (common_params->should_replace_current_entry) {
    navigation_params->frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
  }

  navigation_params->service_worker_network_provider =
      ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
  FillMiscNavigationParams(*common_params, *commit_params,
                           navigation_params.get());
  WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
                                          "UTF-8", error_html);
  // Save the URL that failed to load as the "unreachable URL" so that the
  // we can use that (instead of kUnreachableWebDataURL) for the HistoryItem for
  // this navigation, and also to send back with the DidCommitProvisionalLoad
  // message to the browser.
  // TODO(crbug.com/40150370): Stop sending the URL back with DidCommit.
  navigation_params->unreachable_url = error.url();
  if (commit_params->redirects.size()) {
    navigation_params->pre_redirect_url_for_failed_navigations =
        commit_params->redirects[0];
  } else {
    navigation_params->pre_redirect_url_for_failed_navigations = error.url();
  }

  navigation_params->policy_container =
      ToWebPolicyContainer(std::move(policy_container));

  navigation_params->view_transition_state =
      std::move(commit_params->view_transition_state);

  // The error page load (not to confuse with a failed load of original page)
  // was not initiated through BeginNavigation, therefore
  // |was_initiated_in_this_frame| is false.
  std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
      *common_params, *commit_params, std::move(callback),
      std::move(navigation_client_impl_), blink::GenerateRequestId(),
      false /* was_initiated_in_this_frame */);

  DCHECK(!pending_loader_factories_);
  pending_loader_factories_ = std::move(new_loader_factories);

  // The load of the error page can result in this frame being removed.
  // Use a WeakPtr as an easy way to detect whether this has occurred. If so,
  // this method should return immediately and not touch any part of the object,
  // otherwise it will result in a use-after-free bug.
  base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
  frame_->CommitNavigation(std::move(navigation_params),
                           std::move(document_state));
  if (!weak_this)
    return;

  ResetMembersUsedForDurationOfCommit();
}

void RenderFrameImpl::CommitSameDocumentNavigation(
    blink::mojom::CommonNavigationParamsPtr common_params,
    blink::mojom::CommitNavigationParamsPtr commit_params,
    CommitSameDocumentNavigationCallback callback) {
  DCHECK(!blink::IsRendererDebugURL(common_params->url));
  DCHECK(!NavigationTypeUtils::IsReload(common_params->navigation_type));
  DCHECK(!commit_params->is_view_source);
  DCHECK(NavigationTypeUtils::IsSameDocument(common_params->navigation_type));

  CHECK(in_frame_tree_);
  // Unlike a cross-document navigation commit, detach the MHTMLBodyLoaderClient
  // before resetting it. In the case of a cross-document navigation, it's
  // important to ensure *something* commits, even if the original commit
  // request was replaced by a commit request. However, in the case of a
  // same-document navigation commit request, |this| must already be committed.
  //
  // Note that this means a same-document navigation might cancel a
  // cross-document navigation, which is a bit strange. In the future, explore
  // the idea of allowing the cross-document navigation to continue.
  if (mhtml_body_loader_client_) {
    mhtml_body_loader_client_->Detach();
    mhtml_body_loader_client_.reset();
  }

  PrepareFrameForCommit(common_params->url, *commit_params);

  blink::WebFrameLoadType load_type =
      NavigationTypeToLoadType(common_params->navigation_type,
                               common_params->should_replace_current_entry);

  DocumentState* document_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
  document_state->set_navigation_state(
      NavigationState::CreateForSameDocumentCommitFromBrowser(
          std::move(common_params), std::move(commit_params),
          std::move(callback)));
  NavigationState* navigation_state = document_state->navigation_state();

  blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;
  WebHistoryItem item_for_history_navigation;

  if (navigation_state->common_params().navigation_type ==
      blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT) {
    DCHECK(blink::PageState::CreateFromEncodedData(
               navigation_state->commit_params().page_state)
               .IsValid());
    // We must know the nav entry ID of the page we are navigating back to,
    // which should be the case because history navigations are routed via the
    // browser.
    DCHECK_NE(0, navigation_state->commit_params().nav_entry_id);
    DCHECK(!navigation_state->common_params()
                .is_history_navigation_in_new_child_frame);
    commit_status = PrepareForHistoryNavigationCommit(
        navigation_state->common_params(), navigation_state->commit_params(),
        &item_for_history_navigation, &load_type);
  }

  if (commit_status == blink::mojom::CommitResult::Ok) {
    base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
    // Same-document navigations on data URLs loaded with a valid base URL
    // should keep the base URL as document URL.
    bool use_base_url_for_data_url =
        !navigation_state->common_params().base_url_for_data_url.is_empty();
#if BUILDFLAG(IS_ANDROID)
    use_base_url_for_data_url |=
        !navigation_state->commit_params().data_url_as_string.empty();
#endif

    GURL url;
    if (is_main_frame_ && use_base_url_for_data_url) {
      url = navigation_state->common_params().base_url_for_data_url;
    } else {
      url = navigation_state->common_params().url;
    }
    bool is_client_redirect = !!(navigation_state->common_params().transition &
                                 ui::PAGE_TRANSITION_CLIENT_REDIRECT);
    bool started_with_transient_activation =
        navigation_state->common_params().has_user_gesture;
    bool is_browser_initiated =
        navigation_state->commit_params().is_browser_initiated;
    bool has_ua_visual_transition =
        navigation_state->commit_params().has_ua_visual_transition;
    std::optional<blink::scheduler::TaskAttributionId>
        soft_navigation_heuristics_task_id =
            navigation_state->commit_params()
                .soft_navigation_heuristics_task_id;

    WebSecurityOrigin initiator_origin;
    if (navigation_state->common_params().initiator_origin) {
      initiator_origin =
          navigation_state->common_params().initiator_origin.value();
    }
    bool should_skip_screenshot =
        navigation_state->commit_params().should_skip_screenshot;

    // Load the request.
    commit_status = frame_->CommitSameDocumentNavigation(
        url, load_type, item_for_history_navigation, is_client_redirect,
        started_with_transient_activation, initiator_origin,
        is_browser_initiated, has_ua_visual_transition,
        soft_navigation_heuristics_task_id, should_skip_screenshot);

    // If `commit_status` is Ok, RunCommitSameDocumentNavigationCallback() was
    // called in DidCommitNavigationInternal() or the NavigationApi deferred the
    // commit and will call DidCommitNavigationInternal() when the commit is
    // undeferred. Either way, no further work is needed here.
    if (commit_status == blink::mojom::CommitResult::Ok) {
      return;
    }

    // The load of the URL can result in this frame being removed. Use a
    // WeakPtr as an easy way to detect whether this has occurred. If so, this
    // method should return immediately and not touch any part of the object,
    // otherwise it will result in a use-after-free bug.
    // Similarly, check whether `navigation_state` is still the state associated
    // with the WebDocumentLoader. It may have been preempted by a navigation
    // started by an event handler.
    if (!weak_this || document_state->navigation_state() != navigation_state) {
      return;
    }
  }

  DCHECK_NE(commit_status, blink::mojom::CommitResult::Ok);
  navigation_state->RunCommitSameDocumentNavigationCallback(commit_status);
  document_state->clear_navigation_state();

  // The browser expects the frame to be loading this navigation. Inform it
  // that the load stopped if needed.
  if (frame_ && !frame_->IsLoading()) {
    GetFrameHost()->DidStopLoading();
  }
}

void RenderFrameImpl::UpdateSubresourceLoaderFactories(
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
        subresource_loader_factories) {
  if (loader_factories_->IsHostChildURLLoaderFactoryBundle()) {
    static_cast<blink::HostChildURLLoaderFactoryBundle*>(
        loader_factories_.get())
        ->UpdateThisAndAllClones(std::move(subresource_loader_factories));
  } else {
    // RFHI::recreate_default_url_loader_factory_after_network_service_crash_ is
    // only set for frames that directly get their factory bundle from the
    // browser (HostChildURLLoaderFactoryBundle) rather than inheriting the
    // bundle from another frame (TrackedChildURLLoaderFactoryBundle).
    // Therefore the default factory *may* be present in the `if`/true branch
    // and *must* be missing in the `else` branch.
    DCHECK(!subresource_loader_factories->pending_default_factory().is_valid());

    // If there is no `pending_default_factory` (see the DCHECK above) and no
    // empty-payload IPCs are sent, then the only way to get here should be
    // because of `pending_isolated_world_factories`.
    //
    // TODO(crbug.com/40158699): Remove the whole `else` branch once
    // Chrome Platform Apps and `pending_isolated_world_factories` are gone.
    DCHECK(!subresource_loader_factories->pending_isolated_world_factories()
                .empty());

    // `!IsHostChildURLLoaderFactoryBundle` should only happen if the frame
    // hosts a document that isn't related to a real navigation (i.e. if an
    // initial empty document should "inherit" the factories from its
    // opener/parent).
    DCHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
              navigation_commit_state_);

    auto partial_bundle =
        base::MakeRefCounted<blink::ChildURLLoaderFactoryBundle>();
    static_cast<blink::URLLoaderFactoryBundle*>(partial_bundle.get())
        ->Update(std::move(subresource_loader_factories));
    loader_factories_->Update(partial_bundle->PassInterface());
  }

  // Resetting `background_resource_fetch_context_` here so it will be recreated
  // when MaybeGetBackgroundResourceFetchAssets() is called.
  background_resource_fetch_context_.reset();
}

// content::RenderFrame implementation
// ----------------------------------------
const blink::BrowserInterfaceBrokerProxy&
RenderFrameImpl::GetBrowserInterfaceBroker() {
  return frame_->GetBrowserInterfaceBroker();
}

bool RenderFrameImpl::IsPluginHandledExternally(
    const blink::WebElement& plugin_element,
    const blink::WebURL& url,
    const blink::WebString& suggested_mime_type) {
#if BUILDFLAG(ENABLE_PLUGINS)
  return GetContentClient()->renderer()->IsPluginHandledExternally(
      this, plugin_element, GURL(url), suggested_mime_type.Utf8());
#else
  return false;
#endif
}

bool RenderFrameImpl::IsDomStorageDisabled() const {
  return GetContentClient()->renderer()->IsDomStorageDisabled();
}

v8::Local<v8::Object> RenderFrameImpl::GetScriptableObject(
    const blink::WebElement& plugin_element,
    v8::Isolate* isolate) {
#if BUILDFLAG(ENABLE_PLUGINS)

  return GetContentClient()->renderer()->GetScriptableObject(plugin_element,
                                                             isolate);
#else
  return v8::Local<v8::Object>();
#endif
}

void RenderFrameImpl::BindToFrame(blink::WebNavigationControl* frame) {
  DCHECK(!frame_);

  std::pair<FrameMap::iterator, bool> result =
      g_frame_map.Get().emplace(frame, this);
  CHECK(result.second) << "Inserting a duplicate item.";

  frame_ = frame;
}

blink::WebPlugin* RenderFrameImpl::CreatePlugin(
    const blink::WebPluginParams& params) {
  blink::WebPlugin* plugin = nullptr;
  if (GetContentClient()->renderer()->OverrideCreatePlugin(this, params,
                                                           &plugin)) {
    return plugin;
  }

#if BUILDFLAG(ENABLE_PPAPI)
  WebPluginInfo info;
  std::string mime_type;
  bool found = false;
  GetPepperHost()->GetPluginInfo(params.url, params.mime_type.Utf8(), &found,
                                 &info, &mime_type);
  if (!found)
    return nullptr;

  WebPluginParams params_to_use = params;
  params_to_use.mime_type = WebString::FromUTF8(mime_type);
  return CreatePlugin(info, params_to_use);
#else
  return nullptr;
#endif  // BUILDFLAG(ENABLE_PPAPI)
}

std::unique_ptr<blink::WebMediaPlayer> RenderFrameImpl::CreateMediaPlayer(
    const blink::WebMediaPlayerSource& source,
    WebMediaPlayerClient* client,
    blink::MediaInspectorContext* inspector_context,
    WebMediaPlayerEncryptedMediaClient* encrypted_client,
    WebContentDecryptionModule* initial_cdm,
    const blink::WebString& sink_id,
    const cc::LayerTreeSettings* settings,
    scoped_refptr<base::TaskRunner> compositor_worker_task_runner) {
  // `settings` should be non-null since the WebView created for
  // RenderFrameImpl always composites.
  DCHECK(settings);
  return media_factory_.CreateMediaPlayer(
      source, client, inspector_context, encrypted_client, initial_cdm, sink_id,
      GetLocalRootWebFrameWidget()->GetFrameSinkId(), *settings,
      agent_scheduling_group_->agent_group_scheduler().CompositorTaskRunner(),
      std::move(compositor_worker_task_runner));
}

std::unique_ptr<blink::WebContentSettingsClient>
RenderFrameImpl::CreateWorkerContentSettingsClient() {
  if (!frame_ || !frame_->View())
    return nullptr;
  return GetContentClient()->renderer()->CreateWorkerContentSettingsClient(
      this);
}

#if !BUILDFLAG(IS_ANDROID)
std::unique_ptr<media::SpeechRecognitionClient>
RenderFrameImpl::CreateSpeechRecognitionClient() {
  if (!frame_ || !frame_->View())
    return nullptr;
  return GetContentClient()->renderer()->CreateSpeechRecognitionClient(this);
}
#endif

scoped_refptr<blink::WebWorkerFetchContext>
RenderFrameImpl::CreateWorkerFetchContext() {
  ServiceWorkerNetworkProviderForFrame* provider =
      static_cast<ServiceWorkerNetworkProviderForFrame*>(
          frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider());
  DCHECK(provider);

  mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
      watcher_receiver;
  GetWebView()->RegisterRendererPreferenceWatcher(
      watcher_receiver.InitWithNewPipeAndPassRemote());

  mojo::PendingRemote<blink::mojom::ResourceLoadInfoNotifier>
      pending_resource_load_info_notifier;
  resource_load_info_notifier_receivers_.Add(
      this,
      pending_resource_load_info_notifier.InitWithNewPipeAndPassReceiver(),
      agent_scheduling_group_->agent_group_scheduler().DefaultTaskRunner());

  std::vector<std::string> cors_exempt_header_list =
      RenderThreadImpl::current()->cors_exempt_header_list();
  std::vector<blink::WebString> web_cors_exempt_header_list(
      cors_exempt_header_list.size());
  std::ranges::transform(
      cors_exempt_header_list, web_cors_exempt_header_list.begin(),
      [](const auto& header) { return blink::WebString::FromLatin1(header); });

  // |pending_subresource_loader_updater| and
  // |pending_resource_load_info_notifier| are not used for
  // non-PlzDedicatedWorker and worklets.
  scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext>
      web_dedicated_or_shared_worker_fetch_context =
          blink::WebDedicatedOrSharedWorkerFetchContext::Create(
              provider->context(), GetWebView()->GetRendererPreferences(),
              std::move(watcher_receiver), GetLoaderFactoryBundle()->Clone(),
              GetLoaderFactoryBundle()->Clone(),
              /*pending_subresource_loader_updater=*/mojo::NullReceiver(),
              web_cors_exempt_header_list,
              std::move(pending_resource_load_info_notifier));

  web_dedicated_or_shared_worker_fetch_context->SetAncestorFrameToken(
      frame_->GetLocalFrameToken());
  web_dedicated_or_shared_worker_fetch_context->set_site_for_cookies(
      frame_->GetDocument().SiteForCookies());
  web_dedicated_or_shared_worker_fetch_context->set_top_frame_origin(
      frame_->GetDocument().TopFrameOrigin());

  for (auto& observer : observers_) {
    observer.WillCreateWorkerFetchContext(
        web_dedicated_or_shared_worker_fetch_context.get());
  }
  return web_dedicated_or_shared_worker_fetch_context;
}

scoped_refptr<blink::WebWorkerFetchContext>
RenderFrameImpl::CreateWorkerFetchContextForPlzDedicatedWorker(
    blink::WebDedicatedWorkerHostFactoryClient* factory_client) {
  DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker));
  DCHECK(factory_client);

  mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
      watcher_receiver;
  GetWebView()->RegisterRendererPreferenceWatcher(
      watcher_receiver.InitWithNewPipeAndPassRemote());

  mojo::PendingRemote<blink::mojom::ResourceLoadInfoNotifier>
      pending_resource_load_info_notifier;
  resource_load_info_notifier_receivers_.Add(
      this,
      pending_resource_load_info_notifier.InitWithNewPipeAndPassReceiver(),
      agent_scheduling_group_->agent_group_scheduler().DefaultTaskRunner());

  scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext>
      web_dedicated_or_shared_worker_fetch_context =
          static_cast<DedicatedWorkerHostFactoryClient*>(factory_client)
              ->CreateWorkerFetchContext(
                  GetWebView()->GetRendererPreferences(),
                  std::move(watcher_receiver),
                  std::move(pending_resource_load_info_notifier));

  web_dedicated_or_shared_worker_fetch_context->SetAncestorFrameToken(
      frame_->GetLocalFrameToken());
  web_dedicated_or_shared_worker_fetch_context->set_site_for_cookies(
      frame_->GetDocument().SiteForCookies());
  web_dedicated_or_shared_worker_fetch_context->set_top_frame_origin(
      frame_->GetDocument().TopFrameOrigin());

  for (auto& observer : observers_) {
    observer.WillCreateWorkerFetchContext(
        web_dedicated_or_shared_worker_fetch_context.get());
  }
  return web_dedicated_or_shared_worker_fetch_context;
}

std::unique_ptr<blink::WebPrescientNetworking>
RenderFrameImpl::CreatePrescientNetworking() {
  return GetContentClient()->renderer()->CreatePrescientNetworking(this);
}

std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
RenderFrameImpl::CreateResourceLoadInfoNotifierWrapper() {
  return std::make_unique<blink::ResourceLoadInfoNotifierWrapper>(
      weak_wrapper_resource_load_info_notifier_->AsWeakPtr(),
      GetTaskRunner(blink::TaskType::kNetworking));
}

std::unique_ptr<blink::WebServiceWorkerProvider>
RenderFrameImpl::CreateServiceWorkerProvider() {
  // Bail-out if we are about to be navigated away.
  // We check that DocumentLoader is attached since:
  // - This serves as the signal since the DocumentLoader is detached in
  //   FrameLoader::PrepareForCommit().
  // - Creating ServiceWorkerProvider in
  //   RenderFrameImpl::CreateServiceWorkerProvider() assumes that there is a
  //   DocumentLoader attached to the frame.
  if (!frame_->GetDocumentLoader())
    return nullptr;

  // At this point we should have non-null data source.
  if (!ChildThreadImpl::current())
    return nullptr;  // May be null in some tests.
  ServiceWorkerNetworkProviderForFrame* provider =
      static_cast<ServiceWorkerNetworkProviderForFrame*>(
          frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider());
  if (!provider->context()) {
    // The context can be null when the frame is sandboxed.
    return nullptr;
  }
  return std::make_unique<WebServiceWorkerProviderImpl>(provider->context());
}

blink::AssociatedInterfaceProvider*
RenderFrameImpl::GetRemoteNavigationAssociatedInterfaces() {
  return GetRemoteAssociatedInterfaces();
}

namespace {

// Emit the trace event using a helper as we:
// a) want to ensure that the trace event covers the entire function.
// b) we want to emit the new child routing id as an argument.
// c) child routing id becomes available only after a sync call.
struct CreateChildFrameTraceEvent {
  explicit CreateChildFrameTraceEvent(
      const blink::LocalFrameToken& frame_token) {
    TRACE_EVENT_BEGIN("navigation,rail", "RenderFrameImpl::createChildFrame",
                      "frame_token", frame_token);
  }

  ~CreateChildFrameTraceEvent() {
    TRACE_EVENT_END("navigation,rail", "child_frame_token", child_frame_token);
  }

  blink::LocalFrameToken child_frame_token;
};

}  // namespace

blink::WebLocalFrame* RenderFrameImpl::CreateChildFrame(
    blink::mojom::TreeScopeType scope,
    const blink::WebString& name,
    const blink::WebString& fallback_name,
    const blink::FramePolicy& frame_policy,
    const blink::WebFrameOwnerProperties& frame_owner_properties,
    blink::FrameOwnerElementType frame_owner_element_type,
    blink::WebPolicyContainerBindParams policy_container_bind_params,
    ukm::SourceId document_ukm_source_id,
    FinishChildFrameCreationFn finish_creation) {
  // Tracing analysis uses this to find main frames when this value is
  // MSG_ROUTING_NONE, and build the frame tree otherwise.
  CreateChildFrameTraceEvent trace_event(frame_token_);

  // Allocate child routing ID. This is a synchronous call.
  int child_routing_id;
  blink::LocalFrameToken frame_token;
  base::UnguessableToken devtools_frame_token;
  blink::DocumentToken document_token;
  if (!RenderThread::Get()->GenerateFrameRoutingID(
          child_routing_id, frame_token, devtools_frame_token,
          document_token)) {
    return nullptr;
  }
  trace_event.child_frame_token = frame_token;

  // The unique name generation logic was moved out of Blink, so for historical
  // reasons, unique name generation needs to take something called the
  // |fallback_name| into account. Normally, unique names are generated based on
  // the browsing context name. For new frames, the initial browsing context
  // name comes from the name attribute of the browsing context container
  // element.
  //
  // However, when the browsing context name is null, Blink instead uses the
  // "fallback name" to derive the unique name. The exact contents of the
  // "fallback name" are unspecified, but may contain the value of the
  // 'subresource attribute' of the browsing context container element.
  //
  // Note that Blink can't be changed to just pass |fallback_name| as |name| in
  // the case |name| is empty: |fallback_name| should never affect the actual
  // browsing context name, only unique name generation.
  bool is_created_by_script = GetAgentGroupScheduler().Isolate()->InContext();
  std::string frame_unique_name =
      unique_name_helper_.GenerateNameForNewChildFrame(
          name.IsEmpty() ? fallback_name.Utf8() : name.Utf8(),
          is_created_by_script);

  mojo::PendingAssociatedReceiver<mojom::Frame> pending_frame_receiver;

  mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
      browser_interface_broker;

  mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
      associated_interface_provider;

  // Now create the child frame in the browser via an asynchronous call.
  GetFrameHost()->CreateChildFrame(
      frame_token, pending_frame_receiver.InitWithNewEndpointAndPassRemote(),
      browser_interface_broker.InitWithNewPipeAndPassReceiver(),
      blink::mojom::PolicyContainerBindParams::New(
          std::move(policy_container_bind_params.receiver)),
      associated_interface_provider.InitWithNewEndpointAndPassReceiver(), scope,
      name.Utf8(), frame_unique_name, is_created_by_script, frame_policy,
      blink::mojom::FrameOwnerProperties::From(frame_owner_properties),
      frame_owner_element_type, document_ukm_source_id);

  // Create the RenderFrame and WebLocalFrame, linking the two.
  RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
      *agent_scheduling_group_, frame_token, child_routing_id,
      std::move(pending_frame_receiver),
      std::move(associated_interface_provider), devtools_frame_token,
      /*is_for_nested_main_frame=*/false);
  child_render_frame->SetLoaderFactoryBundle(CloneLoaderFactories());
  child_render_frame->unique_name_helper_.set_propagated_name(
      frame_unique_name);
  if (is_created_by_script)
    child_render_frame->unique_name_helper_.Freeze();
  blink::WebLocalFrame* web_frame = frame_->CreateLocalChild(
      scope, child_render_frame,
      child_render_frame->blink_interface_registry_.get(), frame_token);
  finish_creation(web_frame, document_token,
                  std::move(browser_interface_broker));

  child_render_frame->in_frame_tree_ = true;
  child_render_frame->Initialize(/*parent=*/GetWebFrame());

  return web_frame;
}

void RenderFrameImpl::DidCreateFencedFrame(
    const blink::RemoteFrameToken& frame_token) {
  for (auto& observer : observers_)
    observer.DidCreateFencedFrame(frame_token);
}

blink::WebFrame* RenderFrameImpl::FindFrame(const blink::WebString& name) {
  if (GetBlinkPreferences().renderer_wide_named_frame_lookup) {
    for (const auto& it : g_frame_map.Get()) {
      WebLocalFrame* frame = it.second->GetWebFrame();
      if (frame->AssignedName() == name)
        return frame;
    }
  }

  return GetContentClient()->renderer()->FindFrame(this->GetWebFrame(),
                                                   name.Utf8());
}

void RenderFrameImpl::MaybeInitializeWidget(
    mojom::CreateFrameWidgetParamsPtr widget_params) {
  if (widget_params->reuse_compositor) {
    // Initializing the widget is deferred until commit if this RenderFrame
    // will be replacing a previous RenderFrame. This enables reuse of the
    // compositing setup which is expensive.
    // This step must be deferred until commit since this RenderFrame could be
    // speculative and the previous RenderFrame will continue to be visible
    // and animating until commit.
    //
    // TODO(khushalsagar): Ideal would be to move the widget initialization to
    // the commit stage for all cases. This shouldn't have any perf impact
    // since the expensive parts of compositing (setting up a connection to
    // the GPU process) is not done until the frame is made visible, which
    // happens at commit.
    widget_params_for_lazy_widget_creation_ = std::move(widget_params);
    return;
  }

  InitializeFrameWidgetForFrame(*frame_, /*previous_widget=*/nullptr,
                                std::move(widget_params),
                                is_for_nested_main_frame_);
}

void RenderFrameImpl::InitializeWidgetAtSwap(
    blink::WebLocalFrame& frame_for_compositor_reuse) {
  CHECK(widget_params_for_lazy_widget_creation_);
  DCHECK(widget_params_for_lazy_widget_creation_->reuse_compositor);

  InitializeFrameWidgetForFrame(
      *frame_, frame_for_compositor_reuse.FrameWidget(),
      std::move(widget_params_for_lazy_widget_creation_),
      is_for_nested_main_frame_);
}

void RenderFrameImpl::WillDetach(blink::DetachReason detach_reason) {
  if (detach_reason == blink::DetachReason::kNavigation) {
    if (navigation_client_impl_ &&
        ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
      navigation_client_impl_->ResetWithoutCancelling();
    }

    // If `provisional_frame_for_local_root_swap_` is set by `Swap()`, the
    // widget for the frame being swapped in needs to be initialized now. At
    // this point, the compositor can be passed off safely since the `Document`
    // has already been torn down.
    //
    // TODO(dcheng): This mechanism could probably be used for passing off the
    // unique name as well...
    if (provisional_frame_for_local_root_swap_) {
      CHECK_EQ(
          provisional_frame_for_local_root_swap_->is_for_nested_main_frame_,
          is_for_nested_main_frame_);
      provisional_frame_for_local_root_swap_->InitializeWidgetAtSwap(
          CHECK_DEREF(GetWebFrame()));
      provisional_frame_for_local_root_swap_ = nullptr;
    }
  }

  for (auto& observer : observers_)
    observer.WillDetach(detach_reason);

  // blink::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
  auto& factory = blink::AudioOutputIPCFactory::GetInstance();
  if (factory.io_task_runner())
    factory.MaybeDeregisterRemoteFactory(GetWebFrame()->GetLocalFrameToken());

  // Send a state update before the frame is detached.
  SendUpdateState();
}

void RenderFrameImpl::FrameDetached(blink::DetachReason detach_reason) {
  TRACE_EVENT0("navigation", "RenderFrameImpl::FrameDetached");
  base::ScopedUmaHistogramTimer histogram_timer(
      "Navigation.RenderFrameImpl.FrameDetached");
  // We need to clean up subframes by removing them from the map and deleting
  // the RenderFrameImpl.  In contrast, the main frame is owned by its
  // containing RenderViewHost (so that they have the same lifetime), so only
  // removal from the map is needed and no deletion.
  auto it = g_frame_map.Get().find(frame_);
  CHECK(it != g_frame_map.Get().end());
  CHECK_EQ(it->second, this);
  g_frame_map.Get().erase(it);

  // RenderAccessibilityManager keeps a reference to the RenderFrame that owns
  // it, so we need to clear the pointer to prevent invalid access after the
  // frame gets closed and deleted.
  render_accessibility_manager_.reset();

  // |frame_| may not be referenced after this, so clear the pointer since
  // the actual WebLocalFrame may not be deleted immediately and other methods
  // may try to access it.
  frame_->Close(detach_reason);
  frame_ = nullptr;

  if (mhtml_body_loader_client_) {
    mhtml_body_loader_client_->Detach();
    mhtml_body_loader_client_.reset();
  }

  delete this;
  // Object is invalid after this point.
}

void RenderFrameImpl::DidChangeName(const blink::WebString& name) {
  if (GetWebFrame()->GetCurrentHistoryItem().IsNull()) {
    // Once a navigation has committed, the unique name must no longer change to
    // avoid breaking back/forward navigations: https://crbug.com/607205
    unique_name_helper_.UpdateName(name.Utf8());
  }
  GetFrameHost()->DidChangeName(name.Utf8(), unique_name_helper_.value());
}

void RenderFrameImpl::DidMatchCSS(
    const std::vector<blink::WebString>& newly_matching_selectors,
    const std::vector<blink::WebString>& stopped_matching_selectors) {
  for (auto& observer : observers_)
    observer.DidMatchCSS(newly_matching_selectors, stopped_matching_selectors);
}

bool RenderFrameImpl::ShouldReportDetailedMessageForSourceAndSeverity(
    blink::mojom::ConsoleMessageLevel log_level,
    const blink::WebString& source) {
  if (want_error_message_stack_trace_ &&
      log_level == blink::mojom::ConsoleMessageLevel::kError) {
    return true;
  }
  return GetContentClient()->renderer()->ShouldReportDetailedMessageForSource(
      source.Utf16());
}

void RenderFrameImpl::DidAddMessageToConsole(
    const blink::WebConsoleMessage& message,
    const blink::WebString& source_name,
    unsigned source_line,
    const blink::WebString& stack_trace) {
  if (ShouldReportDetailedMessageForSourceAndSeverity(message.level,
                                                      source_name)) {
    for (auto& observer : observers_) {
      observer.DetailedConsoleMessageAdded(
          message.text.Utf16(), source_name.Utf16(), stack_trace.Utf16(),
          source_line, message.level);
    }
  }
}

void RenderFrameImpl::DidCreateDocumentLoader(
    blink::WebDocumentLoader* document_loader) {
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(document_loader);
  if (!document_state) {
    // This must be an initial empty document.
    document_loader->SetExtraData(BuildDocumentState());
    document_loader->SetServiceWorkerNetworkProvider(
        ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance());
  }

  // Set the code cache host earlier to allow fetching the code cache as soon as
  // possible.
  document_loader->SetCodeCacheHost(
      std::move(pending_code_cache_host_),
      std::move(pending_code_cache_host_for_background_));
}

void RenderFrameImpl::DidCommitNavigation(
    blink::WebHistoryCommitType commit_type,
    bool should_reset_browser_interface_broker,
    const network::ParsedPermissionsPolicy& permissions_policy_header,
    const blink::DocumentPolicyFeatureState& document_policy_header) {
  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::DidCommitNavigation",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  CHECK_EQ(NavigationCommitState::kWillCommit, navigation_commit_state_);
  navigation_commit_state_ = NavigationCommitState::kDidCommit;

  WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(document_loader);
  NavigationState* navigation_state = document_state->navigation_state();
  DCHECK(!navigation_state->WasWithinSameDocument());

  TRACE_EVENT2("navigation,benchmark,rail",
               "RenderFrameImpl::didStartProvisionalLoad", "frame_token",
               frame_token_, "url",
               document_loader->GetUrl().GetString().Utf8());

  // Install factories as early as possible - it needs to happen before the
  // newly committed document starts any subresource fetches.  In particular,
  // this needs to happen before invoking
  // RenderFrameObserver::ReadyToCommitNavigation below.
  if (pending_loader_factories_) {
    // Commits triggered by the browser process should always provide
    // |pending_loader_factories_|.
    SetLoaderFactoryBundle(std::move(pending_loader_factories_));
  }
  DCHECK(loader_factories_);
  DCHECK(loader_factories_->HasBoundDefaultFactory());

  // TODO(dgozman): call DidStartNavigation in various places where we call
  // CommitNavigation() on the frame.
  if (!navigation_state->was_initiated_in_this_frame()) {
    // Navigation initiated in this frame has been already reported in
    // BeginNavigation.
    for (auto& observer : observers_)
      observer.DidStartNavigation(document_loader->GetUrl(), std::nullopt);
  }

  for (auto& observer : observers_)
    observer.ReadyToCommitNavigation(document_loader);

  for (auto& observer : observers_)
    observer.DidCreateNewDocument();

  DVLOG(1) << "Committed provisional load: "
           << TrimURL(GetLoadingUrl().possibly_invalid_spec());
  TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
               "frame_token", frame_token_, "url",
               GetLoadingUrl().possibly_invalid_spec());
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kWaitForDebuggerOnNavigation)) {
    std::string renderer =
        base::StrCat({"Renderer url=\"",
                      TrimURL(GetLoadingUrl().possibly_invalid_spec()), "\""});
    content::WaitForDebugger(renderer);
  }

  // Generate a new embedding token on each document change.
  GetWebFrame()->SetEmbeddingToken(base::UnguessableToken::Create());

  mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
      browser_interface_broker_receiver;

  // blink passes true when the new pipe needs to be bound.
  if (should_reset_browser_interface_broker) {
    // If we're navigating to a new document, bind
    // |browser_interface_broker_proxy_| to a new browser interface broker. The
    // request end of the new BrowserInterfaceBroker interface will be sent over
    // as part of DidCommitProvisionalLoad. After the RFHI receives the commit
    // confirmation, it will immediately close the old message pipe to avoid
    // GetInterface() calls racing with navigation commit, and bind the request
    // end of the message pipe created here. Must initialize
    // |browser_interface_broker_proxy_| with a new working pipe *before*
    // observers receive DidCommitProvisionalLoad, so they can already request
    // remote interfaces. The interface requests will be serviced once the
    // BrowserInterfaceBroker interface request is bound by the
    // RenderFrameHostImpl.
    browser_interface_broker_receiver =
        frame_->GetBrowserInterfaceBroker().Reset(
            agent_scheduling_group_->agent_group_scheduler()
                .DefaultTaskRunner());

    // blink::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
    auto& factory = blink::AudioOutputIPCFactory::GetInstance();
    if (factory.io_task_runner()) {
      // The RendererAudioOutputStreamFactory must be readily accessible on the
      // IO thread when it's needed, because the main thread may block while
      // waiting for the factory call to finish on the IO thread, so if we tried
      // to lazily initialize it, we could deadlock.
      //
      // TODO(crbug.com/40495144): Still, it is odd for one specific
      // factory to be registered here, make this a RenderFrameObserver.
      // code.
      factory.MaybeDeregisterRemoteFactory(GetWebFrame()->GetLocalFrameToken());
      factory.RegisterRemoteFactory(GetWebFrame()->GetLocalFrameToken(),
                                    frame_->GetBrowserInterfaceBroker());
    }

    // If the request for |audio_input_stream_factory_| is in flight when
    // |browser_interface_broker_proxy_| is reset, it will be silently dropped.
    // We reset |audio_input_stream_factory_| to force a new mojo request to be
    // sent the next time it's used. See https://crbug.com/795258 for
    // implementing a nicer solution.
    audio_input_stream_factory_.reset();

    render_accessibility_manager_->CloseConnection();
  }

  // Notify the MediaPermissionDispatcher that its connection will be closed
  // due to a navigation to a different document.
  if (media_permission_dispatcher_)
    media_permission_dispatcher_->OnNavigation();

  ui::PageTransition transition =
      GetTransitionType(frame_->GetDocumentLoader(), IsMainFrame(),
                        GetWebView()->IsFencedFrameRoot());

  // TODO(crbug.com/40092527): Turn this into a DCHECK for origin equality when
  // the linked bug is fixed. Currently sometimes the browser and renderer
  // disagree on the origin during commit navigation.
  if (pending_cookie_manager_info_ &&
      pending_cookie_manager_info_->origin ==
          url::Origin(frame_->GetDocument().GetSecurityOrigin())) {
    frame_->GetDocument().SetCookieManager(
        std::move(pending_cookie_manager_info_->cookie_manager));
  }

  // TODO(crbug.com/40092527): Turn this into a DCHECK for origin equality when
  // the linked bug is fixed. Currently sometimes the browser and renderer
  // disagree on the origin during commit navigation.
  if (pending_storage_info_ &&
      original_storage_key_.origin() ==
          url::Origin(frame_->GetDocument().GetSecurityOrigin())) {
    if (pending_storage_info_->local_storage_area) {
      frame_->SetLocalStorageArea(
          std::move(pending_storage_info_->local_storage_area));
    }
    if (pending_storage_info_->session_storage_area) {
      frame_->SetSessionStorageArea(
          std::move(pending_storage_info_->session_storage_area));
    }
  }

  DidCommitNavigationInternal(
      commit_type, transition, permissions_policy_header,
      document_policy_header,
      should_reset_browser_interface_broker
          ? mojom::DidCommitProvisionalLoadInterfaceParams::New(
                std::move(browser_interface_broker_receiver))
          : nullptr,
      nullptr /* same_document_params */, GetWebFrame()->GetEmbeddingToken());

  // If we end up reusing this WebRequest (for example, due to a #ref click),
  // we don't want the transition type to persist.  Just clear it.
  navigation_state->set_transition_type(ui::PAGE_TRANSITION_LINK);

  // Check whether we have new encoding name.
  UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());

  NotifyObserversOfNavigationCommit(transition);

  document_state->clear_navigation_state();

  ResetMembersUsedForDurationOfCommit();
}

void RenderFrameImpl::DidCommitDocumentReplacementNavigation(
    blink::WebDocumentLoader* document_loader) {
  DocumentState::FromDocumentLoader(document_loader)
      ->set_navigation_state(NavigationState::CreateForSynchronousCommit());
  // TODO(crbug.com/40581836): figure out which of the following observer
  // calls are necessary, if any.
  for (auto& observer : observers_)
    observer.DidStartNavigation(document_loader->GetUrl(), std::nullopt);
  for (auto& observer : observers_)
    observer.ReadyToCommitNavigation(document_loader);
  for (auto& observer : observers_)
    observer.DidCreateNewDocument();
  ui::PageTransition transition = GetTransitionType(
      document_loader, IsMainFrame(), GetWebView()->IsFencedFrameRoot());
  NotifyObserversOfNavigationCommit(transition);
}

void RenderFrameImpl::DidClearWindowObject() {
  TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::DidClearWindowObject",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  if (enabled_bindings_.Has(BindingsPolicyValue::kWebUi)) {
    WebUIExtension::Install(frame_);
  }

  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();

  // DOM automation bindings that allows the JS content to send JSON-encoded
  // data back to automation in the browser process. By default this isn't
  // allowed unless the process has been started up with the --dom-automation
  // switch.
  if (command_line.HasSwitch(switches::kDomAutomationController))
    DomAutomationController::Install(this, frame_);

  // Bindings that allows the JS content to retrieve a variety of internal
  // metrics. By default this isn't allowed unless the process has been started
  // with the --enable-stats-collection-bindings switch.
  if (command_line.HasSwitch(switches::kStatsCollectionController))
    StatsCollectionController::Install(frame_);

  if (command_line.HasSwitch(switches::kEnableGpuBenchmarking)) {
    GpuBenchmarking::Install(weak_factory_.GetWeakPtr());
  }

  if (command_line.HasSwitch(switches::kEnableSkiaBenchmarking))
    SkiaBenchmarking::Install(frame_);

  for (auto& observer : observers_)
    observer.DidClearWindowObject();
}

void RenderFrameImpl::DidCreateDocumentElement() {
  for (auto& observer : observers_)
    observer.DidCreateDocumentElement();
}

void RenderFrameImpl::RunScriptsAtDocumentElementAvailable() {
  // Wait until any RenderFrameObservers for this frame have a chance to be
  // constructed.
  if (!initialized_)
    return;
  GetContentClient()->renderer()->RunScriptsAtDocumentStart(this);
  // Do not use |this|! ContentClient might have deleted them by now!
}

void RenderFrameImpl::DidReceiveTitle(const blink::WebString& title) {
  // Ignore all but top level navigations.
  if (!frame_->Parent() && !title.IsEmpty()) {
    base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
        process_label_id_, title.Utf8());
  } else {
    // Set process title for sub-frames and title-less frames in traces.
    GURL loading_url = GetLoadingUrl();
    if (!loading_url.host().empty() &&
        loading_url.scheme() != url::kFileScheme) {
      std::string frame_title;
      if (frame_->Parent()) {
        frame_title += "Subframe: ";
      }
      frame_title += loading_url.DeprecatedGetOriginAsURL().spec();
      base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
          process_label_id_, frame_title);
    }
  }

  // Also check whether we have new encoding name.
  UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
}

void RenderFrameImpl::DidDispatchDOMContentLoadedEvent() {
  TRACE_EVENT1("navigation,benchmark,rail",
               "RenderFrameImpl::DidDispatchDOMContentLoadedEvent",
               "frame_token", frame_token_);
  for (auto& observer : observers_)
    observer.DidDispatchDOMContentLoadedEvent();

  // Check whether we have new encoding name.
  UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
}

void RenderFrameImpl::RunScriptsAtDocumentReady() {
  DCHECK(initialized_);
  GetContentClient()->renderer()->RunScriptsAtDocumentEnd(this);
}

void RenderFrameImpl::RunScriptsAtDocumentIdle() {
  GetContentClient()->renderer()->RunScriptsAtDocumentIdle(this);
  // ContentClient might have deleted |this| by now!
}

void RenderFrameImpl::DidHandleOnloadEvents() {
  for (auto& observer : observers_)
    observer.DidHandleOnloadEvents();
}

void RenderFrameImpl::DidFinishLoad() {
  TRACE_EVENT1("navigation,benchmark,rail", "RenderFrameImpl::didFinishLoad",
               "frame_token", frame_token_);
  if (!frame_->Parent()) {
    TRACE_EVENT_INSTANT1("WebCore,benchmark,rail", "LoadFinished",
                         TRACE_EVENT_SCOPE_PROCESS, "isOutermostMainFrame",
                         frame_->IsOutermostMainFrame());
  }

  for (auto& observer : observers_)
    observer.DidFinishLoad();
}

void RenderFrameImpl::DidFinishLoadForPrinting() {
  for (auto& observer : observers_)
    observer.DidFinishLoadForPrinting();
}

void RenderFrameImpl::DidFinishSameDocumentNavigation(
    blink::WebHistoryCommitType commit_type,
    bool is_synchronously_committed,
    blink::mojom::SameDocumentNavigationType same_document_navigation_type,
    bool is_client_redirect,
    const std::optional<blink::SameDocNavigationScreenshotDestinationToken>&
        screenshot_destination) {
  TRACE_EVENT1("navigation,rail",
               "RenderFrameImpl::didFinishSameDocumentNavigation",
               "frame_token", frame_token_);
  WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(document_loader);
  if (is_synchronously_committed) {
    document_state->set_navigation_state(
        NavigationState::CreateForSynchronousCommit());
  }
  document_state->navigation_state()->set_was_within_same_document(true);

  ui::PageTransition transition = GetTransitionType(
      document_loader, IsMainFrame(), GetWebView()->IsFencedFrameRoot());
  auto same_document_params =
      mojom::DidCommitSameDocumentNavigationParams::New();
  same_document_params->same_document_navigation_type =
      same_document_navigation_type;
  same_document_params->is_client_redirect = is_client_redirect;
  same_document_params->started_with_transient_activation =
      document_loader->LastNavigationHadTransientUserActivation();
  same_document_params->should_replace_current_entry =
      document_loader->ReplacesCurrentHistoryItem();
  same_document_params->navigation_entry_screenshot_destination =
      screenshot_destination;

  DidCommitNavigationInternal(
      commit_type, transition,
      network::ParsedPermissionsPolicy(),   // permissions_policy_header
      blink::DocumentPolicyFeatureState(),  // document_policy_header
      nullptr,                              // interface_params
      std::move(same_document_params),
      std::nullopt  // embedding_token
  );

  // If we end up reusing this WebRequest (for example, due to a #ref click),
  // we don't want the transition type to persist.  Just clear it.
  document_state->navigation_state()->set_transition_type(
      ui::PAGE_TRANSITION_LINK);

  for (auto& observer : observers_)
    observer.DidFinishSameDocumentNavigation();

  document_state->clear_navigation_state();
}

void RenderFrameImpl::DidFailAsyncSameDocumentCommit() {
  // This is called when the Navigation API deferred a same-document commit,
  // then fails the navigation without committing, so that we can run the
  // callback if this commit was browser-initiated. If the commit is aborted
  // due to frame detach or another navigation preempting it, NavigationState's
  // destructor will run the callback instead.
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
  if (NavigationState* navigation_state = document_state->navigation_state()) {
    navigation_state->RunCommitSameDocumentNavigationCallback(
        blink::mojom::CommitResult::Aborted);
    document_state->clear_navigation_state();
  }
}

void RenderFrameImpl::WillFreezePage() {
  // Make sure browser has the latest info before the page is frozen. If the
  // page goes into the back-forward cache it could be evicted and some of the
  // updates lost.
  SendUpdateState();
}

void RenderFrameImpl::DidOpenDocumentInputStream(const blink::WebURL& url) {
  GURL filtered_url(url);
  if (!IsValidCommitUrl(filtered_url)) {
    filtered_url = GURL(kBlockedURL);
  }
  GetFrameHost()->DidOpenDocumentInputStream(filtered_url);
}

void RenderFrameImpl::DidSetPageLifecycleState(bool restoring_from_bfcache) {
  for (auto& observer : observers_)
    observer.DidSetPageLifecycleState(restoring_from_bfcache);
}

void RenderFrameImpl::NotifyCurrentHistoryItemChanged() {
  SendUpdateState();
}

void RenderFrameImpl::DidUpdateCurrentHistoryItem() {
  StartDelayedSyncTimer();
}

void RenderFrameImpl::StartDelayedSyncTimer() {
  base::TimeDelta delay;
  if (send_content_state_immediately_) {
    SendUpdateState();
    return;
  } else if (GetWebView()->GetVisibilityState() !=
             blink::mojom::PageVisibilityState::kVisible)
    delay = kDelaySecondsForContentStateSyncHidden;
  else
    delay = kDelaySecondsForContentStateSync;

  if (delayed_state_sync_timer_.IsRunning()) {
    // The timer is already running. If the delay of the timer matches the
    // amount we want to delay by, then return. Otherwise stop the timer so that
    // it gets started with the right delay.
    if (delayed_state_sync_timer_.GetCurrentDelay() == delay)
      return;
    delayed_state_sync_timer_.Stop();
  }
  delayed_state_sync_timer_.Start(FROM_HERE, delay, this,
                                  &RenderFrameImpl::SendUpdateState);
}

bool RenderFrameImpl::SwapOutAndDeleteThis(
    bool is_loading,
    blink::mojom::FrameReplicationStatePtr replicated_frame_state,
    const blink::RemoteFrameToken& proxy_frame_token,
    blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
    blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces,
    const std::optional<base::UnguessableToken>& devtools_frame_token) {
  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::SwapOutAndDeleteThis",
               "frame_token", frame_token_);
  DCHECK(!base::RunLoop::IsNestedOnCurrentThread());

  // Create a WebRemoteFrame so we can pass it into `Swap`.
  blink::WebRemoteFrame* remote_frame = blink::WebRemoteFrame::Create(
      frame_->GetTreeScopeType(), proxy_frame_token);

  blink::WebView* web_view = GetWebView();
  bool is_main_frame = is_main_frame_;

  // The swap call deletes this RenderFrame via FrameDetached.  Do not access
  // any members after this call.
  //
  // TODO(creis): WebFrame::swap() can return false.  Most of those cases
  // should be due to the frame being detached during unload (in which case
  // the necessary cleanup has happened anyway), but it might be possible for
  // it to return false without detaching.
  //
  // This executes the unload handlers on this frame and its local descendants.
  bool success =
      frame_->Swap(remote_frame, std::move(remote_frame_interfaces->frame_host),
                   std::move(remote_frame_interfaces->frame_receiver),
                   std::move(replicated_frame_state), devtools_frame_token);

  // WARNING: Do not access 'this' past this point!

  if (is_main_frame) {
    // Main frames should always swap successfully because there is no parent
    // frame to cause them to become detached.
    DCHECK(success);

    // The `blink::RemoteFrame` being swapped in here has now been attached to
    // the Page as its main frame and properly initialized by the
    // WebFrame::Swap() call, so we can call WebView's
    // DidAttachRemoteMainFrame().
    web_view->DidAttachRemoteMainFrame(
        std::move(remote_main_frame_interfaces->main_frame_host),
        std::move(remote_main_frame_interfaces->main_frame));
  }

  if (!success) {
    // The swap can fail when the frame is detached during swap (this can
    // happen while running the unload handlers). When that happens, delete
    // the proxy.
    remote_frame->Close(blink::DetachReason::kFrameDeletion);
    return false;
  }

  if (is_loading)
    remote_frame->DidStartLoading();

  return true;
}

base::UnguessableToken RenderFrameImpl::GetDevToolsFrameToken() {
  return devtools_frame_token_;
}

void RenderFrameImpl::AbortClientNavigation(bool for_new_navigation) {
  CHECK(in_frame_tree_);
  is_requesting_navigation_ = false;
  if (mhtml_body_loader_client_) {
    mhtml_body_loader_client_->Detach();
    mhtml_body_loader_client_.reset();
  }
  NotifyObserversOfFailedProvisionalLoad();
  // See comment in header for more information of how navigation cleanup works.
  // Note: This might not actually cancel the navigation if the navigation is
  // already in the process of committing to a different RenderFrame.

  if (for_new_navigation) {
    navigation_client_impl_->ResetForNewNavigation(
        /*is_duplicate_navigation=*/false);
  } else {
    navigation_client_impl_->ResetForAbort();
  }
  navigation_client_impl_.reset();
}

void RenderFrameImpl::DidChangeSelection(bool is_empty_selection,
                                         blink::SyncCondition force_sync) {
  if (!GetLocalRootWebFrameWidget()->HandlingInputEvent() &&
      !GetLocalRootWebFrameWidget()->HandlingSelectRange())
    return;

  if (is_empty_selection)
    selection_text_.clear();

  // UpdateTextInputState should be called before SyncSelectionIfRequired.
  // UpdateTextInputState may send TextInputStateChanged to notify the focus
  // was changed, and SyncSelectionIfRequired may send SelectionChanged
  // to notify the selection was changed.  Focus change should be notified
  // before selection change.
  GetLocalRootWebFrameWidget()->UpdateTextInputState();
  SyncSelectionIfRequired(force_sync);
}

void RenderFrameImpl::OnMainFrameIntersectionChanged(
    const gfx::Rect& main_frame_intersection_rect) {
  if (main_frame_intersection_rect != main_frame_intersection_rect_) {
    main_frame_intersection_rect_ = main_frame_intersection_rect;
    for (auto& observer : observers_) {
      observer.OnMainFrameIntersectionChanged(main_frame_intersection_rect);
    }
  }
}

void RenderFrameImpl::OnMainFrameViewportRectangleChanged(
    const gfx::Rect& main_frame_viewport_rect) {
  if (main_frame_viewport_rect != main_frame_viewport_rect_) {
    main_frame_viewport_rect_ = main_frame_viewport_rect;
    for (auto& observer : observers_) {
      observer.OnMainFrameViewportRectangleChanged(main_frame_viewport_rect);
    }
  }
}

void RenderFrameImpl::OnMainFrameImageAdRectangleChanged(
    int element_id,
    const gfx::Rect& image_ad_rect) {
  for (auto& observer : observers_) {
    observer.OnMainFrameImageAdRectangleChanged(element_id, image_ad_rect);
  }
}

void RenderFrameImpl::OnOverlayPopupAdDetected() {
  for (auto& observer : observers_) {
    observer.OnOverlayPopupAdDetected();
  }
}

void RenderFrameImpl::OnLargeStickyAdDetected() {
  for (auto& observer : observers_) {
    observer.OnLargeStickyAdDetected();
  }
}

void RenderFrameImpl::FinalizeRequest(blink::WebURLRequest& request) {
  // This method is called for subresources, while transition type is
  // a navigation concept. We pass ui::PAGE_TRANSITION_LINK as default one.
  FinalizeRequestInternal(request, /*for_outermost_main_frame=*/false,
                          ui::PAGE_TRANSITION_LINK);
  for (auto& observer : observers_) {
    // TODO(sky): rename to FinalizeRequest.
    observer.WillSendRequest(request);
  }
}

std::optional<blink::WebURL> RenderFrameImpl::WillSendRequest(
    const blink::WebURL& target,
    const blink::WebSecurityOrigin& security_origin,
    const net::SiteForCookies& site_for_cookies,
    ForRedirect for_redirect,
    const blink::WebURL& upstream_url) {
  return WillSendRequestInternal(target, security_origin, site_for_cookies,
                                 for_redirect, upstream_url,
                                 ui::PAGE_TRANSITION_LINK);
}

std::optional<blink::WebURL> RenderFrameImpl::WillSendRequestInternal(
    const blink::WebURL& target,
    const blink::WebSecurityOrigin& security_origin,
    const net::SiteForCookies& site_for_cookies,
    ForRedirect for_redirect,
    const blink::WebURL& upstream_url,
    ui::PageTransition transition_type) {
  std::optional<blink::WebURL> adjusted = ApplyFilePathAlias(target);

  GURL new_url;
  std::optional<url::Origin> initiator_origin =
      security_origin.IsNull() ? std::optional<url::Origin>()
                               : std::optional<url::Origin>(security_origin);
  GetContentClient()->renderer()->WillSendRequest(
      frame_, transition_type, upstream_url,
      adjusted.has_value() ? *adjusted : target, site_for_cookies,
      base::OptionalToPtr(initiator_origin), &new_url);
  if (!new_url.is_empty()) {
    return WebURL(new_url);
  }
  return adjusted;
}

void RenderFrameImpl::FinalizeRequestInternal(
    blink::WebURLRequest& request,
    bool for_outermost_main_frame,
    ui::PageTransition transition_type) {
  if (GetWebView()->GetRendererPreferences().enable_do_not_track) {
    request.SetHttpHeaderField(
        blink::WebString::FromUTF8(blink::kDoNotTrackHeader), "1");
  }

  // The request's extra data may indicate that we should set a custom user
  // agent. This needs to be done here, after WebKit is through with setting the
  // user agent on its own.
  WebString custom_user_agent;
  if (request.GetURLRequestExtraData()) {
    blink::WebURLRequestExtraData* old_request_extra_data =
        static_cast<blink::WebURLRequestExtraData*>(
            request.GetURLRequestExtraData().get());

    custom_user_agent = old_request_extra_data->custom_user_agent();
    if (!custom_user_agent.IsNull()) {
      if (custom_user_agent.IsEmpty())
        request.ClearHttpHeaderField("User-Agent");
      else
        request.SetHttpHeaderField("User-Agent", custom_user_agent);
    }
  }

  if (!request.GetURLRequestExtraData()) {
    request.SetURLRequestExtraData(
        base::MakeRefCounted<blink::WebURLRequestExtraData>());
  }
  auto* url_request_extra_data = static_cast<blink::WebURLRequestExtraData*>(
      request.GetURLRequestExtraData().get());
  url_request_extra_data->set_custom_user_agent(custom_user_agent);
  url_request_extra_data->set_is_outermost_main_frame(IsMainFrame() &&
                                                      !IsInFencedFrameTree());
  url_request_extra_data->set_transition_type(transition_type);
  bool is_for_no_state_prefetch =
      GetContentClient()->renderer()->IsPrefetchOnly(this);
  url_request_extra_data->set_is_for_no_state_prefetch(
      is_for_no_state_prefetch);
  url_request_extra_data->set_allow_cross_origin_auth_prompt(
      GetWebView()->GetRendererPreferences().allow_cross_origin_auth_prompt);

  request.SetDownloadToNetworkCacheOnly(is_for_no_state_prefetch &&
                                        !for_outermost_main_frame);

  request.SetHasUserGesture(frame_->HasTransientUserActivation());

  if (!GetWebView()->GetRendererPreferences().enable_referrers) {
    request.SetReferrerString(WebString());
    request.SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever);
  }
}

void RenderFrameImpl::DidLoadResourceFromMemoryCache(
    const blink::WebURLRequest& request,
    const blink::WebURLResponse& response) {
  for (auto& observer : observers_) {
    observer.DidLoadResourceFromMemoryCache(
        request.Url(), response.RequestId(), response.EncodedBodyLength(),
        response.MimeType().Utf8(), response.FromArchive());
  }
}

void RenderFrameImpl::DidStartResponse(
    const url::SchemeHostPort& final_response_url,
    int request_id,
    network::mojom::URLResponseHeadPtr response_head,
    network::mojom::RequestDestination request_destination,
    bool is_ad_resource) {
  for (auto& observer : observers_) {
    observer.DidStartResponse(final_response_url, request_id, *response_head,
                              request_destination, is_ad_resource);
  }
}

void RenderFrameImpl::DidCompleteResponse(
    int request_id,
    const network::URLLoaderCompletionStatus& status) {
  for (auto& observer : observers_)
    observer.DidCompleteResponse(request_id, status);
}

void RenderFrameImpl::DidCancelResponse(int request_id) {
  for (auto& observer : observers_)
    observer.DidCancelResponse(request_id);
}

void RenderFrameImpl::DidReceiveTransferSizeUpdate(int resource_id,
                                                   int received_data_length) {
  for (auto& observer : observers_) {
    observer.DidReceiveTransferSizeUpdate(resource_id, received_data_length);
  }
}

void RenderFrameImpl::DidChangePerformanceTiming() {
  for (auto& observer : observers_)
    observer.DidChangePerformanceTiming();
}

void RenderFrameImpl::DidObserveUserInteraction(
    base::TimeTicks max_event_start,
    base::TimeTicks max_event_queued_main_thread,
    base::TimeTicks max_event_commit_finish,
    base::TimeTicks max_event_end,
    uint64_t interaction_offset) {
  for (auto& observer : observers_) {
    observer.DidObserveUserInteraction(
        max_event_start, max_event_queued_main_thread, max_event_commit_finish,
        max_event_end, interaction_offset);
  }
}

void RenderFrameImpl::DidChangeCpuTiming(base::TimeDelta time) {
  for (auto& observer : observers_)
    observer.DidChangeCpuTiming(time);
}

void RenderFrameImpl::DidObserveLoadingBehavior(
    blink::LoadingBehaviorFlag behavior) {
  for (auto& observer : observers_)
    observer.DidObserveLoadingBehavior(behavior);
}

void RenderFrameImpl::DidObserveJavaScriptFrameworks(
    const blink::JavaScriptFrameworkDetectionResult& result) {
  for (auto& observer : observers_) {
    observer.DidObserveJavaScriptFrameworks(result);
  }
}

void RenderFrameImpl::DidObserveSubresourceLoad(
    const blink::SubresourceLoadMetrics& subresource_load_metrics) {
  for (auto& observer : observers_)
    observer.DidObserveSubresourceLoad(subresource_load_metrics);
}

void RenderFrameImpl::DidObserveNewFeatureUsage(
    const blink::UseCounterFeature& feature) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "RenderFrameImpl::DidObserveNewFeatureUsage",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  for (auto& observer : observers_)
    observer.DidObserveNewFeatureUsage(feature);
}

void RenderFrameImpl::DidObserveSoftNavigation(
    blink::SoftNavigationMetrics metrics) {
  for (auto& observer : observers_) {
    observer.DidObserveSoftNavigation(metrics);
  }
}

void RenderFrameImpl::DidObserveLayoutShift(double score,
                                            bool after_input_or_scroll) {
  for (auto& observer : observers_)
    observer.DidObserveLayoutShift(score, after_input_or_scroll);
}

void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
                                             int world_id) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "RenderFrameImpl::DidCreateScriptContext",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  v8::MicrotasksScope microtasks(GetAgentGroupScheduler().Isolate(),
                                 context->GetMicrotaskQueue(),
                                 v8::MicrotasksScope::kDoNotRunMicrotasks);
  if (((enabled_bindings_.Has(BindingsPolicyValue::kMojoWebUi)) ||
       enable_mojo_js_bindings_) &&
      IsMainFrame() && world_id == ISOLATED_WORLD_ID_GLOBAL) {
    // We only allow these bindings to be installed when creating the main
    // world context of the main frame.
    blink::WebV8Features::EnableMojoJS(context, true);

    if (mojo_js_features_) {
      if (mojo_js_features_->file_system_access)
        blink::WebV8Features::EnableMojoJSFileSystemAccessHelper(context, true);
    }
  }

  if (world_id == ISOLATED_WORLD_ID_GLOBAL &&
      mojo_js_interface_broker_.is_valid()) {
    // MojoJS interface broker can be enabled on subframes, and will limit the
    // interfaces JavaScript can request to those provided in the broker.
    blink::WebV8Features::EnableMojoJSAndUseBroker(
        context, std::move(mojo_js_interface_broker_));
  }

  for (auto& observer : observers_)
    observer.DidCreateScriptContext(context, world_id);
}

void RenderFrameImpl::WillReleaseScriptContext(v8::Local<v8::Context> context,
                                               int world_id) {
  for (auto& observer : observers_)
    observer.WillReleaseScriptContext(context, world_id);
}

void RenderFrameImpl::DidChangeScrollOffset() {
  StartDelayedSyncTimer();

  for (auto& observer : observers_)
    observer.DidChangeScrollOffset();
}

blink::WebMediaStreamDeviceObserver*
RenderFrameImpl::MediaStreamDeviceObserver() {
  if (!web_media_stream_device_observer_)
    InitializeMediaStreamDeviceObserver();
  return web_media_stream_device_observer_.get();
}

blink::WebEncryptedMediaClient* RenderFrameImpl::EncryptedMediaClient() {
  return media_factory_.EncryptedMediaClient();
}

blink::WebString RenderFrameImpl::UserAgentOverride() {
  if (ShouldUseUserAgentOverride()) {
    return WebString::FromUTF8(GetWebView()
                                   ->GetRendererPreferences()
                                   .user_agent_override.ua_string_override);
  }

  return blink::WebString();
}

std::optional<blink::UserAgentMetadata>
RenderFrameImpl::UserAgentMetadataOverride() {
  if (ShouldUseUserAgentOverride()) {
    return GetWebView()
        ->GetRendererPreferences()
        .user_agent_override.ua_metadata_override;
  }
  return std::nullopt;
}

bool RenderFrameImpl::ShouldUseUserAgentOverride() const {
  auto* web_view = GetWebView();
  // TODO(nasko): When the top-level frame is remote, there is no
  // WebDocumentLoader associated with it, so the checks below are not valid.
  // Temporarily return early and fix properly as part of
  // https://crbug.com/426555.
  if (web_view->MainFrame()->IsWebRemoteFrame())
    return false;
  const WebLocalFrame* main_frame = web_view->MainFrame()->ToWebLocalFrame();

  WebDocumentLoader* document_loader = main_frame->GetDocumentLoader();
  DocumentState* document_state =
      document_loader ? DocumentState::FromDocumentLoader(document_loader)
                      : nullptr;
  return document_state && document_state->is_overriding_user_agent();
}

blink::mojom::RendererAudioInputStreamFactory*
RenderFrameImpl::GetAudioInputStreamFactory() {
  if (!audio_input_stream_factory_)
    GetBrowserInterfaceBroker().GetInterface(
        audio_input_stream_factory_.BindNewPipeAndPassReceiver(
            agent_scheduling_group_->agent_group_scheduler()
                .DefaultTaskRunner()));
  return audio_input_stream_factory_.get();
}

bool RenderFrameImpl::AllowContentInitiatedDataUrlNavigations(
    const blink::WebURL& url) {
  // Error pages can navigate to data URLs.
  return url.GetString() == kUnreachableWebDataURL;
}

void RenderFrameImpl::PostAccessibilityEvent(const ui::AXEvent& event) {
  if (!IsAccessibilityEnabled())
    return;

  render_accessibility_manager_->GetRenderAccessibilityImpl()->HandleAXEvent(
      event);
}

bool RenderFrameImpl::SendAccessibilitySerialization(
    std::vector<ui::AXTreeUpdate> updates,
    std::vector<ui::AXEvent> events,
    ui::AXLocationAndScrollUpdates location_and_scroll_updates,
    bool had_load_complete_messages) {
  // This function should never be called from a11y unless it's enabled.
  CHECK(IsAccessibilityEnabled());

  return render_accessibility_manager_->GetRenderAccessibilityImpl()
      ->SendAccessibilitySerialization(std::move(updates), std::move(events),
                                       std::move(location_and_scroll_updates),
                                       had_load_complete_messages);
}

void RenderFrameImpl::AddObserver(RenderFrameObserver* observer) {
  observers_.AddObserver(observer);
}

void RenderFrameImpl::RemoveObserver(RenderFrameObserver* observer) {
  observer->RenderFrameGone();
  observers_.RemoveObserver(observer);
}

void RenderFrameImpl::OnDroppedNavigation() {
  is_requesting_navigation_ = false;
  frame_->DidDropNavigation();
}

void RenderFrameImpl::WasHidden() {
  frame_->WasHidden();
  for (auto& observer : observers_)
    observer.WasHidden();

#if BUILDFLAG(ENABLE_PPAPI)
  for (PepperPluginInstanceImpl* plugin : active_pepper_instances_) {
    plugin->PageVisibilityChanged(false);
  }
#endif  // BUILDFLAG(ENABLE_PPAPI)
}

void RenderFrameImpl::WasShown() {
  frame_->WasShown();
  for (auto& observer : observers_)
    observer.WasShown();

#if BUILDFLAG(ENABLE_PPAPI)
  for (PepperPluginInstanceImpl* plugin : active_pepper_instances_) {
    plugin->PageVisibilityChanged(true);
  }
#endif  // BUILDFLAG(ENABLE_PPAPI)
}

void RenderFrameImpl::OnFrameVisibilityChanged(
    blink::mojom::FrameVisibility render_status) {
  for (auto& observer : observers_) {
    observer.OnFrameVisibilityChanged(render_status);
  }
}

bool RenderFrameImpl::IsMainFrame() {
  return is_main_frame_;
}

bool RenderFrameImpl::IsInFencedFrameTree() const {
  return GetWebFrame()->IsInFencedFrameTree();
}

bool RenderFrameImpl::IsHidden() {
  CHECK(GetWebFrame()->IsProvisional() || GetLocalRootWebFrameWidget())
      << "Only provisional frames are created with no widget";
  if (!GetLocalRootWebFrameWidget()) {
    return true;
  }
  return GetLocalRootWebFrameWidget()->IsHidden();
}

bool RenderFrameImpl::IsLocalRoot() const {
  return !(frame_->Parent() && frame_->Parent()->IsWebLocalFrame());
}

const RenderFrameImpl* RenderFrameImpl::GetLocalRoot() const {
  return IsLocalRoot() ? this
                       : RenderFrameImpl::FromWebFrame(frame_->LocalRoot());
}

base::WeakPtr<RenderFrameImpl> RenderFrameImpl::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

mojom::DidCommitProvisionalLoadParamsPtr
RenderFrameImpl::MakeDidCommitProvisionalLoadParams(
    blink::WebHistoryCommitType commit_type,
    ui::PageTransition transition,
    const network::ParsedPermissionsPolicy& permissions_policy_header,
    const blink::DocumentPolicyFeatureState& document_policy_header,
    const std::optional<base::UnguessableToken>& embedding_token) {
  WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
  const WebURLResponse& response = document_loader->GetWebResponse();

  DocumentState* document_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
  NavigationState* navigation_state = document_state->navigation_state();

  auto params = mojom::DidCommitProvisionalLoadParams::New();
  params->http_status_code = response.HttpStatusCode();
  params->url_is_unreachable = document_loader->HasUnreachableURL();
  params->method = "GET";
  params->post_id = -1;
  params->embedding_token = embedding_token;
  params->origin_calculation_debug_info =
      document_loader->OriginCalculationDebugInfo().Utf8();

  // Pass the navigation token back to the browser process, or generate a new
  // one if this navigation is committing without the browser process asking for
  // it.
  // TODO(clamy): We should add checks on navigations that commit without having
  // been asked to commit by the browser process.
  params->navigation_token = navigation_state->commit_params().navigation_token;
  if (params->navigation_token.is_empty())
    params->navigation_token = base::UnguessableToken::Create();

  // "Standard" commits from Blink create new NavigationEntries. We also treat
  // main frame "inert" commits as creating new NavigationEntries if they
  // replace the current entry on a cross-document navigation (e.g., client
  // redirects, location.replace, navigation to same URL), since this will
  // replace all the subframes and could go cross-origin. We don't want to rely
  // on updating the existing NavigationEntry in this case, since it could leave
  // stale state around.
  params->did_create_new_entry =
      (commit_type == blink::kWebStandardCommit) ||
      (commit_type == blink::kWebHistoryInertCommit && !frame_->Parent() &&
       document_loader->ReplacesCurrentHistoryItem() &&
       !navigation_state->WasWithinSameDocument());

  WebDocument frame_document = frame_->GetDocument();
  // Set the origin of the frame.  This will be replicated to the corresponding
  // RenderFrameProxies in other processes.
  WebSecurityOrigin frame_origin = frame_document.GetSecurityOrigin();
  params->origin = frame_origin;

  params->permissions_policy_header = permissions_policy_header;
  params->document_policy_header = document_policy_header;

  params->insecure_request_policy = frame_->GetInsecureRequestPolicy();
  params->insecure_navigations_set = frame_->GetInsecureRequestToUpgrade();

  params->has_potentially_trustworthy_unique_origin =
      frame_origin.IsOpaque() && frame_origin.IsPotentiallyTrustworthy();

  // Set the URL to be displayed in the browser UI to the user. Note this might
  // be different than the URL actually used in the DocumentLoader (see comments
  // in GetLoadingUrl() and MaybeGetOverriddenURL()). This might not be the URL
  // actually shown to the user as well, since the browser has additional logic
  // for virtual URLs (e.g. the "history URL" is shown for loadDataWithBaseURL
  // instead of this URL).
  params->url = GetLoadingUrl();
  // Note: since we get the security origin from the `frame_document`, we also
  // get the base url from it too.
  if (params->url.IsAboutBlank() || params->url.IsAboutSrcdoc()) {
    GURL base_url = frame_document.BaseURL();
    // Only pass the base URL if it is valid and can be serialized by Mojo.
    if (base_url.is_valid() &&
        base_url.possibly_invalid_spec().length() <= url::kMaxURLChars) {
      params->initiator_base_url = base_url;
    }
  }

  // Don't send commit URLs to the browser that are known to be unsupported
  // (e.g., would not pass RenderProcessHostImpl::FilterURL). This is applied
  // after the initiator_base_url check above to avoid passing a base URL when
  // the URL was not about:blank but was rewritten to about:blank#blocked.
  if (!IsValidCommitUrl(params->url)) {
    params->url = GURL(kBlockedURL);
  }

  // TODO(crbug.com/40161149): Reconsider how we calculate
  // should_update_history.
  params->should_update_history =
      !document_loader->HasUnreachableURL() && response.HttpStatusCode() != 404;

  // Make navigation state a part of the DidCommitProvisionalLoad message so
  // that committed entry has it at all times.  Send a single HistoryItem for
  // this frame, rather than the whole tree.  It will be stored in the
  // corresponding FrameNavigationEntry.
  const WebHistoryItem& item = GetWebFrame()->GetCurrentHistoryItem();
  params->page_state = GetWebFrame()->CurrentHistoryItemToPageState();

  params->method = document_loader->HttpMethod().Latin1();
  if (params->method == "POST")
    params->post_id = ExtractPostId(item);

  params->item_sequence_number = item.ItemSequenceNumber();
  params->document_sequence_number = item.DocumentSequenceNumber();
  params->navigation_api_key = item.GetNavigationApiKey().Utf8();

  // Note that the value of `referrer` will be overwritten in the browser with a
  // browser-calculated value in most cases. The exceptions are
  // renderer-initated same-document navigations and the synchronous about:blank
  // commit (because the browser doesn't know anything about those navigations).
  // In those cases, the referrer policy component will still be overwritten in
  // the browser, because this navigation won't change it and the browser
  // already had access to the previous one. Send ReferrerPolicy::kDefault as a
  // placeholder.
  // TODO(crbug.com/40150370): Remove `referrer` from
  // DidCommitProvisionalLoadParams.
  params->referrer = blink::mojom::Referrer::New(
      blink::WebStringToGURL(document_loader->Referrer()),
      network::mojom::ReferrerPolicy::kDefault);

  if (!frame_->Parent()) {
    // Top-level navigation.

    // Update contents MIME type for main frame.
    params->contents_mime_type =
        document_loader->GetWebResponse().MimeType().Utf8();

    params->transition = transition;
    // Check that if we are in a fenced frame tree then we must have
    // PAGE_TRANSITION_AUTO_SUBFRAME. Otherwise we are a main frame
    // and should have valid main frame values.
    if (GetWebView()->IsFencedFrameRoot()) {
      DCHECK(ui::PageTransitionCoreTypeIs(params->transition,
                                          ui::PAGE_TRANSITION_AUTO_SUBFRAME));
    } else {
      DCHECK(ui::PageTransitionIsMainFrame(params->transition));
    }

    // If the page contained a client redirect (meta refresh, document.loc...),
    // set the transition appropriately.
    if (document_loader->IsClientRedirect()) {
      params->transition = ui::PageTransitionFromInt(
          params->transition | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
    }

    // Send the user agent override back.
    params->is_overriding_user_agent =
        document_state->is_overriding_user_agent();

    params->history_list_was_cleared =
        navigation_state->commit_params().should_clear_history_list;
  } else {
    // Subframe navigation: the type depends on whether this navigation
    // generated a new session history entry. When they do generate a session
    // history entry, it means the user initiated the navigation and we should
    // mark it as such.
    if (commit_type == blink::kWebStandardCommit)
      params->transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
    else
      params->transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;

    DCHECK(!navigation_state->commit_params().should_clear_history_list);
    params->history_list_was_cleared = false;
  }

  bool requires_universal_access = false;
  const bool file_scheme_with_universal_access =
      params->origin.scheme() == url::kFileScheme &&
      GetBlinkPreferences().allow_universal_access_from_file_urls;

  // Standard URLs must match the reported origin, when it is not unique.
  // This check is very similar to RenderFrameHostImpl::CanCommitOrigin, but
  // adapted to the renderer process side.
  if (!params->origin.opaque() && params->url.IsStandard() &&
      GetBlinkPreferences().web_security_enabled) {
    if (!params->origin.IsSameOriginWith(params->url)) {
      // Exclude file: URLs when settings allow them access any origin.
      if (!file_scheme_with_universal_access) {
        SCOPED_CRASH_KEY_STRING256("MakeDCPLParams", "mismatched_url",
                                   params->url.possibly_invalid_spec());
        SCOPED_CRASH_KEY_STRING256("MakeDCPLParams", "mismatched_origin",
                                   params->origin.GetDebugString());
        NOTREACHED() << " url:" << params->url << " origin:" << params->origin;
      } else {
        requires_universal_access = true;
      }
    }
    if (file_scheme_with_universal_access) {
      base::UmaHistogramBoolean(
          "Android.WebView.UniversalAccess.OriginUrlMismatchInRenderFrame",
          requires_universal_access);
    }
  }
  params->request_id = document_state->request_id();

  params->unload_start =
      GetWebFrame()->PerformanceMetricsForNestedContexts().UnloadStart();
  params->unload_end =
      GetWebFrame()->PerformanceMetricsForNestedContexts().UnloadEnd();
  params->commit_navigation_start = navigation_state->commit_start_time();
  params->commit_navigation_end = GetWebFrame()
                                      ->PerformanceMetricsForNestedContexts()
                                      .CommitNavigationEnd();
  // Note: this value should be recorded close to sending the DidCommit IPC.
  params->commit_reply_sent = base::TimeTicks().Now();

  return params;
}

void RenderFrameImpl::UpdateNavigationHistory(
    blink::WebHistoryCommitType commit_type) {
  NavigationState* navigation_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader())
          ->navigation_state();
  const blink::mojom::CommitNavigationParams& commit_params =
      navigation_state->commit_params();

  GetWebFrame()->UpdateCurrentHistoryItem();
  GetWebFrame()->SetTargetToCurrentHistoryItem(
      blink::WebString::FromUTF8(unique_name_helper_.value()));

  bool is_new_navigation = commit_type == blink::kWebStandardCommit;
  blink::WebView* webview = GetWebView();
  if (commit_params.should_clear_history_list) {
    webview->SetHistoryListFromNavigation(/*history_index*/ 0,
                                          /*history_length*/ 1);
  } else if (is_new_navigation) {
    DCHECK(!navigation_state->common_params().should_replace_current_entry ||
           (webview->HistoryBackListCount() +
            webview->HistoryForwardListCount() + 1) > 0);
    if (!navigation_state->common_params().should_replace_current_entry)
      webview->IncreaseHistoryListFromNavigation();
  } else if (commit_params.nav_entry_id != 0 &&
             !commit_params.intended_as_new_entry) {
    webview->SetHistoryListFromNavigation(
        navigation_state->commit_params().pending_history_list_index, {});
  }
}

void RenderFrameImpl::NotifyObserversOfNavigationCommit(
    ui::PageTransition transition) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "RenderFrameImpl::NotifyObserversOfNavigationCommit",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  for (auto& observer : observers_)
    observer.DidCommitProvisionalLoad(transition);
}

void RenderFrameImpl::UpdateStateForCommit(
    blink::WebHistoryCommitType commit_type,
    ui::PageTransition transition) {
  DocumentState* document_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
  NavigationState* navigation_state = document_state->navigation_state();

  // We need to update the last committed session history entry with state for
  // the previous page. Do this before updating the current history item.
  SendUpdateState();

  UpdateNavigationHistory(commit_type);

  if (!frame_->Parent()) {  // Only for top frames.
    RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
    if (render_thread_impl) {  // Can be NULL in tests.
      render_thread_impl->histogram_customizer()->RenderViewNavigatedToHost(
          GetLoadingUrl().host(), blink::WebView::GetWebViewCount());
    }
  }

  if (IsLocalRoot()) {
    // This forces zoom factor to be propagated to the blink core frame.
    auto& widget = CHECK_DEREF(GetLocalRootWebFrameWidget());
    widget.SetZoomLevel(widget.GetZoomLevel());
  }

  // If we are a top frame navigation to another document we should clear any
  // existing autoplay flags on the Page. This is because flags are stored at
  // the page level so subframes would only add to them.
  if (!frame_->Parent() && !navigation_state->WasWithinSameDocument()) {
    GetWebView()->ClearAutoplayFlags();
  }

  // Set the correct autoplay flags on the Page and wipe the cached origin so
  // this will not be used incorrectly.
  if (url::Origin(frame_->GetSecurityOrigin()) == autoplay_flags_.first) {
    GetWebView()->AddAutoplayFlags(autoplay_flags_.second);
    autoplay_flags_.first = url::Origin();
  }
}

void RenderFrameImpl::DidCommitNavigationInternal(
    blink::WebHistoryCommitType commit_type,
    ui::PageTransition transition,
    const network::ParsedPermissionsPolicy& permissions_policy_header,
    const blink::DocumentPolicyFeatureState& document_policy_header,
    mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
    mojom::DidCommitSameDocumentNavigationParamsPtr same_document_params,
    const std::optional<base::UnguessableToken>& embedding_token) {
  DCHECK(!(same_document_params && interface_params));
  UpdateStateForCommit(commit_type, transition);

  if (GetBlinkPreferences().renderer_wide_named_frame_lookup)
    GetWebFrame()->SetAllowsCrossBrowsingInstanceFrameLookup();

  auto params = MakeDidCommitProvisionalLoadParams(
      commit_type, transition, permissions_policy_header,
      document_policy_header, embedding_token);
  NavigationState* navigation_state =
      DocumentState::FromDocumentLoader(frame_->GetDocumentLoader())
          ->navigation_state();

  // This invocation must precede any calls to allowScripts(), allowImages(),
  // or allowPlugins() for the new page. This ensures that when these functions
  // call chrome::ContentSettingsManager::OnContentBlocked, those calls arrive
  // after the browser process has already been informed of the provisional
  // load committing.
  if (same_document_params) {
    GetFrameHost()->DidCommitSameDocumentNavigation(
        std::move(params), std::move(same_document_params));
    // This will be a noop if this same document navigation is a synchronous
    // renderer-initiated commit.
    navigation_state->RunCommitSameDocumentNavigationCallback(
        blink::mojom::CommitResult::Ok);
  } else {
    if (navigation_state->has_navigation_client()) {
      navigation_state->RunCommitNavigationCallback(
          std::move(params), std::move(interface_params));
    } else {
      GetFrameHost()->DidCommitProvisionalLoad(std::move(params),
                                               std::move(interface_params));
    }
  }

  // Ensure we will propagate the main frame and viewport rect when the main
  // frame commits even if the rect does not change across navigations.
  if (IsMainFrame()) {
    main_frame_intersection_rect_.reset();
    main_frame_viewport_rect_.reset();
  }
}

void RenderFrameImpl::PrepareFrameForCommit(
    const GURL& url,
    const blink::mojom::CommitNavigationParams& commit_params) {
  is_requesting_navigation_ = false;
  GetContentClient()->SetActiveURL(
      url, frame_->Top()->GetSecurityOrigin().ToString().Utf8());

  GetWebView()->SetHistoryListFromNavigation(
      commit_params.current_history_list_index,
      commit_params.current_history_list_length);
}

blink::mojom::CommitResult RenderFrameImpl::PrepareForHistoryNavigationCommit(
    const blink::mojom::CommonNavigationParams& common_params,
    const blink::mojom::CommitNavigationParams& commit_params,
    WebHistoryItem* item_for_history_navigation,
    blink::WebFrameLoadType* load_type) {
  blink::mojom::NavigationType navigation_type = common_params.navigation_type;
  DCHECK(navigation_type ==
             blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT ||
         navigation_type ==
             blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT ||
         navigation_type == blink::mojom::NavigationType::RESTORE ||
         navigation_type == blink::mojom::NavigationType::RESTORE_WITH_POST);
  *item_for_history_navigation = WebHistoryItem(
      blink::PageState::CreateFromEncodedData(commit_params.page_state));
  if (item_for_history_navigation->IsNull())
    return blink::mojom::CommitResult::Aborted;

  // The browser process sends a single WebHistoryItem for this frame.
  // TODO(creis): Change PageState to FrameState.  In the meantime, we
  // store the relevant frame's WebHistoryItem in the root of the
  // PageState.
  if (navigation_type == blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT ||
      navigation_type ==
          blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT) {
    *load_type = blink::WebFrameLoadType::kBackForward;
  } else {
    *load_type = blink::WebFrameLoadType::kRestore;
  }

  // Keep track of which subframes the browser process has history items
  // for during a history navigation.
  history_subframe_unique_names_ = commit_params.subframe_unique_names;

  if (navigation_type == blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT) {
    // If this is marked as a same document load but we haven't committed
    // anything, we can't proceed with the load. The browser shouldn't let this
    // happen.
    // TODO(crbug.com/41489044): A same-document history navigation was
    // performed but the renderer does not have a history item. Diagnose this,
    // make it a CHECK again, and drop the Restart.
    DCHECK(!GetWebFrame()->GetCurrentHistoryItem().IsNull());
    if (GetWebFrame()->GetCurrentHistoryItem().IsNull()) {
      SCOPED_CRASH_KEY_BOOL("history_no_item", "is_main_frame", IsMainFrame());
      SCOPED_CRASH_KEY_NUMBER("history_no_item", "renderer_commit_state",
                              (int)navigation_commit_state_);
      SCOPED_CRASH_KEY_NUMBER("history_no_item", "browser_history_index",
                              commit_params.current_history_list_index);
      SCOPED_CRASH_KEY_NUMBER("history_no_item", "browser_history_len",
                              commit_params.current_history_list_length);
      SCOPED_CRASH_KEY_NUMBER("history_no_item", "renderer_history_len",
                              GetWebView()->HistoryBackListCount() +
                                  GetWebView()->HistoryForwardListCount() + 1);
      base::debug::DumpWithoutCrashing();
      return blink::mojom::CommitResult::RestartCrossDocument;
    }

    // Additionally, if the current history item's document sequence number
    // doesn't match the one sent from the browser, it is possible that this
    // renderer has committed a different document. In such case, the navigation
    // cannot be loaded as a same-document navigation. The browser shouldn't let
    // this happen.
    // TODO(crbug.com/40055210): A same document history navigation was
    // performed but the renderer thinks there's a different document loaded.
    // Where did this bad state of a different document + same-document
    // navigation come from? Figure it out, make this a CHECK again, and drop
    // the Restart.
    DCHECK_EQ(GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber(),
              item_for_history_navigation->DocumentSequenceNumber());
    if (GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber() !=
        item_for_history_navigation->DocumentSequenceNumber()) {
      SCOPED_CRASH_KEY_NUMBER(
          "history_bad_seq", "browser_doc_seq_num",
          item_for_history_navigation->DocumentSequenceNumber());
      SCOPED_CRASH_KEY_NUMBER(
          "history_bad_seq", "renderer_doc_seq_num",
          GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber());
      base::debug::DumpWithoutCrashing();
      return blink::mojom::CommitResult::RestartCrossDocument;
    }
  }

  // Note: we used to check that initial history navigation in the child frame
  // was not canceled by a client redirect before committing it. However,
  // we now destroy the NavigationClient for initial history navigation, and
  // commit does not arrive to the renderer in this case.

  return blink::mojom::CommitResult::Ok;
}

bool RenderFrameImpl::SwapIn(WebFrame* previous_web_frame) {
  CHECK(!in_frame_tree_);

  // The unique name can still change in `WebFrame::Swap()` below (due to JS
  // changing the browsing context name), but in practice, this seems to be good
  // enough.
  unique_name_helper_.set_propagated_name(
      GetUniqueNameOfWebFrame(previous_web_frame));

  // Swapping out a frame can dispatch JS event handlers, causing `this` to be
  // deleted.
  bool is_main_frame = is_main_frame_;
  if (auto* render_frame = RenderFrameImpl::FromWebFrame(previous_web_frame);
      render_frame && widget_params_for_lazy_widget_creation_) {
    // For local -> local swaps that lazily initialize the widget, tell the
    // previous RenderFrame (i.e. the frame being swapped out) about `this`
    // (i.e. the frame being swapped in), so `WillDetach()` on the previous
    // RenderFrame can pass off its compositor to `this` for reuse.
    //
    // TODO(dcheng): Blink should already know the provisional frame internally;
    // consider exposing that through the public API to avoid having to plumb
    // state at a distance like this.
    render_frame->provisional_frame_for_local_root_swap_ = GetWeakPtr();
  }
  if (!previous_web_frame->Swap(frame_)) {
    // Main frames should always swap successfully because there is no parent
    // frame to cause them to become detached.
    DCHECK(!is_main_frame);
    return false;
  }

  // For local roots, whether the frame widget is created eagerly or lazily, it
  // must be initialized by this point.
  //
  // For non local roots, they must have a local root ancestor which already has
  // a frame widget.
  CHECK(GetLocalRootWebFrameWidget());

  // `previous_web_frame` is now detached, and should no longer be referenced.

  in_frame_tree_ = true;

  // If this is the main frame going from a remote frame to a local frame,
  // it needs to set RenderViewImpl's pointer for the main frame to itself.
  if (is_main_frame_) {
    // The WebFrame being swapped in here has now been attached to the Page as
    // its main frame, and the WebFrameWidget was previously initialized when
    // the frame was created so we can call WebView's DidAttachLocalMainFrame().
    GetWebView()->DidAttachLocalMainFrame();
  }

  return true;
}

void RenderFrameImpl::DidStartLoading() {
  // TODO(dgozman): consider removing this callback.
  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::didStartLoading",
               "frame_token", frame_token_);
}

void RenderFrameImpl::DidStopLoading() {
  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::didStopLoading",
               "frame_token", frame_token_);

  // Any subframes created after this point won't be considered part of the
  // current history navigation (if this was one), so we don't need to track
  // this state anymore.
  history_subframe_unique_names_.clear();

  GetFrameHost()->DidStopLoading();
}

void RenderFrameImpl::NotifyAccessibilityModeChange(ui::AXMode new_mode) {
  for (auto& observer : observers_)
    observer.AccessibilityModeChanged(new_mode);
}

void RenderFrameImpl::FocusedElementChanged(const blink::WebElement& element) {
  for (auto& observer : observers_)
    observer.FocusedElementChanged(element);
}

void RenderFrameImpl::BeginNavigation(
    std::unique_ptr<blink::WebNavigationInfo> info) {
  // A provisional frame should never make a renderer-initiated navigation: no
  // JS should be running in |this|, and no other frame should have a reference
  // to |this|.
  CHECK(in_frame_tree_);

  // This might be the first navigation in this RenderFrame.
  const bool first_navigation_in_render_frame = !had_started_any_navigation_;
  had_started_any_navigation_ = true;

  // This method is only called for renderer initiated navigations, which
  // may have originated from a link-click, script, drag-n-drop operation, etc.

  // Note that we don't want to go to browser for a navigation to an empty url,
  // which happens for window.open('') call. An example would be embedder
  // deciding to fork the process for the empty url, or setting
  // |browser_handles_all_top_level_requests| preference.
  //
  // Doing a browser-side navigation might later trigger unload handlers,
  // e.g. when the dom window of the popup has already been touched
  // synchronously in this process. We should avoid that.
  //
  // See the checks for empty url in the cases below.
  // TODO(dgozman): if we rewrite empty url to about:blank earlier
  // (we currently do that in DocumentLoader), all the empty checks can be
  // removed, since they already account for an empty url.

  const GURL& url = info->url_request.Url();
  TRACE_EVENT2("navigation", "RenderFrameImpl::BeginNavigation", "url",
               url.possibly_invalid_spec(), "navigation_type",
               static_cast<int>(info->navigation_type));

  // When an MHTML Archive is present, it should be used to serve iframe
  // content instead of doing a network request. This should never be true for
  // the main frame.
  bool use_archive = (info->archive_status ==
                      blink::WebNavigationInfo::ArchiveStatus::Present) &&
                     !url.SchemeIs(url::kDataScheme);
  DCHECK(!(use_archive && IsMainFrame()));

#if BUILDFLAG(IS_ANDROID)
  // The handlenavigation API is deprecated and will be removed once
  // crbug.com/325351 is resolved.
  if (!url.is_empty() && !use_archive && !IsURLHandledByNetworkStack(url) &&
      GetContentClient()->renderer()->HandleNavigation(
          this, frame_, info->url_request, info->navigation_type,
          info->navigation_policy, false /* is_redirect */)) {
    return;
  }
#endif

  // TODO(crbug.com/40221940): Refactor _unfencedTop handling.
  if (info->is_unfenced_top_navigation) {
    OpenURL(std::move(info));
    return;
  }

  // If the browser is interested, then give it a chance to look at the request.
  if (IsTopLevelNavigation(frame_) &&
      GetWebView()
          ->GetRendererPreferences()
          .browser_handles_all_top_level_requests) {
    OpenURL(std::move(info));
    return;  // Suppress the load here.
  }

  // Back/forward navigations in newly created subframes should be sent to the
  // browser if there is a matching FrameNavigationEntry, and if it isn't just
  // staying at about:blank.  If this frame isn't in the map of unique names
  // that have history items, or if it's staying at the initial about:blank URL,
  // fall back to loading the default url.  (We remove each name as we encounter
  // it, because it will only be used once as the frame is created.)
  // Note: Skip this logic for MHTML files (|use_archive|), which should load
  // their subframes from the archive and not from history.
  bool is_history_navigation_in_new_child_frame = false;
  if (info->is_history_navigation_in_new_child_frame && frame_->Parent() &&
      !use_archive) {
    // Check whether the browser has a history item for this frame that isn't
    // just staying at the initial about:blank document.
    RenderFrameImpl* parent = RenderFrameImpl::FromWebFrame(frame_->Parent());
    auto iter = parent->history_subframe_unique_names_.find(
        unique_name_helper_.value());
    if (iter != parent->history_subframe_unique_names_.end()) {
      bool history_item_is_about_blank = iter->second;
      is_history_navigation_in_new_child_frame =
          !history_item_is_about_blank || url != url::kAboutBlankURL;
      parent->history_subframe_unique_names_.erase(iter);
    }
  }

  if (is_history_navigation_in_new_child_frame) {
    // Don't do this if |info| also says it is a client redirect, in which
    // case JavaScript on the page is trying to interrupt the history
    // navigation.
    if (info->is_client_redirect) {
      // Client redirects during an initial history load should attempt to
      // cancel the history navigation.  They will create a provisional
      // document loader, causing the history load to be ignored in
      // NavigateInternal, and this IPC will try to cancel any cross-process
      // history load.
      is_history_navigation_in_new_child_frame = false;
      GetFrameHost()->CancelInitialHistoryLoad();
    }
  }

  // Use the frame's original request's URL rather than the document's URL for
  // subsequent checks.  For a popup, the document's URL may become the opener
  // window's URL if the opener has called document.write().
  // See http://crbug.com/93517.
  GURL old_url(frame_->GetDocumentLoader()->GetUrl());

  // Detect when we're crossing a permission-based boundary (e.g. into or out of
  // an extension or app origin, leaving a WebUI page, etc). We only care about
  // top-level navigations (not iframes). But we sometimes navigate to
  // about:blank to clear a tab, and we want to still allow that.
  if (IsTopLevelNavigation(frame_) && !url.SchemeIs(url::kAboutScheme) &&
      !url.is_empty()) {
    // All navigations to or from WebUI URLs or within WebUI-enabled
    // RenderProcesses must be handled by the browser process so that the
    // correct bindings and data sources can be registered.
    // All frames in a WebUI process must have the same enabled_bindings_, so
    // we can do a per-frame check here rather than a process-wide check.
    bool should_fork = HasWebUIScheme(url) || HasWebUIScheme(old_url) ||
                       enabled_bindings_.HasAny(kWebUIBindingsPolicySet);
    if (should_fork) {
      OpenURL(std::move(info));
      return;  // Suppress the load here.
    }
  }

  if (frame_->IsOutermostMainFrame() && url.is_valid() &&
      url.SchemeIsHTTPOrHTTPS() &&
      (base::FeatureList::IsEnabled(
           blink::features::kHttpDiskCachePrewarming) ||
       base::FeatureList::IsEnabled(
           blink::features::kSpeculativeServiceWorkerWarmUp))) {
    frame_->MaybeStartOutermostMainFrameNavigation(std::vector<WebURL>({url}));
  }

  // Depending on navigation policy, send one of three IPCs to the browser
  // process: DownloadURL handles downloads, OpenURL handles all navigations
  // that will end up in a different tab/window, and BeginNavigation handles
  // everything else.
  if (info->navigation_policy == blink::kWebNavigationPolicyDownload) {
    mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token =
        CloneBlobURLToken(info->blob_url_token);

    frame_->DownloadURL(info->url_request,
                        network::mojom::RedirectMode::kFollow,
                        std::move(blob_url_token));
    return;
  }
  if (info->navigation_policy != blink::kWebNavigationPolicyCurrentTab) {
    OpenURL(std::move(info));
    return;
  }

  // Execute the BeforeUnload event. If asked not to proceed or the frame is
  // destroyed, ignore the navigation.
  // Keep a WeakPtr to this RenderFrameHost to detect if executing the
  // BeforeUnload event destroyed this frame.
  base::WeakPtr<RenderFrameImpl> weak_self = weak_factory_.GetWeakPtr();

  base::TimeTicks renderer_before_unload_start = base::TimeTicks::Now();
  if (!frame_->DispatchBeforeUnloadEvent(info->navigation_type ==
                                         blink::kWebNavigationTypeReload) ||
      !weak_self) {
    return;
  }
  base::TimeTicks renderer_before_unload_end = base::TimeTicks::Now();

  if (!info->form.IsNull()) {
    for (auto& observer : observers_)
      observer.WillSubmitForm(info->form);
  }

  if (mhtml_body_loader_client_) {
    mhtml_body_loader_client_->Detach();
    mhtml_body_loader_client_.reset();
  }

  // In certain cases, Blink re-navigates to about:blank when creating a new
  // browsing context (when opening a new window or creating an iframe) and
  // expects the navigation to complete synchronously.
  // TODO(crbug.com/40184245): Remove the synchronous about:blank
  // navigation.
  bool should_do_synchronous_about_blank_navigation =
      // Mainly a proxy for checking about:blank, even though it can match
      // other things like about:srcdoc (or any empty document schemes that
      // are registered).
      // TODO(crbug.com/40184245): Tighten the condition to only accept
      // about:blank or an empty URL which defaults to about:blank, per the
      // spec:
      // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:about:blank
      WebDocumentLoader::WillLoadUrlAsEmpty(url) &&
      // The navigation method must be "GET". This is to avoid issues like
      // https://crbug.com/1210653, where a form submits to about:blank
      // targeting a new window using a POST. The browser never expects this
      // to happen synchronously because it only expects the synchronous
      // about:blank navigation to originate from browsing context creation,
      // which will always be GET requests.
      info->url_request.HttpMethod().Equals("GET") &&
      // If the frame has committed or even started a navigation before, this
      // navigation can't possibly be triggered by browsing context creation,
      // which would have triggered the navigation synchronously as the first
      // navigation in this frame. Note that we check both
      // IsOnInitialEmptyDocument() and `first_navigation_in_render_frame`
      // here because `first_navigation_in_render_frame` only tracks the state
      // in this *RenderFrame*, so it will be true even if this navigation
      // happens on a frame that has existed before in another process (e.g.
      // an <iframe> pointing to a.com being navigated to a cross-origin
      // about:blank document that happens in a new frame). Meanwhile,
      // IsOnInitialEmptyDocument() tracks the state of the frame, so it will
      // be true in the aforementioned case and we would not do a synchronous
      // commit here.
      frame_->IsOnInitialEmptyDocument() && first_navigation_in_render_frame &&
      // If this is a subframe history navigation that should be sent to the
      // browser, don't commit it synchronously.
      !is_history_navigation_in_new_child_frame &&
      // Synchronous about:blank commits on iframes should only be triggered
      // when first creating the iframe with an unset/about:blank URL, which
      // means the origin should inherit from the parent.
      (IsMainFrame() || info->url_request.RequestorOrigin().IsSameOriginWith(
                            static_cast<WebLocalFrame*>(frame_->Parent())
                                ->GetDocument()
                                .GetSecurityOrigin()));

  if (should_do_synchronous_about_blank_navigation) {
    for (auto& observer : observers_)
      observer.DidStartNavigation(url, info->navigation_type);
    SynchronouslyCommitAboutBlankForBug778318(std::move(info));
    return;
  }

  // Everything else is handled asynchronously by the browser process through
  // BeginNavigation.
  BeginNavigationInternal(
      std::move(info), is_history_navigation_in_new_child_frame,
      renderer_before_unload_start, renderer_before_unload_end);
}

void RenderFrameImpl::SynchronouslyCommitAboutBlankForBug778318(
    std::unique_ptr<blink::WebNavigationInfo> info) {
  CHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
           navigation_commit_state_);
  navigation_commit_state_ = NavigationCommitState::kNone;
  AssertNavigationCommits assert_navigation_commits(this);

  // TODO(dgozman): should we follow the RFI::CommitNavigation path instead?
  auto navigation_params = WebNavigationParams::CreateFromInfo(*info);
  // This quirk is internal to the renderer, so just reuse the previous
  // DocumentToken.
  navigation_params->document_token = frame_->GetDocument().Token();
  navigation_params->is_synchronous_commit_for_bug_778318 = true;
  // We need the provider to be non-null, otherwise Blink crashes, even
  // though the provider should not be used for any actual networking.
  navigation_params->service_worker_network_provider =
      ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
  // The synchronous about:blank commit should only happen when the frame is
  // currently showing the initial empty document. For iframes, all navigations
  // that happen on the initial empty document should result in replacement, we
  // must have set the `frame_load_type` to kReplaceCurrentItem. For main frames
  // there are still cases where we will append instead of replace, but the
  // browser already expects this case.
  // TODO(crbug.com/40184245): Ensure main frame cases always do
  // replacement too.
  DCHECK(IsMainFrame() || navigation_params->frame_load_type ==
                              WebFrameLoadType::kReplaceCurrentItem);

  // This corresponds to steps 3 and 20 of
  // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context,
  // which sets the new Document's `referrer` member to the initiator frame's
  // full unredacted URL, in the case of new browsing context creation.
  //
  // The initiator might no longer exist however, in which case we cannot get
  // its document's full URL to use as the referrer.
  if (info->initiator_frame_token.has_value() &&
      WebFrame::FromFrameToken(info->initiator_frame_token.value())) {
    WebFrame* initiator =
        WebFrame::FromFrameToken(info->initiator_frame_token.value());
    DCHECK(initiator->IsWebLocalFrame());
    navigation_params->referrer =
        initiator->ToWebLocalFrame()->GetDocument().Url().GetString();
  }

  // To prevent pages from being able to abuse window.open() to determine the
  // system entropy, always set a fixed value of 'normal', for consistency with
  // other top-level navigations.
  if (IsMainFrame()) {
    navigation_params->navigation_timings.system_entropy_at_navigation_start =
        blink::mojom::SystemEntropy::kNormal;
  } else {
    // Sub frames always have an empty entropy state since they are generally
    // renderer-initiated. See
    // https://docs.google.com/document/d/1D6DqptsCEd3wPRsZ0q1iwVBAXXmhxZuLV-KKFI0ptCg/edit?usp=sharing
    // for background.
    DCHECK_EQ(blink::mojom::SystemEntropy::kEmpty,
              navigation_params->navigation_timings
                  .system_entropy_at_navigation_start);
  }

  frame_->CommitNavigation(std::move(navigation_params), BuildDocumentState());
}

// mojom::MhtmlFileWriter implementation
// ----------------------------------------

void RenderFrameImpl::SerializeAsMHTML(mojom::SerializeAsMHTMLParamsPtr params,
                                       SerializeAsMHTMLCallback callback) {
  TRACE_EVENT0("page-serialization", "RenderFrameImpl::SerializeAsMHTML");

  // Unpack payload.
  const WebString mhtml_boundary =
      WebString::FromUTF8(params->mhtml_boundary_marker);
  DCHECK(!mhtml_boundary.IsEmpty());

  // Holds WebThreadSafeData instances for some or all of header, contents and
  // footer.
  std::vector<WebThreadSafeData> mhtml_contents;
  auto delegate =
      std::make_unique<MHTMLPartsGenerationDelegateImpl>(std::move(params));

  // Generate MHTML header if needed.
  if (IsMainFrame()) {
    TRACE_EVENT0("page-serialization",
                 "RenderFrameImpl::SerializeAsMHTML header");
    // The returned data can be empty if the main frame should be skipped. If
    // the main frame is skipped, then the whole archive is bad.
    mhtml_contents.emplace_back(WebFrameSerializer::GenerateMHTMLHeader(
        mhtml_boundary, GetWebFrame(), delegate.get()));
  }

  // Generate MHTML parts.  Note that if this is not the main frame, then even
  // skipping the whole parts generation step is not an error - it simply
  // results in an omitted resource in the final file.
  TRACE_EVENT0("page-serialization",
               "RenderFrameImpl::SerializeAsMHTML parts serialization");
  MHTMLPartsGenerationDelegateImpl* delegate_ptr = delegate.get();
  WebFrameSerializer::GenerateMHTMLParts(
      mhtml_boundary, GetWebFrame(), delegate_ptr,
      base::BindOnce(&RenderFrameImpl::OnSerializeMHTMLComplete,
                     weak_factory_.GetWeakPtr(), std::move(delegate),
                     std::move(callback), std::move(mhtml_contents)));
}

void RenderFrameImpl::OnSerializeMHTMLComplete(
    std::unique_ptr<MHTMLPartsGenerationDelegateImpl> delegate,
    SerializeAsMHTMLCallback callback,
    std::vector<blink::WebThreadSafeData> mhtml_contents,
    blink::WebThreadSafeData frame_mhtml_data) {
  TRACE_EVENT0("page-serialization",
               "RenderFrameImpl::SerializeAsMHTML parts serialization");
  // The returned data can be empty if the frame should be skipped, but this
  // is OK.
  mhtml_contents.emplace_back(frame_mhtml_data);
  bool has_some_data = false;
  for (const auto& c : mhtml_contents) {
    if (!c.IsEmpty()) {
      has_some_data = true;
      break;
    }
  }

  // Note: the MHTML footer is written by the browser process, after the last
  // frame is serialized by a renderer process.

  MHTMLHandleWriterDelegate handle_delegate(
      *delegate->TakeParams(),
      base::BindOnce(&RenderFrameImpl::OnWriteMHTMLComplete,
                     weak_factory_.GetWeakPtr(), std::move(callback),
                     delegate->TakeSerializedResourcesUriDigests()),
      GetTaskRunner(blink::TaskType::kInternalDefault));

  if (has_some_data) {
    handle_delegate.WriteContents(mhtml_contents);
  } else {
    handle_delegate.Finish(mojom::MhtmlSaveStatus::kSuccess);
  }
}

void RenderFrameImpl::OnWriteMHTMLComplete(
    SerializeAsMHTMLCallback callback,
    std::unordered_set<std::string> serialized_resources_uri_digests,
    mojom::MhtmlSaveStatus save_status) {
  TRACE_EVENT1("page-serialization", "RenderFrameImpl::OnWriteMHTMLComplete",
               "frame save status", save_status);
  DCHECK(RenderThread::IsMainThread())
      << "Must run in the main renderer thread";

  // Convert the set into a vector for transport.
  std::vector<std::string> digests_of_new_parts(
      std::make_move_iterator(serialized_resources_uri_digests.begin()),
      std::make_move_iterator(serialized_resources_uri_digests.end()));

  // Notify the browser process about completion using the callback.
  std::move(callback).Run(save_status, std::move(digests_of_new_parts));
}

#ifndef STATIC_ASSERT_ENUM
#define STATIC_ASSERT_ENUM(a, b)                            \
  static_assert(static_cast<int>(a) == static_cast<int>(b), \
                "mismatching enums: " #a)
#undef STATIC_ASSERT_ENUM
#endif

void RenderFrameImpl::RequestOverlayRoutingToken(
    media::RoutingTokenCallback callback) {
  std::move(callback).Run(GetWebFrame()->GetFrameToken().value());
}

void RenderFrameImpl::OpenURL(std::unique_ptr<blink::WebNavigationInfo> info) {
  // A valid RequestorOrigin is always expected to be present.
  DCHECK(!info->url_request.RequestorOrigin().IsNull());

  WebNavigationPolicy policy = info->navigation_policy;
  auto params = blink::mojom::OpenURLParams::New();
  params->url = info->url_request.Url();
  params->initiator_origin = info->url_request.RequestorOrigin();
  if (info->requestor_base_url.IsValid()) {
    params->initiator_base_url = info->requestor_base_url;
  }
  params->post_body = blink::GetRequestBodyForWebURLRequest(info->url_request);
  DCHECK_EQ(!!params->post_body, IsHttpPost(info->url_request));
  params->extra_headers =
      blink::GetWebURLRequestHeadersAsString(info->url_request).Latin1();

  params->referrer = blink::mojom::Referrer::New(
      blink::WebStringToGURL(info->url_request.ReferrerString()),
      info->url_request.GetReferrerPolicy());
  params->disposition = NavigationPolicyToDisposition(policy);
  params->triggering_event_info = info->triggering_event_info;
  params->blob_url_token = CloneBlobURLToken(info->blob_url_token);
  params->should_replace_current_entry =
      info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem &&
      GetWebView()->HistoryBackListCount() +
          GetWebView()->HistoryForwardListCount() + 1;
  params->user_gesture = info->has_transient_user_activation;
  params->is_unfenced_top_navigation = info->is_unfenced_top_navigation;
  params->initiator_navigation_state_keep_alive_handle =
      std::move(info->initiator_navigation_state_keep_alive_handle);

  params->initiator_frame_token = info->initiator_frame_token;

  // TODO(antoniosartori): Consider plumbing in the source location also for
  // navigations performed via OpenURL.
  params->source_location = network::mojom::SourceLocation::New();

  params->impression = info->impression;

  if (GetContentClient()->renderer()->AllowPopup())
    params->user_gesture = true;

  // A main frame navigation should already have consumed an activation in
  // FrameLoader::StartNavigation.
  DCHECK(!is_main_frame_ || !frame_->HasTransientUserActivation());
  if (!is_main_frame_ &&
      (policy == blink::kWebNavigationPolicyNewBackgroundTab ||
       policy == blink::kWebNavigationPolicyNewForegroundTab ||
       policy == blink::kWebNavigationPolicyNewWindow ||
       policy == blink::kWebNavigationPolicyNewPopup ||
       policy == blink::kWebNavigationPolicyPictureInPicture)) {
    frame_->ConsumeTransientUserActivation();
  }

  params->href_translate = info->href_translate.Latin1();

  bool current_frame_has_download_sandbox_flag = !frame_->IsAllowedToDownload();
  bool has_download_sandbox_flag =
      info->initiator_frame_has_download_sandbox_flag ||
      current_frame_has_download_sandbox_flag;
  bool from_ad = info->initiator_frame_is_ad || frame_->IsAdFrame();

  params->download_policy.ApplyDownloadFramePolicy(
      info->is_opener_navigation, info->url_request.HasUserGesture(),
      info->url_request.RequestorOrigin().CanAccess(
          frame_->GetSecurityOrigin()),
      has_download_sandbox_flag, from_ad);

  params->initiator_activation_and_ad_status =
      blink::GetNavigationInitiatorActivationAndAdStatus(
          info->url_request.HasUserGesture(), info->initiator_frame_is_ad,
          info->is_ad_script_in_stack);

  params->has_rel_opener = info->has_rel_opener;

  GetFrameHost()->OpenURL(std::move(params));
}

void RenderFrameImpl::SetLoaderFactoryBundle(
    scoped_refptr<blink::ChildURLLoaderFactoryBundle> loader_factories) {
  // `background_resource_fetch_context_` will be lazy initialized the first
  // time MaybeGetBackgroundResourceFetchAssets() is called if
  // BackgroundResourceFetch feature is enabled.
  background_resource_fetch_context_.reset();
  loader_factories_ = std::move(loader_factories);
}

blink::ChildURLLoaderFactoryBundle* RenderFrameImpl::GetLoaderFactoryBundle() {
  // GetLoaderFactoryBundle should not be called before `loader_factories_` have
  // been set up - before a document is committed (e.g. before a navigation
  // commits or the initial empty document commits) it is not possible to 1)
  // trigger subresource loads, or 2) trigger propagation of the factories into
  // a new frame.
  DCHECK(loader_factories_);

  return loader_factories_.get();
}

scoped_refptr<blink::ChildURLLoaderFactoryBundle>
RenderFrameImpl::CreateLoaderFactoryBundle(
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle> info,
    std::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
        subresource_overrides,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        subresource_proxying_loader_factory,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>
        keep_alive_loader_factory,
    mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
        fetch_later_loader_factory) {
  DCHECK(info);
  // We don't check `DCHECK(info->pending_default_factory())`, because it will
  // be missing for speculative frames (and in other cases where no subresource
  // loads are expected - e.g. in test frames created via RenderViewTest).  See
  // also the DCHECK in URLLoaderFactoryBundle::GetFactory.

  // If resource metadata is present, construct an in-process resource loader
  // and have it intercept requests it may be able to service.
  info = MaybeSetUpLocalResourceLoader(std::move(info));

  auto loader_factories =
      base::MakeRefCounted<blink::HostChildURLLoaderFactoryBundle>(
          GetTaskRunner(blink::TaskType::kInternalLoading));

  loader_factories->Update(
      std::make_unique<blink::ChildPendingURLLoaderFactoryBundle>(
          std::move(info)));

  if (subresource_overrides) {
    loader_factories->UpdateSubresourceOverrides(&*subresource_overrides);
  }
  if (subresource_proxying_loader_factory) {
    loader_factories->SetSubresourceProxyingLoaderFactory(
        std::move(subresource_proxying_loader_factory));
  }
  if (keep_alive_loader_factory) {
    loader_factories->SetKeepAliveLoaderFactory(
        std::move(keep_alive_loader_factory));
  }
  if (base::FeatureList::IsEnabled(blink::features::kFetchLaterAPI) &&
      fetch_later_loader_factory) {
    loader_factories->SetFetchLaterLoaderFactory(
        std::move(fetch_later_loader_factory));
  }

  return loader_factories;
}

void RenderFrameImpl::UpdateEncoding(WebFrame* frame,
                                     const std::string& encoding_name) {
  // Only update main frame's encoding_name.
  if (!frame->Parent()) {
    // TODO(crbug.com/40188381): Move `UpdateEncoding()` to the
    // `LocalMainFrameHost` interface where it makes sense. That is not a simple
    // as just migrating the method, since upon main frame creation we attempt
    // to send the encoding information before
    // `WebViewImpl::local_main_frame_host_remote_` is set-up, which breaks
    // things.`
    GetFrameHost()->UpdateEncoding(encoding_name);
  }
}

void RenderFrameImpl::SyncSelectionIfRequired(blink::SyncCondition force_sync) {
  std::u16string text;
  size_t offset;
  gfx::Range range;
#if BUILDFLAG(ENABLE_PPAPI)
  if (focused_pepper_plugin_) {
    focused_pepper_plugin_->GetSurroundingText(&text, &range);
    offset = 0;  // Pepper API does not support offset reporting.
    // TODO(kinaba): cut as needed.
  } else
#endif
  {
    WebRange selection =
        frame_->GetInputMethodController()->GetSelectionOffsets();
    if (selection.IsNull())
      return;

    range = gfx::Range(selection.StartOffset(), selection.EndOffset());

    if (frame_->GetInputMethodController()->TextInputType() !=
        blink::kWebTextInputTypeNone) {
      // If current focused element is editable, we will send 100 more chars
      // before and after selection. It is for input method surrounding text
      // feature.
      if (selection.StartOffset() > kExtraCharsBeforeAndAfterSelection)
        offset = selection.StartOffset() - kExtraCharsBeforeAndAfterSelection;
      else
        offset = 0;
      size_t length =
          selection.EndOffset() - offset + kExtraCharsBeforeAndAfterSelection;
      text = frame_->RangeAsText(WebRange(offset, length)).Utf16();
    } else {
      offset = selection.StartOffset();
      text = frame_->SelectionAsText().Utf16();
      // http://crbug.com/101435
      // In some case, frame->selectionAsText() returned text's length is not
      // equal to the length returned from frame_->GetSelectionOffsets(). So we
      // have to set the range according to text.length().
      range.set_end(range.start() + text.length());
    }
  }

  // TODO(dglazkov): Investigate if and why this would be happening,
  // and resolve this. We shouldn't be carrying selection text here.
  // http://crbug.com/632920.
  // Sometimes we get repeated didChangeSelection calls from webkit when
  // the selection hasn't actually changed. We don't want to report these
  // because it will cause us to continually claim the X clipboard.
  if (force_sync == blink::SyncCondition::kForced ||
      selection_text_offset_ != offset || selection_range_ != range ||
      selection_text_ != text) {
    selection_text_ = text;
    selection_text_offset_ = offset;
    selection_range_ = range;
    SetSelectedText(text, offset, range);
  }
  GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}

void RenderFrameImpl::CreateAudioInputStream(
    blink::CrossVariantMojoRemote<
        blink::mojom::RendererAudioInputStreamFactoryClientInterfaceBase>
        client,
    const base::UnguessableToken& session_id,
    const media::AudioParameters& params,
    bool automatic_gain_control,
    uint32_t shared_memory_count,
    blink::CrossVariantMojoReceiver<
        media::mojom::AudioProcessorControlsInterfaceBase> controls_receiver,
    const media::AudioProcessingSettings* settings) {
  DCHECK_EQ(!!settings, !!controls_receiver);
  media::mojom::AudioProcessingConfigPtr processing_config;
  if (controls_receiver) {
    DCHECK(settings);
    processing_config = media::mojom::AudioProcessingConfig::New(
        std::move(controls_receiver), *settings);
  }

  GetAudioInputStreamFactory()->CreateStream(
      std::move(client), session_id, params, automatic_gain_control,
      shared_memory_count, std::move(processing_config));
}

void RenderFrameImpl::AssociateInputAndOutputForAec(
    const base::UnguessableToken& input_stream_id,
    const std::string& output_device_id) {
  GetAudioInputStreamFactory()->AssociateInputAndOutputForAec(input_stream_id,
                                                              output_device_id);
}

void RenderFrameImpl::InitializeMediaStreamDeviceObserver() {
  RenderThreadImpl* render_thread = RenderThreadImpl::current();
  if (!render_thread)  // Will be NULL during unit tests.
    return;

  DCHECK(!web_media_stream_device_observer_);
  web_media_stream_device_observer_ =
      std::make_unique<blink::WebMediaStreamDeviceObserver>(GetWebFrame());
}

void RenderFrameImpl::BeginNavigationInternal(
    std::unique_ptr<blink::WebNavigationInfo> info,
    bool is_history_navigation_in_new_child_frame,
    base::TimeTicks renderer_before_unload_start,
    base::TimeTicks renderer_before_unload_end) {
  // Provisional frames shouldn't initiate navigations.
  CHECK(!GetWebFrame()->IsProvisional());

  if (!frame_->WillStartNavigation(*info))
    return;

  for (auto& observer : observers_)
    observer.DidStartNavigation(info->url_request.Url(), info->navigation_type);
  is_requesting_navigation_ = true;

  // Set SiteForCookies.
  WebDocument frame_document = frame_->GetDocument();
  if (info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel) {
    info->url_request.SetSiteForCookies(
        net::SiteForCookies::FromUrl(info->url_request.Url()));
  } else {
    info->url_request.SetSiteForCookies(frame_document.SiteForCookies());
  }

  ui::PageTransition transition_type = GetTransitionType(
      ui::PAGE_TRANSITION_LINK,
      info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
      IsMainFrame(), GetWebView()->IsFencedFrameRoot(), info->navigation_type);
  if (info->is_client_redirect) {
    transition_type = ui::PageTransitionFromInt(
        transition_type | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
  }

  // Note: At this stage, the goal is to apply all the modifications the
  // renderer wants to make to the request, and then send it to the browser, so
  // that the actual network request can be started. Ideally, all such
  // modifications should take place in WillSendRequestInternal, and in the
  // implementation of willSendRequest for the various InspectorAgents
  // (devtools).
  //
  // TODO(clamy): Apply devtools override.
  // TODO(clamy): Make sure that navigation requests are not modified somewhere
  // else in blink.
  bool for_outermost_main_frame = frame_->IsOutermostMainFrame();
  {
    // upstream_url can be empty here because ForRedirect(false) implies that
    // this isn't a browser initiated navigation.
    std::optional<blink::WebURL> adjusted_request_url = WillSendRequestInternal(
        info->url_request.Url(), info->url_request.RequestorOrigin(),
        info->url_request.SiteForCookies(), ForRedirect(false),
        /*upstream_url=*/GURL(), transition_type);
    if (adjusted_request_url.has_value()) {
      info->url_request.SetUrl(adjusted_request_url.value());
    }
  }
  FinalizeRequestInternal(info->url_request, for_outermost_main_frame,
                          transition_type);
  // The extra data was created in WillSendRequestInternal if it didn't exist.
  DCHECK(info->url_request.GetURLRequestExtraData());

  // These values are assumed on the browser side for navigations. These checks
  // ensure the renderer has the correct values.
  DCHECK_EQ(network::mojom::RequestMode::kNavigate,
            info->url_request.GetMode());
  DCHECK_EQ(network::mojom::CredentialsMode::kInclude,
            info->url_request.GetCredentialsMode());
  DCHECK_EQ(network::mojom::RedirectMode::kManual,
            info->url_request.GetRedirectMode());
  DCHECK(frame_->Parent() ||
         info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel);
  DCHECK(!frame_->Parent() ||
         info->frame_type == blink::mojom::RequestContextFrameType::kNested);

  bool is_form_submission =
      (info->navigation_type == blink::kWebNavigationTypeFormSubmitted ||
       info->navigation_type ==
           blink::kWebNavigationTypeFormResubmittedBackForward ||
       info->navigation_type == blink::kWebNavigationTypeFormResubmittedReload);

  bool was_initiated_by_link_click =
      info->navigation_type == blink::kWebNavigationTypeLinkClicked;

  GURL searchable_form_url;
  std::string searchable_form_encoding;
  if (!info->form.IsNull()) {
    WebSearchableFormData web_searchable_form_data(info->form);
    searchable_form_url = web_searchable_form_data.Url();
    searchable_form_encoding = web_searchable_form_data.Encoding().Utf8();
  }

  GURL client_side_redirect_url;
  if (info->is_client_redirect)
    client_side_redirect_url = frame_->GetDocument().Url();

  mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token(
      CloneBlobURLToken(info->blob_url_token));

  int load_flags = info->url_request.GetLoadFlagsForWebUrlRequest();
  std::optional<base::Value::Dict> devtools_initiator;
  if (!info->devtools_initiator_info.IsNull()) {
    std::optional<base::Value> devtools_initiator_value =
        base::JSONReader::Read(info->devtools_initiator_info.Utf8());
    if (devtools_initiator_value && devtools_initiator_value->is_dict()) {
      devtools_initiator = std::move(*devtools_initiator_value).TakeDict();
    }
  }

  blink::mojom::NavigationInitiatorActivationAndAdStatus
      initiator_activation_and_ad_status =
          blink::GetNavigationInitiatorActivationAndAdStatus(
              info->url_request.HasUserGesture(), info->initiator_frame_is_ad,
              info->is_ad_script_in_stack);

  blink::mojom::BeginNavigationParamsPtr begin_params =
      blink::mojom::BeginNavigationParams::New(
          info->initiator_frame_token,
          blink::GetWebURLRequestHeadersAsString(info->url_request).Latin1(),
          load_flags, info->url_request.GetSkipServiceWorker(),
          blink::GetRequestContextTypeForWebURLRequest(info->url_request),
          blink::GetMixedContentContextTypeForWebURLRequest(info->url_request),
          is_form_submission, was_initiated_by_link_click,
          info->force_history_push, searchable_form_url,
          searchable_form_encoding, client_side_redirect_url,
          std::move(devtools_initiator),
          info->url_request.TrustTokenParams()
              ? info->url_request.TrustTokenParams()->Clone()
              : nullptr,
          info->impression, renderer_before_unload_start,
          renderer_before_unload_end, initiator_activation_and_ad_status,
          info->is_container_initiated, info->storage_access_api_status,
          info->has_rel_opener);

  bool current_frame_has_download_sandbox_flag = !frame_->IsAllowedToDownload();
  bool has_download_sandbox_flag =
      info->initiator_frame_has_download_sandbox_flag ||
      current_frame_has_download_sandbox_flag;
  bool from_ad = info->initiator_frame_is_ad || frame_->IsAdFrame();

  mojo::PendingRemote<blink::mojom::NavigationStateKeepAliveHandle>
      initiator_navigation_state_keep_alive_handle =
          std::move(info->initiator_navigation_state_keep_alive_handle);

  network::mojom::RequestDestination request_destination =
      blink::GetRequestDestinationForWebURLRequest(info->url_request);

  blink::mojom::CommonNavigationParamsPtr common_params =
      MakeCommonNavigationParams(frame_->GetSecurityOrigin(), std::move(info),
                                 load_flags, has_download_sandbox_flag, from_ad,
                                 is_history_navigation_in_new_child_frame,
                                 request_destination);

  bool is_duplicate_navigation = false;
  base::TimeDelta nav_start_diff;

  if (navigation_client_impl_ &&
      navigation_client_impl_->HasBeginNavigationParams()) {
    // We ignore navigations that are identical to the ongoing navigation. This
    // is because the navigation is likely to be accidental (e.g. user clicked
    // the same link multiple times, etc).
    auto& prev_begin_params = navigation_client_impl_->begin_params();
    auto& prev_common_params = navigation_client_impl_->common_params();
    if (begin_params->was_initiated_by_link_click ==
            prev_begin_params.was_initiated_by_link_click &&
        common_params->url == prev_common_params.url &&
        common_params->method == "GET" && prev_common_params.method == "GET" &&
        common_params->initiator_origin ==
            prev_common_params.initiator_origin &&
        common_params->has_user_gesture ==
            prev_common_params.has_user_gesture &&
        common_params->referrer == prev_common_params.referrer &&
        common_params->transition == prev_common_params.transition &&
        common_params->should_replace_current_entry ==
            prev_common_params.should_replace_current_entry &&
        begin_params->headers == prev_begin_params.headers &&
        begin_params->has_rel_opener == prev_begin_params.has_rel_opener) {
      is_duplicate_navigation = true;
      nav_start_diff = (common_params->navigation_start -
                        prev_common_params.navigation_start);
    }
  }
  base::UmaHistogramBoolean(
      "Navigation.RendererInitiated.IsDuplicateWithoutThresholdCheck",
      is_duplicate_navigation);
  if (is_duplicate_navigation) {
    // The navigation is similar to a previous navigation. Check if it's started
    // close enough to the start of the previous navigation, in which case we
    // can just ignore the new navigation and keep the previous navigation.
    bool start_diff_under_threshold =
        (nav_start_diff <= features::kDuplicateNavThreshold.Get());
    base::UmaHistogramBoolean(
        "Navigation.RendererInitiated.DuplicateNavIsUnderThreshold",
        start_diff_under_threshold);
    base::UmaHistogramTimes(
        "Navigation.RendererInitiated.DuplicateNavStartTimeDiff",
        nav_start_diff);
    if (start_diff_under_threshold &&
        base::FeatureList::IsEnabled(features::kIgnoreDuplicateNavs)) {
      return;
    }
  }

  mojo::PendingAssociatedRemote<mojom::NavigationClient>
      navigation_client_remote;
  BindNavigationClientWithParams(
      navigation_client_remote.InitWithNewEndpointAndPassReceiver(),
      begin_params.Clone(), common_params.Clone(), is_duplicate_navigation);
  mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
      renderer_cancellation_listener_receiver;
  navigation_client_impl_->SetUpRendererInitiatedNavigation(
      renderer_cancellation_listener_receiver.InitWithNewPipeAndPassRemote());
  GetFrameHost()->BeginNavigation(
      std::move(common_params), std::move(begin_params),
      std::move(blob_url_token), std::move(navigation_client_remote),
      std::move(initiator_navigation_state_keep_alive_handle),
      std::move(renderer_cancellation_listener_receiver));
}

void RenderFrameImpl::DecodeDataURL(
    const blink::mojom::CommonNavigationParams& common_params,
    const blink::mojom::CommitNavigationParams& commit_params,
    std::string* mime_type,
    std::string* charset,
    std::string* data,
    GURL* base_url) {
  // A loadData request with a specified base URL.
  GURL data_url = common_params.url;
#if BUILDFLAG(IS_ANDROID)
  if (!commit_params.data_url_as_string.empty()) {
#if DCHECK_IS_ON()
    {
      std::string mime_type_tmp, charset_tmp, data_tmp;
      DCHECK(net::DataURL::Parse(data_url, &mime_type_tmp, &charset_tmp,
                                 &data_tmp));
      DCHECK(data_tmp.empty());
    }
#endif
    // If `data_url_as_string` is set, the `url` in CommonNavigationParams will
    // only contain the data: URL header and won't contain any actual data (see
    // NavigationControllerAndroid::LoadUrl()), so we should use
    // `data_url_as_string` if possible.
    data_url = GURL(commit_params.data_url_as_string);
    if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme)) {
      // If the given data URL is invalid, use the data: URL header as a
      // fallback.
      data_url = common_params.url;
    }
  }
#endif
  // Parse the given data, then set the `base_url`, which is used as the
  // document URL.
  if (net::DataURL::Parse(data_url, mime_type, charset, data)) {
    // Since the base URL will also be used as the document URL, we should not
    // use an empty URL. If it's empty, use the data: URL as a fallback.
    // TODO(crbug.com/40187599): Maybe this should consider
    // `data_url_as_string` too. Otherwise, the base URL might be set to the
    // data: URL header with empty data, instead of the data: URL that contains
    // the actual data.
    *base_url = common_params.base_url_for_data_url.is_empty()
                    ? common_params.url
                    : common_params.base_url_for_data_url;
  } else {
    NOTREACHED() << "Invalid URL passed: "
                 << common_params.url.possibly_invalid_spec();
  }
}

void RenderFrameImpl::SendUpdateState() {
  // Since we are sending immediately we can cancel any pending delayed sync
  // timer.
  delayed_state_sync_timer_.Stop();
  if (GetWebFrame()->GetCurrentHistoryItem().IsNull())
    return;

  GetFrameHost()->UpdateState(GetWebFrame()->CurrentHistoryItemToPageState());
}

std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
RenderFrameImpl::MaybeSetUpLocalResourceLoader(
    std::unique_ptr<blink::PendingURLLoaderFactoryBundle> factory_bundle) {
  if (!factory_bundle->local_resource_loader_config()) {
    return factory_bundle;
  }

  // The pipe to the browser-side WebUIURLLoaderFactory could be in one of two
  // places:
  //
  // 1. factory_bundle->pending_default_factory()
  // 2. factory_bundle->scheme_specific_factories
  //
  // (This may change in the future, see https://crbug.com/40091019)
  //
  // The browser process only sends the config in the first case (see call to
  // PendingURLLoaderFactoryBundle::set_local_resource_loader_config in
  // RenderFrameHostImpl::CommitNavigation), so if we're constructing a new
  // in-process resource loader we always take that as the fallback.

  // Take the existing PendingRemote and make it the fallback for a new
  // in-process resource loader.
  local_resource_loader_.emplace(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
      std::move(factory_bundle->local_resource_loader_config()),
      std::move(factory_bundle->pending_default_factory()));
  // Create a pipe and pass the receiving end to the new in-process loader.
  mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
  local_resource_loader_
      .AsyncCall(&content::LocalResourceURLLoaderFactory::Clone)
      .WithArgs(pending_remote.InitWithNewPipeAndPassReceiver());
  // Swap in the new PendingRemote.
  factory_bundle->pending_default_factory() = std::move(pending_remote);

  return factory_bundle;
}

blink::WebURL RenderFrameImpl::LastCommittedUrlForUKM() {
  return GetLoadingUrl();
}

// Returns the "loading URL", which might be different than the "document URL"
// used in the DocumentLoader in some cases:
// - For error pages, it will return the URL that failed to load, instead of the
// chrome-error:// URL used as the document URL in the DocumentLoader.
// - For loadDataWithBaseURL() navigations, it will return the data: URL
// used to commit, instead of the "base URL" used as the document URL in the
// DocumentLoader. See comments in  BuildDocumentStateFromParams() and
// in navigation_params.mojom's `is_load_data_with_base_url` for more details.
GURL RenderFrameImpl::GetLoadingUrl() const {
  WebDocumentLoader* document_loader = frame_->GetDocumentLoader();

  GURL overriden_url;
  if (MaybeGetOverriddenURL(document_loader, &overriden_url))
    return overriden_url;

  return document_loader->GetUrl();
}

media::MediaPermission* RenderFrameImpl::GetMediaPermission() {
  if (!media_permission_dispatcher_) {
    media_permission_dispatcher_ =
        std::make_unique<MediaPermissionDispatcher>(this);
  }
  return media_permission_dispatcher_.get();
}

void RenderFrameImpl::RegisterMojoInterfaces() {
  // TODO(dcheng): Fold this interface into mojom::Frame.
  GetAssociatedInterfaceRegistry()
      ->AddInterface<blink::mojom::AutoplayConfigurationClient>(
          base::BindRepeating(&RenderFrameImpl::BindAutoplayConfiguration,
                              weak_factory_.GetWeakPtr()));

  GetAssociatedInterfaceRegistry()->AddInterface<mojom::FrameBindingsControl>(
      base::BindRepeating(&RenderFrameImpl::BindFrameBindingsControl,
                          weak_factory_.GetWeakPtr()));

  GetAssociatedInterfaceRegistry()->AddInterface<mojom::NavigationClient>(
      base::BindRepeating(&RenderFrameImpl::BindNavigationClient,
                          weak_factory_.GetWeakPtr()));

  // TODO(dcheng): Fold this interface into mojom::Frame.
  GetAssociatedInterfaceRegistry()->AddInterface<mojom::MhtmlFileWriter>(
      base::BindRepeating(&RenderFrameImpl::BindMhtmlFileWriter,
                          base::Unretained(this)));

  GetAssociatedInterfaceRegistry()
      ->AddInterface<blink::mojom::RenderAccessibility>(base::BindRepeating(
          &RenderAccessibilityManager::BindReceiver,
          base::Unretained(render_accessibility_manager_.get())));

#if BUILDFLAG(IS_ANDROID)
  GetAssociatedInterfaceRegistry()->AddInterface<mojom::GinJavaBridge>(
      base::BindRepeating(&RenderFrameImpl::BindGinJavaBridge,
                          weak_factory_.GetWeakPtr()));
#endif
}

#if BUILDFLAG(IS_ANDROID)
void RenderFrameImpl::BindGinJavaBridge(
    mojo::PendingAssociatedReceiver<mojom::GinJavaBridge> receiver) {
  mojo::MakeSelfOwnedAssociatedReceiver(
      std::make_unique<GinJavaBridgeDispatcher>(this), std::move(receiver));
}
#endif

void RenderFrameImpl::BindMhtmlFileWriter(
    mojo::PendingAssociatedReceiver<mojom::MhtmlFileWriter> receiver) {
  mhtml_file_writer_receiver_.reset();
  mhtml_file_writer_receiver_.Bind(
      std::move(receiver), GetTaskRunner(blink::TaskType::kInternalDefault));
}

scoped_refptr<network::SharedURLLoaderFactory>
RenderFrameImpl::GetURLLoaderFactory() {
  if (!RenderThreadImpl::current()) {
    // Some tests (e.g. RenderViewTests) do not have RenderThreadImpl,
    // and must create a factory override instead.
    if (url_loader_factory_override_for_test_) {
      return url_loader_factory_override_for_test_;
    }

    // If the override does not exist, try looking in the ancestor chain since
    // we might have created child frames and asked them to create a URL loader
    // factory.
    for (auto* ancestor = GetWebFrame()->Parent(); ancestor;
         ancestor = ancestor->Parent()) {
      RenderFrameImpl* ancestor_frame = RenderFrameImpl::FromWebFrame(ancestor);
      if (ancestor_frame &&
          ancestor_frame->url_loader_factory_override_for_test_) {
        return ancestor_frame->url_loader_factory_override_for_test_;
      }
    }
    // At this point we can't create anything.
    NOTREACHED();
  }
  return GetLoaderFactoryBundle();
}

blink::URLLoaderThrottleProvider*
RenderFrameImpl::GetURLLoaderThrottleProvider() {
  RenderThreadImpl* render_thread = RenderThreadImpl::current();
  // The RenderThreadImpl may not be valid in some tests.
  return render_thread ? render_thread->url_loader_throttle_provider()
                       : nullptr;
}

scoped_refptr<blink::WebBackgroundResourceFetchAssets>
RenderFrameImpl::MaybeGetBackgroundResourceFetchAssets() {
  if (!base::FeatureList::IsEnabled(
          blink::features::kBackgroundResourceFetch)) {
    return nullptr;
  }

  if (!background_resource_fetch_context_) {
    if (!background_resource_fetch_task_runner_) {
      background_resource_fetch_task_runner_ =
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::TaskPriority::USER_BLOCKING});
    }
    background_resource_fetch_context_ =
        base::MakeRefCounted<BackgroundResourceFetchAssets>(
            GetLoaderFactoryBundle()->Clone(),
            GetURLLoaderThrottleProvider()
                ? GetURLLoaderThrottleProvider()->Clone()
                : nullptr,
            background_resource_fetch_task_runner_,
            frame_->GetLocalFrameToken());
  }
  return background_resource_fetch_context_;
}

void RenderFrameImpl::OnStopLoading() {
  for (auto& observer : observers_)
    observer.OnStop();
}

bool RenderFrameImpl::IsRequestingNavigation() {
  return is_requesting_navigation_;
}

void RenderFrameImpl::LoadHTMLStringForTesting(std::string_view html,
                                               const GURL& base_url,
                                               const std::string& text_encoding,
                                               const GURL& unreachable_url,
                                               bool replace_current_item) {
  AssertNavigationCommits assert_navigation_commits(
      this, kMayReplaceInitialEmptyDocument);

  mojo::PendingRemote<network::mojom::URLLoaderFactory>
      url_loader_factory_remote;
  if (url_loader_factory_override_for_test_) {
    url_loader_factory_override_for_test_->Clone(
        url_loader_factory_remote.InitWithNewPipeAndPassReceiver());
  } else {
    url_loader_factory_remote =
        network::NotImplementedURLLoaderFactory::Create();
  }

  pending_loader_factories_ = CreateLoaderFactoryBundle(
      blink::ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
          std::move(url_loader_factory_remote)),
      /*subresource_overrides=*/std::nullopt,
      /*subresource_proxying_loader_factory=*/{},
      /*keep_alive_loader_factory=*/{},
      /*fetch_later_loader_factory=*/{});

  auto navigation_params = std::make_unique<WebNavigationParams>();
  navigation_params->url = base_url;
  WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
                                          WebString::FromUTF8(text_encoding),
                                          html);
  navigation_params->unreachable_url = unreachable_url;
  navigation_params->frame_load_type =
      replace_current_item ? blink::WebFrameLoadType::kReplaceCurrentItem
                           : blink::WebFrameLoadType::kStandard;
  navigation_params->service_worker_network_provider =
      ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
  frame_->CommitNavigation(std::move(navigation_params), BuildDocumentState());
}

scoped_refptr<base::SingleThreadTaskRunner> RenderFrameImpl::GetTaskRunner(
    blink::TaskType task_type) {
  return GetWebFrame()->GetTaskRunner(task_type);
}

BindingsPolicySet RenderFrameImpl::GetEnabledBindings() {
  return enabled_bindings_;
}

void RenderFrameImpl::SetAccessibilityModeForTest(ui::AXMode new_mode) {
  render_accessibility_manager_->SetMode(new_mode, 1);
}

const RenderFrameMediaPlaybackOptions&
RenderFrameImpl::GetRenderFrameMediaPlaybackOptions() {
  return renderer_media_playback_options_;
}

void RenderFrameImpl::SetRenderFrameMediaPlaybackOptions(
    const RenderFrameMediaPlaybackOptions& opts) {
  renderer_media_playback_options_ = opts;
}

void RenderFrameImpl::SetAllowsCrossBrowsingInstanceFrameLookup() {
  GetWebFrame()->SetAllowsCrossBrowsingInstanceFrameLookup();
}

bool RenderFrameImpl::IsAccessibilityEnabled() const {
  return render_accessibility_manager_->GetAccessibilityMode().has_mode(
      ui::AXMode::kWebContents);
}

#if BUILDFLAG(ENABLE_PPAPI)

mojom::PepperHost* RenderFrameImpl::GetPepperHost() {
  if (!pepper_host_remote_.is_bound())
    GetRemoteAssociatedInterfaces()->GetInterface(&pepper_host_remote_);
  return pepper_host_remote_.get();
}

void RenderFrameImpl::PepperInstanceCreated(
    PepperPluginInstanceImpl* instance,
    mojo::PendingAssociatedRemote<mojom::PepperPluginInstance> mojo_instance,
    mojo::PendingAssociatedReceiver<mojom::PepperPluginInstanceHost>
        mojo_host) {
  active_pepper_instances_.insert(instance);
  GetPepperHost()->InstanceCreated(
      instance->pp_instance(), std::move(mojo_instance), std::move(mojo_host));
}

void RenderFrameImpl::PepperInstanceDeleted(
    PepperPluginInstanceImpl* instance) {
  active_pepper_instances_.erase(instance);

  if (focused_pepper_plugin_ == instance)
    PepperFocusChanged(instance, false);
}

void RenderFrameImpl::PepperFocusChanged(PepperPluginInstanceImpl* instance,
                                         bool focused) {
  if (focused)
    focused_pepper_plugin_ = instance;
  else if (focused_pepper_plugin_ == instance)
    focused_pepper_plugin_ = nullptr;

  GetLocalRootWebFrameWidget()->UpdateTextInputState();
  GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}

#endif  // BUILDFLAG(ENABLE_PPAPI)

std::unique_ptr<blink::WebSocketHandshakeThrottle>
RenderFrameImpl::CreateWebSocketHandshakeThrottle() {
  // Lazily create the provider.
  if (!websocket_handshake_throttle_provider_) {
    websocket_handshake_throttle_provider_ =
        GetContentClient()
            ->renderer()
            ->CreateWebSocketHandshakeThrottleProvider();
    if (!websocket_handshake_throttle_provider_)
      return nullptr;
  }

  return websocket_handshake_throttle_provider_->CreateThrottle(
      frame_->GetLocalFrameToken(),
      GetTaskRunner(blink::TaskType::kInternalDefault));
}

bool RenderFrameImpl::GetCaretBoundsFromFocusedPlugin(gfx::Rect& rect) {
#if BUILDFLAG(ENABLE_PPAPI)
  if (focused_pepper_plugin_) {
    rect = focused_pepper_plugin_->GetCaretBounds();
    return true;
  }
#endif
  return false;
}

void RenderFrameImpl::AddMessageToConsoleImpl(
    blink::mojom::ConsoleMessageLevel level,
    const std::string& message,
    bool discard_duplicates) {
  blink::WebConsoleMessage wcm(level, WebString::FromUTF8(message));
  frame_->AddMessageToConsole(wcm, discard_duplicates);
}

void RenderFrameImpl::SetURLLoaderFactoryOverrideForTest(
    scoped_refptr<network::SharedURLLoaderFactory> factory) {
  url_loader_factory_override_for_test_ = std::move(factory);
}

scoped_refptr<blink::ChildURLLoaderFactoryBundle>
RenderFrameImpl::CloneLoaderFactories() {
  auto pending_bundle = base::WrapUnique(
      static_cast<blink::TrackedChildPendingURLLoaderFactoryBundle*>(
          GetLoaderFactoryBundle()->Clone().release()));
  return base::MakeRefCounted<blink::TrackedChildURLLoaderFactoryBundle>(
      std::move(pending_bundle));
}

blink::scheduler::WebAgentGroupScheduler&
RenderFrameImpl::GetAgentGroupScheduler() {
  return agent_scheduling_group_->agent_group_scheduler();
}

url::Origin RenderFrameImpl::GetSecurityOriginOfTopFrame() {
  return frame_->Top()->GetSecurityOrigin();
}

base::WeakPtr<media::DecoderFactory> RenderFrameImpl::GetMediaDecoderFactory() {
  return media_factory_.GetDecoderFactory();
}

gfx::Rect RenderFrameImpl::ConvertViewportToWindow(const gfx::Rect& rect) {
  return GetLocalRootWebFrameWidget()->BlinkSpaceToEnclosedDIPs(rect);
}

float RenderFrameImpl::GetDeviceScaleFactor() {
  return GetLocalRootWebFrameWidget()->GetScreenInfo().device_scale_factor;
}

bool RenderFrameImpl::DeferMediaLoad(bool has_played_media_before,
                                     base::OnceClosure closure) {
  if (frame_->GetDocument().IsPrerendering()) {
    frame_->GetDocument().AddPostPrerenderingActivationStep(
        base::BindOnce(CallClientDeferMediaLoad, weak_factory_.GetWeakPtr(),
                       has_played_media_before, std::move(closure)));
    return true;
  }

  return GetContentClient()->renderer()->DeferMediaLoad(
      this, has_played_media_before, std::move(closure));
}

WebView* RenderFrameImpl::CreateNewWindow(
    const WebURLRequest& request,
    const blink::WebWindowFeatures& features,
    const WebString& frame_name,
    WebNavigationPolicy policy,
    network::mojom::WebSandboxFlags sandbox_flags,
    const blink::SessionStorageNamespaceId& session_storage_namespace_id,
    bool& consumed_user_gesture,
    const std::optional<blink::Impression>& impression,
    const std::optional<blink::WebPictureInPictureWindowOptions>& pip_options,
    const blink::WebURL& base_url) {
  consumed_user_gesture = false;
  mojom::CreateNewWindowParamsPtr params = mojom::CreateNewWindowParams::New();

  // The user activation check is done at the browser process through
  // |frame_host->CreateNewWindow()| call below.  But the extensions case
  // handled through the following |if| is an exception.
  params->allow_popup = false;
  if (GetContentClient()->renderer()->AllowPopup())
    params->allow_popup = true;

  params->window_container_type = WindowFeaturesToContainerType(features);

  params->session_storage_namespace_id = session_storage_namespace_id;
  if (!features.noopener) {
    params->clone_from_session_storage_namespace_id =
        GetWebView()->GetSessionStorageNamespaceId();
  }

  const std::string& frame_name_utf8 = frame_name.Utf8(
      WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
  params->frame_name = frame_name_utf8;
  params->opener_suppressed = features.noopener;
  params->disposition = NavigationPolicyToDisposition(policy);
  if (!request.IsNull()) {
    params->target_url = request.Url();
    // The browser process does not consider empty URLs as valid (partly due to
    // a risk of treating them as a navigation to the privileged NTP in some
    // cases), so treat an attempt to create a window with an empty URL as
    // opening about:blank.
    //
    // Similarly, javascript: URLs should not be sent to the browser process,
    // since they are either handled within the renderer process (if a window is
    // created within the same browsing context group) or ignored (in the
    // noopener case). Use about:blank for the URL in that case as well, to
    // reduce the risk of running them incorrectly.
    if (params->target_url.is_empty() ||
        params->target_url.SchemeIs(url::kJavaScriptScheme)) {
      params->target_url = GURL(url::kAboutBlankURL);
    }

    params->referrer = blink::mojom::Referrer::New(
        blink::WebStringToGURL(request.ReferrerString()),
        request.GetReferrerPolicy());
  }
  params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features);

  params->is_form_submission = request.IsFormSubmission();
  params->form_submission_post_data =
      blink::GetRequestBodyForWebURLRequest(request);
  params->form_submission_post_content_type = request.HttpContentType().Utf8();

  params->impression = impression;

  if (pip_options) {
    CHECK_EQ(policy, blink::kWebNavigationPolicyPictureInPicture);
    auto pip_mojom_opts = blink::mojom::PictureInPictureWindowOptions::New();
    pip_mojom_opts->width = pip_options->width;
    pip_mojom_opts->height = pip_options->height;
    pip_mojom_opts->disallow_return_to_opener =
        pip_options->disallow_return_to_opener;
    pip_mojom_opts->prefer_initial_window_placement =
        pip_options->prefer_initial_window_placement;
    params->pip_options = std::move(pip_mojom_opts);
  }

  params->download_policy.ApplyDownloadFramePolicy(
      /*is_opener_navigation=*/false, request.HasUserGesture(),
      // `openee_can_access_opener_origin` only matters for opener navigations,
      // so its value here is irrelevant.
      /*openee_can_access_opener_origin=*/true,
      !GetWebFrame()->IsAllowedToDownload(), GetWebFrame()->IsAdFrame());

  params->initiator_activation_and_ad_status =
      blink::GetNavigationInitiatorActivationAndAdStatus(
          request.HasUserGesture(), GetWebFrame()->IsAdFrame(),
          GetWebFrame()->IsAdScriptInStack());

  // We preserve this information before sending the message since |params| is
  // moved on send.
  bool is_background_tab =
      params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB;

  mojom::CreateNewWindowStatus status;
  mojom::CreateNewWindowReplyPtr reply;
  auto* frame_host = GetFrameHost();
  if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) {
    // The sync IPC failed, e.g. maybe the render process is in the middle of
    // shutting down. Can't create a new window without the browser process,
    // so just bail out.
    return nullptr;
  }

  // If creation of the window was blocked (e.g. because this frame doesn't
  // have user activation), return before consuming user activation. A frame
  // that isn't allowed to open a window  shouldn't be able to consume the
  // activation for the rest of the frame tree.
  if (status == mojom::CreateNewWindowStatus::kBlocked)
    return nullptr;

  // 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. We also don't need to consume user gestures to protect against
  // multiple windows being opened, because, well, the app doesn't support
  // multiple windows.
  // TODO(dcheng): It's awkward that this is plumbed into Blink but not really
  // used much in Blink, except to enable web testing... perhaps this should
  // be checked directly in the browser side.
  if (status == mojom::CreateNewWindowStatus::kReuse) {
    // In this case, treat javascript: URLs as blocked rather than running them
    // in a reused main frame in Android WebView. See https://crbug.com/1083819.
    if (!request.IsNull() && request.Url().ProtocolIs(url::kJavaScriptScheme)) {
      return nullptr;
    }
    return GetWebView();
  }

  // Consume the transient user activation in the current renderer.
  consumed_user_gesture = GetWebFrame()->ConsumeTransientUserActivation(
      blink::UserActivationUpdateSource::kBrowser);

  // If we should ignore the new window (e.g. because of `noopener`), return
  // now that user activation was consumed.
  if (status == mojom::CreateNewWindowStatus::kIgnore)
    return nullptr;

  DCHECK(reply);
  DCHECK_NE(MSG_ROUTING_NONE, reply->main_frame_route_id);
  DCHECK_NE(MSG_ROUTING_NONE, reply->widget_params->routing_id);

  // While this view may be a background extension page, it can spawn a visible
  // render view. So we just assume that the new one is not another background
  // page instead of passing on our own value.
  // TODO(vangelis): Can we tell if the new view will be a background page?
  bool never_composited = false;

  // The initial hidden state for the RenderViewImpl here has to match what the
  // browser will eventually decide for the given disposition. Since we have to
  // return from this call synchronously, we just have to make our best guess
  // and rely on the browser sending a WasHidden / WasShown message if it
  // disagrees.
  mojom::CreateViewParamsPtr view_params = mojom::CreateViewParams::New();

  view_params->opener_frame_token = GetWebFrame()->GetFrameToken();
  view_params->window_was_opened_by_another_window = true;
  view_params->renderer_preferences = GetWebView()->GetRendererPreferences();
  view_params->web_preferences = GetWebView()->GetWebPreferences();

  view_params->replication_state = blink::mojom::FrameReplicationState::New();
  view_params->replication_state->frame_policy.sandbox_flags = sandbox_flags;
  view_params->replication_state->name = frame_name_utf8;
  view_params->devtools_main_frame_token = reply->devtools_main_frame_token;
  view_params->browsing_context_group_info = reply->browsing_context_group_info;
  view_params->color_provider_colors = reply->color_provider_colors;

  auto main_frame_params = mojom::CreateLocalMainFrameParams::New();
  main_frame_params->frame_token = reply->main_frame_token;
  main_frame_params->routing_id = reply->main_frame_route_id;
  main_frame_params->frame = std::move(reply->frame);
  main_frame_params->interface_broker =
      std::move(reply->main_frame_interface_broker);
  main_frame_params->document_token = reply->document_token;
  main_frame_params->policy_container = std::move(reply->policy_container);
  main_frame_params->associated_interface_provider_remote =
      std::move(reply->associated_interface_provider);
  main_frame_params->widget_params = std::move(reply->widget_params);
  main_frame_params->subresource_loader_factories =
      base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>(
          CloneLoaderFactories()->Clone().release()));

  view_params->main_frame =
      mojom::CreateMainFrameUnion::NewLocalParams(std::move(main_frame_params));
  view_params->blink_page_broadcast = std::move(reply->page_broadcast);
  view_params->session_storage_namespace_id =
      reply->cloned_session_storage_namespace_id;
  DCHECK(!view_params->session_storage_namespace_id.empty())
      << "Session storage namespace must be populated.";
  view_params->hidden = is_background_tab;
  view_params->never_composited = never_composited;
  view_params->partitioned_popin_params =
      std::move(reply->partitioned_popin_params);

  WebView* web_view = agent_scheduling_group_->CreateWebView(
      std::move(view_params),
      /*was_created_by_renderer=*/true, base_url);

  if (reply->wait_for_debugger) {
    blink::WebFrameWidget* frame_widget =
        web_view->MainFrame()->ToWebLocalFrame()->LocalRoot()->FrameWidget();
    frame_widget->WaitForDebuggerWhenShown();
  }

  return web_view;
}

std::unique_ptr<blink::WebLinkPreviewTriggerer>
RenderFrameImpl::CreateLinkPreviewTriggerer() {
  return GetContentClient()->renderer()->CreateLinkPreviewTriggerer();
}

void RenderFrameImpl::ResetMembersUsedForDurationOfCommit() {
  pending_loader_factories_ = nullptr;
  pending_code_cache_host_.reset();
  pending_cookie_manager_info_.reset();
  pending_storage_info_.reset();
  is_requesting_navigation_ = false;
}

}  // namespace content