// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/web_contents/web_contents_impl.h"

#include <stddef.h>

#include <algorithm>
#include <cmath>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>

#include "base/auto_reset.h"
#include "base/base_switches.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/flat_set.h"
#include "base/debug/crash_logging.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/to_string.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/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/optional_trace_event.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_session_observer.h"
#include "build/build_config.h"
#include "cc/input/browser_controls_offset_tag_modifications.h"
#include "components/download/public/common/download_stats.h"
#include "components/input/cursor_manager.h"
#include "components/input/features.h"
#include "components/input/render_widget_host_input_event_router.h"
#include "components/input/switches.h"
#include "components/input/utils.h"
#include "components/url_formatter/url_formatter.h"
#include "components/viz/common/features.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/back_forward_cache/back_forward_cache_impl.h"
#include "content/browser/bad_message.h"
#include "content/browser/browser_context_impl.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/browser_plugin/browser_plugin_embedder.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/btm/btm_bounce_detector.h"
#include "content/browser/closewatcher/close_listener_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/declarative_performance_observer/declarative_performance_observer_coordinator.h"
#include "content/browser/device_posture/device_posture_provider_impl.h"
#include "content/browser/devtools/protocol/page_handler.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/display_cutout/display_cutout_host_impl.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/download/mhtml_generation_manager.h"
#include "content/browser/download/save_package.h"
#include "content/browser/fenced_frame/fenced_frame.h"
#include "content/browser/find_request_manager.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/guest_page_holder_impl.h"
#include "content/browser/host_zoom_map_impl.h"
#include "content/browser/media/audio_stream_monitor.h"
#include "content/browser/media/media_web_contents_observer.h"
#include "content/browser/memory/scheduler_loop_quarantine_web_contents_observer.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/permissions/permission_util.h"
#include "content/browser/preloading/prefetch/prefetch_request.h"
#include "content/browser/preloading/prefetch/prefetch_service.h"
#include "content/browser/preloading/prefetch/prefetch_type.h"
#include "content/browser/preloading/preloading.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/preloading/prerender/prerender_handle_impl.h"
#include "content/browser/preloading/prerender/prerender_host_registry.h"
#include "content/browser/preloading/prerender/prerender_metrics.h"
#include "content/browser/preloading/prerender/prerender_new_tab_handle.h"
#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include "content/browser/renderer_host/cross_process_frame_connector.h"
#include "content/browser/renderer_host/frame_token_message_queue.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/input/touch_emulator_impl.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/page_impl.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_host_manager.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_factory.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
#include "content/browser/renderer_host/spare_render_process_host_manager_impl.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/browser/renderer_host/visible_time_request_trigger.h"
#include "content/browser/screen_details/screen_change_monitor.h"
#include "content/browser/screen_orientation/screen_orientation_provider.h"
#include "content/browser/security/cpsp/child_process_security_policy_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/surface_embed/surface_embed_connector_impl.h"
#include "content/browser/wake_lock/wake_lock_context_host.h"
#include "content/browser/web_contents/drag_source_document_tracker.h"
#include "content/browser/web_contents/drag_source_map.h"
#include "content/browser/web_contents/file_chooser_impl.h"
#include "content/browser/web_contents/java_script_dialog_commit_deferring_condition.h"
#include "content/browser/web_contents/slow_web_preference_cache.h"
#include "content/browser/web_contents/web_contents_view.h"
#include "content/browser/web_contents/web_contents_view_child_frame.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/content_switches_internal.h"
#include "content/common/features.h"
#include "content/public/browser/ax_inspect_factory.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/color_chooser.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/document_picture_in_picture_window_controller.h"
#include "content/public/browser/document_user_data.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/focused_node_details.h"
#include "content/public/browser/frame_type.h"
#include "content/public/browser/global_dom_node_id.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/javascript_dialog_manager.h"
#include "content/public/browser/keyboard_event_processing_result.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_throttle_registry.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/picture_in_picture_window_controller.h"
#include "content/public/browser/preload_pipeline_info.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/render_widget_host_observer.h"
#include "content/public/browser/restore_type.h"
#include "content/public/browser/scoped_accessibility_mode.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/webid/federated_embedder_login_request.h"
#include "content/public/browser/webid/identity_credential_source.h"
#include "content/public/browser/webui_config.h"
#include "content/public/browser/webui_config_map.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/referrer_type_converters.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/widget_type.h"
#include "ipc/constants.mojom.h"
#include "media/base/media_switches.h"
#include "net/base/url_util.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "partition_alloc/buildflags.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/request_destination.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/loader/resource_type_util.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
#include "third_party/blink/public/common/page/content_to_visible_time_request.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/common/page_state/page_state.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "third_party/blink/public/common/security/protocol_handler_security_level.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/common/widget/constants.h"
#include "third_party/blink/public/mojom/frame/deferred_fetch_policy.mojom-shared.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-shared.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#include "third_party/blink/public/mojom/page/draggable_region.mojom.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/accessibility/ax_tree_combiner.h"
#include "ui/accessibility/platform/browser_accessibility.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
#include "ui/base/clipboard/clipboard_metadata.h"
#include "ui/base/ime/mojom/virtual_keyboard_types.mojom.h"
#include "ui/base/mojom/window_show_state.mojom.h"
#include "ui/base/pointer/pointer_device.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_types.h"
#include "ui/base/window_open_disposition.h"
#include "ui/color/color_provider_key.h"
#include "ui/color/color_provider_manager.h"
#include "ui/color/color_provider_utils.h"
#include "ui/compositor/compositor.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/animation/animation.h"

#if BUILDFLAG(IS_WIN)
#include "base/threading/thread_restrictions.h"
#include "components/stylus_handwriting/win/features.h"
#include "content/browser/renderer_host/dip_util.h"
#include "ui/gfx/geometry/dip_util.h"
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_ANDROID)
#include "base/android/device_info.h"
#include "base/android/scoped_service_binding_batch.h"
#include "base/check.h"
#include "components/viz/common/gpu/raster_context_provider.h"
#include "content/browser/android/java_interfaces_impl.h"
#include "content/browser/android/nfc_host.h"
#include "content/browser/android/selection/selection_popup_controller.h"
#include "content/browser/navigation_transitions/back_forward_transition_animation_manager_android.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/web_contents/web_contents_android.h"
#include "content/browser/web_contents/web_contents_view_android.h"
#include "content/public/browser/android/child_process_importance.h"
#include "content/public/browser/android/selection_popup_delegate.h"
#include "services/device/public/mojom/nfc.mojom.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "ui/android/event_forwarder.h"
#include "ui/android/view_android.h"
#include "ui/base/device_form_factor.h"
#else  // !BUILDFLAG(IS_ANDROID)
#include "ui/accessibility/accessibility_features.h"
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
#include "content/browser/date_time_chooser/date_time_chooser.h"
#endif

#if BUILDFLAG(ENABLE_VR)
#include "content/browser/xr/service/xr_runtime_manager_impl.h"
#endif

#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/wm/core/window_util.h"
#endif

#if BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_TVOS)
#include "content/browser/ios/nfc_host.h"
#endif

namespace content {

namespace {

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange(CrashRepHandlingOutcome)
enum class CrashRepHandlingOutcome {
  // Dialog wasn't suppressed and the crash report will be potentially queued.
  kPotentiallyQueued = 0,
  // Dialog was suppressed so the crash report was dropped.
  kDropped = 1,
  kMaxValue = kDropped,
};
// LINT.ThenChange(//tools/metrics/histograms/enums.xml:CrashRepHandlingOutcome)

// The window which we dobounce load info updates in.
constexpr auto kUpdateLoadStatesInterval = base::Milliseconds(250);

using LifecycleState = RenderFrameHost::LifecycleState;
using LifecycleStateImpl = RenderFrameHostImpl::LifecycleStateImpl;

base::LazyInstance<base::RepeatingCallbackList<void(WebContents*)>>::
    DestructorAtExit g_created_callbacks = LAZY_INSTANCE_INITIALIZER;

bool HasMatchingWidgetHost(FrameTree* tree, RenderWidgetHostImpl* host) {
  // This method scans the frame tree rather than checking whether
  // host->delegate() == this, which allows it to return false when the host
  // for a frame that is pending or pending deletion.
  if (!host) {
    return false;
  }

  for (FrameTreeNode* node : tree->NodesIncludingInnerTreeNodes()) {
    // We might cross a WebContents boundary here, but it's fine as we are only
    // comparing the RWHI with the given `host`, which is always guaranteed to
    // belong to the same WebContents as `tree`.
    if (node->current_frame_host()->GetRenderWidgetHost() == host) {
      DCHECK_EQ(WebContentsImpl::FromFrameTreeNode(node),
                WebContentsImpl::FromRenderWidgetHostImpl(host));
      return true;
    }
  }
  return false;
}

RenderFrameHostImpl* FindOpenerRFH(const WebContents::CreateParams& params) {
  RenderFrameHostImpl* opener_rfh = nullptr;
  if (params.opener_id) {
    opener_rfh = RenderFrameHostImpl::FromID(params.opener_id);
  }
  return opener_rfh;
}

// Returns |true| if |type| is the kind of user input that should trigger the
// user interaction observers.
bool IsUserInteractionInputType(blink::WebInputEvent::Type type) {
  // TODO(mustaq): This list should be based off the HTML spec:
  // https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation,
  // and kGestureScrollBegin is a clear outlier.
  return type == blink::WebInputEvent::Type::kMouseDown ||
         type == blink::WebInputEvent::Type::kGestureScrollBegin ||
         type == blink::WebInputEvent::Type::kTouchStart ||
         type == blink::WebInputEvent::Type::kRawKeyDown ||
         type == blink::WebInputEvent::Type::kKeyDown;
}

// Ensures that OnDialogClosed is only called once.
class CloseDialogCallbackWrapper
    : public base::RefCountedThreadSafe<CloseDialogCallbackWrapper> {
 public:
  using CloseCallback =
      base::OnceCallback<void(bool, bool, const std::u16string&)>;

  explicit CloseDialogCallbackWrapper(CloseCallback callback)
      : callback_(std::move(callback)) {}

  void Run(bool dialog_was_suppressed,
           bool success,
           const std::u16string& user_input) {
    if (callback_.is_null()) {
      return;
    }
    std::move(callback_).Run(dialog_was_suppressed, success, user_input);
  }

 private:
  friend class base::RefCountedThreadSafe<CloseDialogCallbackWrapper>;
  ~CloseDialogCallbackWrapper() = default;

  CloseCallback callback_;
};

bool FrameCompareDepth(RenderFrameHostImpl* a, RenderFrameHostImpl* b) {
  return a->GetFrameDepth() < b->GetFrameDepth();
}

bool AreValidRegisterProtocolHandlerArguments(
    const std::string& protocol,
    const GURL& url,
    const url::Origin& origin,
    blink::ProtocolHandlerSecurityLevel security_level) {
  ChildProcessSecurityPolicyImpl* policy =
      ChildProcessSecurityPolicyImpl::GetInstance();
  if (policy->IsPseudoScheme(protocol)) {
    return false;
  }

  // Implementation of the protocol handler arguments normalization steps
  // defined in the spec.
  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
  //
  // Verify custom handler schemes for errors as described in steps 1 and 2
  if (!blink::IsValidCustomHandlerScheme(protocol, security_level)) {
    return false;
  }

  blink::URLSyntaxErrorCode code =
      blink::IsValidCustomHandlerURLSyntax(url, security_level);
  if (code != blink::URLSyntaxErrorCode::kNoError) {
    return false;
  }

  // Verify custom handler URL security as described in steps 6 and 7
  if (!blink::IsAllowedCustomHandlerURL(url, security_level)) {
    return false;
  }
  url::Origin url_origin = url::Origin::Create(url);
  if (url_origin.opaque()) {
    return false;
  }
  if (security_level < blink::ProtocolHandlerSecurityLevel::kUntrustedOrigins &&
      !origin.IsSameOriginWith(url)) {
    return false;
  }

  return true;
}

void RecordMaxFrameCountUMA(size_t max_frame_count) {
  UMA_HISTOGRAM_COUNTS_10000("Navigation.MainFrame.MaxFrameCount",
                             max_frame_count);
}

// Returns the set of all WebContentses that are reachable from |web_contents|
// by applying some combination of
// WebContents::GetFirstWebContentsInLiveOriginalOpenerChain() and
// WebContents::GetOuterWebContents(). The |web_contents| parameter will be
// included in the returned set.
base::flat_set<raw_ptr<WebContentsImpl>> GetAllOpeningWebContents(
    WebContentsImpl* web_contents) {
  base::flat_set<raw_ptr<WebContentsImpl>> result;
  base::flat_set<WebContentsImpl*> current;

  current.insert(web_contents);

  while (!current.empty()) {
    WebContentsImpl* current_contents = *current.begin();
    current.erase(current.begin());
    auto insert_result = result.insert(current_contents);

    if (insert_result.second) {
      if (WebContents* opener_contents =
              current_contents
                  ->GetFirstWebContentsInLiveOriginalOpenerChain()) {
        current.insert(static_cast<WebContentsImpl*>(opener_contents));
      }

      WebContentsImpl* outer_contents = current_contents->GetOuterWebContents();
      if (outer_contents) {
        current.insert(outer_contents);
      }
    }
  }

  return result;
}

// Store a set of fullscreen WebContents and metadata for the browser context.
// Storing this information on the browser context is done for two reasons. One,
// related WebContentses must necessarily share a browser context, so this saves
// lookup time by restricting to one specific browser context. Two, separating
// by browser context is preemptive paranoia about keeping things separate.
class FullscreenUserData : public base::SupportsUserData::Data {
 public:
  FullscreenUserData() = default;
  ~FullscreenUserData() override = default;

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

  base::flat_set<raw_ptr<WebContentsImpl, CtnExperimental>>* set() {
    return &set_;
  }

  std::map<url::Origin, base::TimeTicks>* last_exits() { return &last_exits_; }

 private:
  base::flat_set<raw_ptr<WebContentsImpl, CtnExperimental>> set_;
  // Track latest exits by origin to briefly block re-entry without a gesture.
  std::map<url::Origin, base::TimeTicks> last_exits_;
};

const char kFullscreenUserData[] = "fullscreen-user-data";

FullscreenUserData* GetFullscreenUserData(BrowserContext* browser_context) {
  auto* set_holder = static_cast<FullscreenUserData*>(
      browser_context->GetUserData(kFullscreenUserData));
  if (!set_holder) {
    auto new_holder = std::make_unique<FullscreenUserData>();
    set_holder = new_holder.get();
    browser_context->SetUserData(kFullscreenUserData, std::move(new_holder));
  }
  return set_holder;
}

base::flat_set<raw_ptr<WebContentsImpl, CtnExperimental>>*
FullscreenContentsSet(BrowserContext* browser_context) {
  return GetFullscreenUserData(browser_context)->set();
}

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

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

// Returns true if `host` has the Automatic Fullscreen permission granted.
bool IsAutomaticFullscreenGranted(RenderFrameHost* host) {
  content::PermissionController* permission_controller =
      host->GetBrowserContext()->GetPermissionController();
  CHECK(permission_controller);

  return permission_controller->GetPermissionStatusForCurrentDocument(
             content::PermissionDescriptorUtil::
                 CreatePermissionDescriptorForPermissionType(
                     blink::PermissionType::AUTOMATIC_FULLSCREEN),
             host) == blink::mojom::PermissionStatus::GRANTED;
}

// Adjust the requested `rect` for opening or placing a window and return the id
// of the display where the window will be placed. The bounds may not extend
// outside a single screen's work area, and the `host` requires permission to
// specify bounds on a screen other than its current screen.
// TODO(crbug.com/40092782): These adjustments are inaccurate for window.open(),
// which specifies the inner content size, and for window.moveTo, resizeTo, etc.
// calls on newly created windows, which may pass empty sizes or positions to
// indicate uninitialized placement information in the renderer. Constraints
// enforced later should resolve most inaccuracies, but this early enforcement
// is needed to ensure bounds indicate the appropriate display.
int64_t AdjustWindowRectForDisplay(gfx::Rect* rect, RenderFrameHost* host) {
  auto* screen = display::Screen::Get();
  auto display = screen->GetDisplayMatching(*rect);

  // Check, but do not prompt, for permission to place windows on other screens.
  // Sites generally need permission to get such bounds in the first place.
  // Also clamp offscreen bounds to the window's current screen.
  if (!rect->Intersects(display.bounds()) || !IsWindowManagementGranted(host)) {
    // Use the main frame's NativeView; cross-origin iframes yield null.
    gfx::NativeView view = host->GetOutermostMainFrame()->GetNativeView();
    display = screen->GetDisplayNearestView(view);
  }
  rect->AdjustToFit(display.work_area());
  return display.id();
}

// Adjusts the bounds to the minimum window size provided. Defaults to
// `blink::kMinimumWindowSize` but can be overridden, e.g. for unframed apps.
void AdjustWindowRectForMinimum(gfx::Rect* bounds,
                                int minimum_size = blink::kMinimumWindowSize) {
  // Size 0 indicates default size, not minimum.
  if (bounds->width()) {
    bounds->set_width(std::max(minimum_size, bounds->width()));
  }
  if (bounds->height()) {
    bounds->set_height(std::max(minimum_size, bounds->height()));
  }
}

// A ColorProviderSource used when one has not been explicitly set. This source
// only reflects theme information present in the web NativeTheme singleton.
// Keep this an implementation detail of WebContentsImpl as we should not be
// exposing a default ColorProviderSource generally.
class DefaultColorProviderSource : public ui::ColorProviderSource,
                                   public ui::NativeThemeObserver {
 public:
  DefaultColorProviderSource() {
    native_theme_observation_.Observe(ui::NativeTheme::GetInstanceForWeb());
  }
  DefaultColorProviderSource(const DefaultColorProviderSource&) = delete;
  DefaultColorProviderSource& operator=(const DefaultColorProviderSource&) =
      delete;
  ~DefaultColorProviderSource() override = default;

  static DefaultColorProviderSource* GetInstance() {
    static base::NoDestructor<DefaultColorProviderSource> instance;
    return instance.get();
  }

  // ui::ColorProviderSource:
  const ui::ColorProvider* GetColorProvider() const override {
    return ui::ColorProviderManager::Get().GetColorProviderFor(
        GetColorProviderKey());
  }

  ui::RendererColorMap GetRendererColorMap(
      ui::ColorProviderKey::ColorMode color_mode,
      ui::ColorProviderKey::ForcedColors forced_colors) const override {
    auto key = GetColorProviderKey();
    key.color_mode = color_mode;
    key.forced_colors = forced_colors;
    ui::ColorProvider* color_provider =
        ui::ColorProviderManager::Get().GetColorProviderFor(key);
    CHECK(color_provider);

    return ui::CreateRendererColorMap(*color_provider);
  }

  // ui::NativeThemeObserver:
  void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override {
    DCHECK(native_theme_observation_.IsObservingSource(observed_theme));
    NotifyColorProviderChanged();
  }

 protected:
  // ui::ColorProviderSource:
  ui::ColorProviderKey GetColorProviderKey() const override {
    return ui::NativeTheme::GetInstanceForWeb()->GetColorProviderKey(nullptr);
  }

 private:
  base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
      native_theme_observation_{this};
};

size_t GetFrameTreeSize(FrameTree* frame_tree) {
  size_t tree_size = 0;
  FrameTree::NodeRange node_range = frame_tree->NodesIncludingInnerTreeNodes();
  FrameTree::NodeIterator node_iter = node_range.begin();
  while (node_iter != node_range.end()) {
    // Skip over collapsed frame trees.
    if ((*node_iter)->is_collapsed()) {
      node_iter.AdvanceSkippingChildren();
    } else {
      ++tree_size;
      ++node_iter;
    }
  }
  return tree_size;
}

using RenderWidgetHostAtPointCallback =
    base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>,
                            std::optional<gfx::PointF>)>;

void RunCallback(RenderWidgetHostAtPointCallback callback,
                 base::WeakPtr<input::RenderWidgetHostViewInput> view,
                 std::optional<gfx::PointF> point) {
  auto* target = static_cast<RenderWidgetHostViewBase*>(view.get());
  if (!callback.is_null()) {
    std::move(callback).Run(target ? target->GetWeakPtr() : nullptr, point);
  }
}

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RenderProcessHostPriority {
  kBestEffort = 0,
  kUserVisible = 1,
  kUserBlocking = 2,
  kMissingRenderProcessHost = 3,
  kMaxValue = kMissingRenderProcessHost,
};

RenderProcessHostPriority GetRenderProcessHostPriority(
    RenderProcessHost* render_process_host) {
  if (!render_process_host) {
    return RenderProcessHostPriority::kMissingRenderProcessHost;
  }
  switch (render_process_host->GetPriority()) {
    case base::Process::Priority::kBestEffort:
      return RenderProcessHostPriority::kBestEffort;
    case base::Process::Priority::kUserVisible:
      return RenderProcessHostPriority::kUserVisible;
    case base::Process::Priority::kUserBlocking:
      return RenderProcessHostPriority::kUserBlocking;
  }
  NOTREACHED();
}

void RecordRendererUnresponsiveMetrics(
    bool web_contents_visible,
    RenderWidgetHostImpl* render_widget_host) {
  base::UmaHistogramBoolean("Renderer.Unresponsive.Visibility",
                            web_contents_visible);

  if (!web_contents_visible) {
    return;
  }

  RenderProcessHostPriority rph_priority =
      GetRenderProcessHostPriority(render_widget_host->GetProcess());
  base::UmaHistogramEnumeration(
      "Renderer.Unresponsive.PageVisible.RenderProcessHostPriority",
      rph_priority);

  bool widget_visible = !render_widget_host->IsHidden();
  base::UmaHistogramBoolean(
      "Renderer.Unresponsive.PageVisible.WidgetVisibility", widget_visible);

  if (!widget_visible) {
    return;
  }

  base::UmaHistogramEnumeration(
      "Renderer.Unresponsive.WidgetVisible.RenderProcessHostPriority",
      rph_priority);
}

// Helper function to simplify getting the correct HostZoomMapImpl for a
// RenderFrameHost. Implemented here to be close to its only consumer,
// GetPendingZoomLevel().
HostZoomMapImpl* HostZoomMapForRenderFrameHost(const RenderFrameHost* rfh) {
  return static_cast<HostZoomMapImpl*>(
      HostZoomMap::Get(rfh->GetSiteInstance()));
}

}  // namespace

// This is a small helper class created while a JavaScript dialog is showing
// and destroyed when it's dismissed. Clients can register callbacks to receive
// a notification when the dialog is dismissed.
class JavaScriptDialogDismissNotifier {
 public:
  JavaScriptDialogDismissNotifier() = default;

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

  ~JavaScriptDialogDismissNotifier() {
    for (auto& callback : callbacks_) {
      std::move(callback).Run();
    }
  }

  void NotifyOnDismiss(base::OnceClosure callback) {
    callbacks_.push_back(std::move(callback));
  }

 private:
  std::vector<base::OnceClosure> callbacks_;
};

CreatedWindow::CreatedWindow() = default;
CreatedWindow::CreatedWindow(std::unique_ptr<WebContentsImpl> contents,
                             GURL target_url)
    : contents(std::move(contents)), target_url(std::move(target_url)) {}
CreatedWindow::~CreatedWindow() = default;
CreatedWindow::CreatedWindow(CreatedWindow&&) = default;
CreatedWindow& CreatedWindow::operator=(CreatedWindow&&) = default;

std::unique_ptr<WebContents> WebContents::Create(
    const WebContents::CreateParams& params) {
  return WebContentsImpl::Create(params);
}

std::unique_ptr<WebContentsImpl> WebContentsImpl::Create(
    const CreateParams& params) {
  return CreateWithOpener(params, FindOpenerRFH(params));
}

std::unique_ptr<WebContents> WebContents::CreateWithSessionStorage(
    const WebContents::CreateParams& params,
    const SessionStorageNamespaceMap& session_storage_namespace_map) {
  OPTIONAL_TRACE_EVENT0("content", "WebContents::CreateWithSessionStorage");
  std::unique_ptr<WebContentsImpl> new_contents(
      new WebContentsImpl(params.browser_context));
  RenderFrameHostImpl* opener_rfh = FindOpenerRFH(params);
  FrameTreeNode* opener = nullptr;
  if (opener_rfh) {
    opener = opener_rfh->frame_tree_node();
  }
  new_contents->SetOpenerForNewContents(opener, params.opener_suppressed);

  for (const auto& it : session_storage_namespace_map) {
    new_contents->GetController().SetSessionStorageNamespace(it.first,
                                                             it.second.get());
  }

  WebContentsImpl* outer_web_contents = nullptr;
  if (params.guest_delegate) {
    // This makes |new_contents| act as a guest.
    // For more info, see comment above class BrowserPluginGuest.
    BrowserPluginGuest::CreateInWebContents(new_contents.get(),
                                            params.guest_delegate);
    outer_web_contents = static_cast<WebContentsImpl*>(
        params.guest_delegate->GetOwnerWebContents());
  }

  new_contents->Init(params, blink::FramePolicy());
  if (outer_web_contents) {
    outer_web_contents->InnerWebContentsCreated(new_contents.get());
  }
  return new_contents;
}

base::CallbackListSubscription
WebContentsImpl::FriendWrapper::AddCreatedCallbackForTesting(
    const CreatedCallback& callback) {
  return g_created_callbacks.Get().Add(callback);
}

WebContents* WebContents::FromRenderViewHost(RenderViewHost* rvh) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContents::FromRenderViewHost", "render_view_host",
                        rvh);
  if (!rvh) {
    return nullptr;
  }
  return static_cast<WebContentsImpl*>(
      static_cast<RenderViewHostImpl*>(rvh)->GetDelegate());
}

WebContents* WebContents::FromRenderFrameHost(RenderFrameHost* rfh) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContents::FromRenderFrameHost", "render_frame_host",
                        rfh);
  return WebContentsImpl::FromRenderFrameHostImpl(
      static_cast<RenderFrameHostImpl*>(rfh));
}

WebContentsImpl* WebContentsImpl::FromRenderFrameHostImpl(
    RenderFrameHostImpl* rfh) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::FromRenderFrameHostImpl",
                        "render_frame_host", rfh);
  if (!rfh) {
    return nullptr;
  }
  return static_cast<WebContentsImpl*>(rfh->delegate());
}

WebContents* WebContents::FromFrameTreeNodeId(
    FrameTreeNodeId frame_tree_node_id) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContents::FromFrameTreeNodeId",
                        "frame_tree_node_id", frame_tree_node_id);
  FrameTreeNode* frame_tree_node =
      FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (!frame_tree_node) {
    return nullptr;
  }
  return WebContentsImpl::FromFrameTreeNode(frame_tree_node);
}

// static
WebContents* WebContents::FromDragId(BrowserContext* browser_context,
                                     const DragId& drag_id) {
  auto* map = DragSourceMap::GetOrCreate(browser_context);
  GlobalRenderFrameHostToken source_rfh_token = map->GetDragSource(drag_id);
  if (source_rfh_token == GlobalRenderFrameHostToken()) {
    return nullptr;
  }

  RenderFrameHost* rfh = RenderFrameHost::FromFrameToken(source_rfh_token);
  if (!rfh) {
    return nullptr;
  }

  auto* tracker = DragSourceDocumentTracker::GetForCurrentDocument(rfh);
  if (!tracker || !tracker->has_drag_id(drag_id)) {
    return nullptr;
  }

  return WebContents::FromRenderFrameHost(rfh);
}

WebContentsImpl* WebContentsImpl::FromRenderWidgetHostImpl(
    RenderWidgetHostImpl* rwh) {
  if (!rwh) {
    return nullptr;
  }
  return static_cast<WebContentsImpl*>(rwh->delegate());
}

bool WebContentsImpl::IsPopup() const {
  return is_popup_;
}

WindowOpenDisposition WebContentsImpl::GetOriginalWindowOpenDisposition()
    const {
  return original_window_open_disposition_;
}

void WebContents::SetScreenOrientationDelegate(
    ScreenOrientationDelegate* delegate) {
  ScreenOrientationProvider::SetDelegate(delegate);
}

// WebContentsImpl::RenderWidgetHostDestructionObserver -----------------------

class WebContentsImpl::RenderWidgetHostDestructionObserver
    : public RenderWidgetHostObserver {
 public:
  RenderWidgetHostDestructionObserver(WebContentsImpl* owner,
                                      RenderWidgetHost* watched_host)
      : owner_(owner), watched_host_(watched_host) {
    watched_host_->AddObserver(this);
  }

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

  ~RenderWidgetHostDestructionObserver() override {
    watched_host_->RemoveObserver(this);
  }

  // RenderWidgetHostObserver:
  void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) override {
    owner_->OnRenderWidgetHostDestroyed(widget_host);
  }

 private:
  raw_ptr<WebContentsImpl> owner_;
  raw_ptr<RenderWidgetHost> watched_host_;
};

// WebContentsImpl::WebContentsDestructionObserver ----------------------------

class WebContentsImpl::WebContentsDestructionObserver
    : public WebContentsObserver {
 public:
  WebContentsDestructionObserver(WebContentsImpl* owner,
                                 WebContents* watched_contents)
      : WebContentsObserver(watched_contents), owner_(owner) {}

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

  // WebContentsObserver:
  void WebContentsDestroyed() override {
    owner_->OnWebContentsDestroyed(
        static_cast<WebContentsImpl*>(web_contents()));
  }

 private:
  raw_ptr<WebContentsImpl> owner_;
};

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// TODO(sreejakshetty): Make |WebContentsImpl::ColorChooserHolder| per-frame
// instead of WebContents-owned.
// WebContentsImpl::ColorChooserHolder -----------------------------------------
class WebContentsImpl::ColorChooserHolder : public blink::mojom::ColorChooser {
 public:
  ColorChooserHolder(
      mojo::PendingReceiver<blink::mojom::ColorChooser> receiver,
      mojo::PendingRemote<blink::mojom::ColorChooserClient> client)
      : receiver_(this, std::move(receiver)), client_(std::move(client)) {}

  ~ColorChooserHolder() override {
    if (chooser_) {
      chooser_->End();
    }
  }

  void SetChooser(std::unique_ptr<content::ColorChooser> chooser) {
    chooser_ = std::move(chooser);
    if (chooser_) {
      receiver_.set_disconnect_handler(
          base::BindOnce([](content::ColorChooser* chooser) { chooser->End(); },
                         base::Unretained(chooser_.get())));
    }
  }

  void SetSelectedColor(SkColor color) override {
    OPTIONAL_TRACE_EVENT0(
        "content", "WebContentsImpl::ColorChooserHolder::SetSelectedColor");
    if (chooser_) {
      chooser_->SetSelectedColor(color);
    }
  }

  void DidChooseColorInColorChooser(SkColor color) {
    OPTIONAL_TRACE_EVENT0(
        "content",
        "WebContentsImpl::ColorChooserHolder::DidChooseColorInColorChooser");
    client_->DidChooseColor(color);
  }

 private:
  // Color chooser that was opened by this tab.
  std::unique_ptr<content::ColorChooser> chooser_;

  // mojo receiver.
  mojo::Receiver<blink::mojom::ColorChooser> receiver_;

  // mojo renderer client.
  mojo::Remote<blink::mojom::ColorChooserClient> client_;
};
#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)

// WebContentsImpl::WebContentsTreeNode ----------------------------------------
WebContentsImpl::WebContentsTreeNode::WebContentsTreeNode(
    WebContentsImpl* current_web_contents)
    : current_web_contents_(current_web_contents),
      outer_web_contents_(nullptr) {}

WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() = default;

void WebContentsImpl::WebContentsTreeNode::AttachInnerWebContents(
    WebContents* inner_web_contents,
    RenderFrameHostImpl* render_frame_host,
    bool should_take_ownership) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsTreeNode::AttachInnerWebContents");
  WebContentsImpl* inner_web_contents_impl =
      static_cast<WebContentsImpl*>(inner_web_contents);
  WebContentsTreeNode& inner_web_contents_node = inner_web_contents_impl->node_;

  inner_web_contents_node.outer_web_contents_ = current_web_contents_;
  inner_web_contents_node.outer_contents_frame_tree_node_id_ =
      render_frame_host->frame_tree_node()->frame_tree_node_id();

  if (should_take_ownership) {
    inner_web_contents->SetOwnerLocationForDebug(FROM_HERE);
    owned_inner_web_contents_.push_back(base::WrapUnique(inner_web_contents));
  } else {
    unowned_inner_web_contents_.push_back(inner_web_contents);
  }

  render_frame_host->frame_tree_node()->AddObserver(&inner_web_contents_node);
  current_web_contents_->InnerWebContentsAttached(inner_web_contents_impl);
}

void WebContentsImpl::WebContentsTreeNode::DetachInnerWebContents(
    WebContents* inner_web_contents) {
  OPTIONAL_TRACE_EVENT0(
      "content",
      "WebContentsImpl::WebContentsTreeNode::DetachInnerWebContents");
  CHECK_EQ(inner_web_contents->GetOuterWebContents(), current_web_contents_);

  auto* inner_web_contents_impl =
      static_cast<WebContentsImpl*>(inner_web_contents);
  if (IsUnownedInnerWebContents(inner_web_contents)) {
    DetachUnownedInnerWebContents(inner_web_contents_impl);
  } else {
    DestroyOwnedInnerWebContents(inner_web_contents_impl);
  }
}

bool WebContentsImpl::WebContentsTreeNode::IsUnownedInnerWebContents(
    WebContents* inner_web_contents) const {
  CHECK_EQ(inner_web_contents->GetOuterWebContents(), current_web_contents_);
  return std::ranges::contains(unowned_inner_web_contents_, inner_web_contents);
}

void WebContentsImpl::WebContentsTreeNode::DetachUnownedInnerWebContents(
    WebContentsImpl* inner_web_contents) {
  std::erase(unowned_inner_web_contents_, inner_web_contents);

  // Detach WebContents tree node and frame tree node.
  bool was_inner_web_contents_focused =
      inner_web_contents->ContainsOrIsFocusedWebContents();
  FrameTree* focused_frame_tree = inner_web_contents->GetFocusedFrameTree();
  WebContentsTreeNode& inner_web_contents_node = inner_web_contents->node_;
  inner_web_contents_node.outer_web_contents_ = nullptr;
  FrameTreeNode* outer_contents_frame_tree_node =
      inner_web_contents_node.OuterContentsFrameTreeNode();
  outer_contents_frame_tree_node->RemoveObserver(&inner_web_contents_node);
  outer_contents_frame_tree_node->current_frame_host()
      ->set_inner_tree_main_frame_tree_node_id(FrameTreeNodeId());
  outer_contents_frame_tree_node->render_manager()
      ->set_detach_inner_delegate_complete();
  inner_web_contents_node.outer_contents_frame_tree_node_id_ =
      FrameTreeNodeId();

  // Reset inner WebContents's focused frame tree.
  // When attached, only the outermost WebContents retains a focused tree. After
  // detaching, the inner WebContents becomes the outermost WebContents from its
  // perspective, so its focused frame tree needs to be set.
  inner_web_contents_node.SetFocusedFrameTree(
      was_inner_web_contents_focused
          ? focused_frame_tree
          : &inner_web_contents->GetPrimaryFrameTree());
  // Reset the outermost WebContents's focused frame tree if the inner
  // WebContents was focused before detaching.
  if (was_inner_web_contents_focused) {
    current_web_contents_->SetAsFocusedWebContentsIfNecessary();
  }

  current_web_contents_->InnerWebContentsDetached(inner_web_contents);
}

void WebContentsImpl::WebContentsTreeNode::DestroyOwnedInnerWebContents(
    WebContentsImpl* inner_web_contents) {
  std::unique_ptr<WebContents> inner_web_contents_to_delete;
  for (std::unique_ptr<WebContents>& web_contents : owned_inner_web_contents_) {
    if (web_contents.get() == inner_web_contents) {
      // Remove the WebContents from the list of owned WebContents.
      inner_web_contents_to_delete = std::move(web_contents);
      std::swap(web_contents, owned_inner_web_contents_.back());
      owned_inner_web_contents_.pop_back();
      break;
    }
  }
  CHECK(inner_web_contents_to_delete);
  inner_web_contents->SetOwnerLocationForDebug(std::nullopt);
  current_web_contents_->InnerWebContentsDetached(inner_web_contents);
  inner_web_contents_to_delete.reset();
}

FrameTreeNode*
WebContentsImpl::WebContentsTreeNode::OuterContentsFrameTreeNode() const {
  return FrameTreeNode::GloballyFindByID(outer_contents_frame_tree_node_id_);
}

void WebContentsImpl::WebContentsTreeNode::OnFrameTreeNodeDestroyed(
    FrameTreeNode* node) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsTreeNode::OnFrameTreeNodeDestroyed");
  DCHECK_EQ(outer_contents_frame_tree_node_id_, node->frame_tree_node_id())
      << "WebContentsTreeNode should only receive notifications for the "
         "FrameTreeNode in its outer WebContents that hosts it.";

  node->RemoveObserver(this);

  if (outer_web_contents_->node_.IsUnownedInnerWebContents(
          current_web_contents_)) {
    // Detach at all levels (WebContentsTreeNode and FrameTreeNode,
    // RenderFrameProxyHost, etc.). This will not delete `this` (the inner
    // WebContents).
    outer_web_contents_->DetachUnownedInnerWebContents(current_web_contents_);
  } else {
    // Detach only at the WebContentsTreeNode level. This will delete `this` (
    // the inner WebContents).
    outer_web_contents_->node_.DetachInnerWebContents(current_web_contents_);
  }
}

void WebContentsImpl::NotifySwappedRWHVChildFrameFromRenderManager(
    RenderWidgetHostViewChildFrame* new_view,
    bool allow_paint_holding) {
  if (surface_embed_connector_) {
    surface_embed_connector_->SetView(new_view, allow_paint_holding);
  }
}

FrameTree* WebContentsImpl::WebContentsTreeNode::focused_frame_tree() {
  CHECK(focused_frame_tree_);
  return focused_frame_tree_;
}

void WebContentsImpl::WebContentsTreeNode::SetFocusedFrameTree(
    FrameTree* frame_tree) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsTreeNode::SetFocusedFrameTree");
  DCHECK(!outer_web_contents())
      << "Only the outermost WebContents tracks focus.";
  focused_frame_tree_ = frame_tree;
}

WebContentsImpl*
WebContentsImpl::WebContentsTreeNode::GetInnerWebContentsInFrame(
    const FrameTreeNode* frame) {
  auto ftn_id = frame->frame_tree_node_id();
  for (auto& contents : owned_inner_web_contents_) {
    WebContentsImpl* impl = static_cast<WebContentsImpl*>(contents.get());
    if (impl->node_.outer_contents_frame_tree_node_id() == ftn_id) {
      return impl;
    }
  }
  for (auto& contents : unowned_inner_web_contents_) {
    WebContentsImpl* impl = static_cast<WebContentsImpl*>(contents);
    if (impl->node_.outer_contents_frame_tree_node_id() == ftn_id) {
      return impl;
    }
  }
  return nullptr;
}

std::vector<WebContentsImpl*>
WebContentsImpl::WebContentsTreeNode::GetInnerWebContents() const {
  std::vector<WebContentsImpl*> inner_web_contents;
  for (auto& contents : owned_inner_web_contents_) {
    inner_web_contents.push_back(static_cast<WebContentsImpl*>(contents.get()));
  }
  for (auto& contents : unowned_inner_web_contents_) {
    inner_web_contents.push_back(static_cast<WebContentsImpl*>(contents));
  }

  return inner_web_contents;
}

// WebContentsObserverList -----------------------------------------------------
WebContentsImpl::WebContentsObserverList::WebContentsObserverList() = default;
WebContentsImpl::WebContentsObserverList::~WebContentsObserverList() = default;

void WebContentsImpl::WebContentsObserverList::AddObserver(
    WebContentsObserver* observer) {
  observers_.AddObserver(observer);
}

void WebContentsImpl::WebContentsObserverList::RemoveObserver(
    WebContentsObserver* observer) {
  observers_.RemoveObserver(observer);
}

// WebContentsImpl -------------------------------------------------------------

namespace {

// A helper for ensuring that WebContents are closed (or otherwise destroyed)
// *before* their BrowserContext is destroyed.
class WebContentsOfBrowserContext : public base::SupportsUserData::Data {
 public:
  static void Attach(WebContentsImpl& web_contents) {
    WebContentsOfBrowserContext* self =
        GetOrCreate(*web_contents.GetBrowserContext());
    self->web_contents_set_.insert(&web_contents);
  }

  static void Detach(WebContentsImpl& web_contents) {
    WebContentsOfBrowserContext* self =
        GetOrCreate(*web_contents.GetBrowserContext());
    self->web_contents_set_.erase(&web_contents);
  }

  ~WebContentsOfBrowserContext() override {
    // The ~WebContentsOfBrowserContext destructor is called when the
    // BrowserContext (and its UserData) gets destroyed.  At this point
    // in time all the WebContents of this BrowserContext should have been
    // already closed (i.e. we expect the `web_contents_set_` set to be empty at
    // this point - emptied by calls to the `Detach` method above).
    if (web_contents_set_.empty()) {
      return;  // Everything is okay - nothing to warn about.
    }

    // Any remaining WebContents contain dangling pointers to the
    // BrowserContext being destroyed.  Such WebContents (and their
    // RenderFrameHosts, SiteInstances, etc.) risk causing
    // use-after-free bugs.  For more discussion about managing the
    // lifetime of WebContents please see https://crbug.com/1376879#c44.
    WebContentsImpl* const web_contents_with_dangling_ptr_to_browser_context =
        *web_contents_set_.begin();
    const std::string creator =
        web_contents_with_dangling_ptr_to_browser_context->GetCreatorLocation()
            .ToString();
    SCOPED_CRASH_KEY_STRING256("shutdown", "web_contents/creator", creator);

    const std::optional<base::Location>& ownership_location =
        web_contents_with_dangling_ptr_to_browser_context->ownership_location();
    std::string owner;
    if (ownership_location) {
      if (ownership_location->has_source_info()) {
        owner = std::string(ownership_location->function_name()) + "@" +
                ownership_location->file_name();
      } else {
        owner = "no_source_info";
      }
    } else {
      owner = "unknown";
    }
    SCOPED_CRASH_KEY_STRING256("shutdown", "web_contents/owner", owner);

#if BUILDFLAG(IS_ANDROID)
    // On Android, also report the Java stack trace from WebContents's
    // creation.
    WebContentsAndroid::ReportDanglingPtrToBrowserContext(
        base::android::AttachCurrentThread(),
        web_contents_with_dangling_ptr_to_browser_context);
#endif  // BUILDFLAG(IS_ANDROID)

    NOTREACHED()
        << "BrowserContext is getting destroyed without first closing all "
        << "WebContents (for more info see https://crbug.com/1376879#c44); "
        << "creator = " << creator;
  }

  std::unique_ptr<Data> Clone() override {
    // Indicate to `SupportsUserData` / `BrowserContext` that cloning is not
    // supported.
    return nullptr;
  }

 private:
  WebContentsOfBrowserContext() = default;

  // Gets WebContentsOfBrowserContext associated with the given
  // `browser_context` (creating a new WebContentsOfBrowserContext if
  // necessary - if one hasn't been created yet).
  static WebContentsOfBrowserContext* GetOrCreate(
      BrowserContext& browser_context) {
    static const char* kUserDataKey = "WebContentsOfBrowserContext/UserDataKey";
    WebContentsOfBrowserContext* result =
        static_cast<WebContentsOfBrowserContext*>(
            browser_context.GetUserData(kUserDataKey));
    if (!result) {
      result = new WebContentsOfBrowserContext();
      browser_context.SetUserData(kUserDataKey, base::WrapUnique(result));
    }
    return result;
  }

  // Set of all `WebContents` within the tracked `BrowserContext`.
  //
  // Usage of `raw_ptr` below is okay (i.e. it shouldn't dangle), because
  // when `WebContentsImpl`'s destructor runs, then it removes the set entry
  // (by calling `Detach`).
  std::set<raw_ptr<WebContentsImpl>> web_contents_set_;
};

Visibility FrameVisibilityToVisibility(
    blink::mojom::FrameVisibility visibility) {
  switch (visibility) {
    case blink::mojom::FrameVisibility::kRenderedInViewport:
      return Visibility::VISIBLE;
    case blink::mojom::FrameVisibility::kRenderedOutOfViewport:
      return Visibility::OCCLUDED;
    case blink::mojom::FrameVisibility::kNotRendered:
      return Visibility::HIDDEN;
  }
}

}  // namespace

WebContentsImpl::WebContentsImpl(BrowserContext* browser_context)
    : ColorProviderSourceObserver(DefaultColorProviderSource::GetInstance()),
      delegate_(nullptr),
      render_view_host_delegate_view_(nullptr),
      opened_by_another_window_(false),
      node_(this),
      primary_frame_tree_(browser_context,
                          this,
                          this,
                          this,
                          this,
                          this,
                          this,
                          this,
                          this,
                          FrameTree::Type::kPrimary),
      primary_main_frame_process_status_(
          base::TERMINATION_STATUS_STILL_RUNNING),
      primary_main_frame_process_error_code_(0),
      load_state_(net::LOAD_STATE_IDLE, std::u16string()),
      upload_size_(0),
      upload_position_(0),
      is_resume_pending_(false),
      notify_disconnection_(false),
      is_showing_before_unload_dialog_(false),
      last_active_time_ticks_(base::TimeTicks::Now()),
      last_active_time_(base::Time::Now()),
      closed_by_user_gesture_(false),
      minimum_zoom_percent_(
          static_cast<int>(blink::kMinimumBrowserZoomFactor * 100)),
      maximum_zoom_percent_(
          static_cast<int>(blink::kMaximumBrowserZoomFactor * 100)),
      zoom_scroll_remainder_(0),
      force_disable_overscroll_content_(false),
      last_dialog_suppressed_(false),
      audio_stream_monitor_(this),
      media_web_contents_observer_(
          std::make_unique<MediaWebContentsObserver>(this)),
      is_overlay_content_(false),
      showing_context_menu_(false),
      prerender_host_registry_(std::make_unique<PrerenderHostRegistry>(*this)),
      compositor_frame_sink_grouping_id_(base::UnguessableToken::Create()) {
  TRACE_EVENT0("content", "WebContentsImpl::WebContentsImpl");
  back_forward_cache_ = std::make_unique<BackForwardCacheImpl>(*this);
  WebContentsOfBrowserContext::Attach(*this);
  node_.SetFocusedFrameTree(&primary_frame_tree_);
#if BUILDFLAG(IS_ANDROID)
  safe_area_insets_host_ = SafeAreaInsetsHost::Create(this);
#endif

  auto* const native_theme = ui::NativeTheme::GetInstanceForWeb();
  native_theme_observation_.Observe(native_theme);
  slow_web_preference_cache_observation_.Observe(
      SlowWebPreferenceCache::GetInstance());
  renderer_preferences_.caret_blink_interval =
      native_theme->caret_blink_interval();
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
    BUILDFLAG(IS_WIN)
  renderer_preferences_.use_overlay_scrollbar =
      native_theme->use_overlay_scrollbar();
#endif

  screen_change_monitor_ =
      std::make_unique<ScreenChangeMonitor>(base::BindRepeating(
          &WebContentsImpl::OnScreensChange, base::Unretained(this)));
}

WebContentsImpl::~WebContentsImpl() {
  base::trace_event::TraceSessionObserverList::RemoveObserver(this);
  TRACE_EVENT0("content", "WebContentsImpl::~WebContentsImpl");
  if (tracing_track_) {
    TRACE_STATE("content.web_contents.lifecycle", nullptr,
                tracing_track_->track());
  }

  WebContentsOfBrowserContext::Detach(*this);

  // Imperfect sanity check against double free, given some crashes unexpectedly
  // observed in the wild.
  CHECK(!IsBeingDestroyed());

  // We generally keep track of is_being_destroyed_ to let other features know
  // to avoid certain actions during destruction.
  is_being_destroyed_ = true;

  // A WebContents should never be deleted while it is notifying observers,
  // since this will lead to a use-after-free as it continues to notify later
  // observers.
  CHECK(!observers_.is_notifying_observers());
  // This is used to watch for one-off observers (i.e. non-WebContentsObservers)
  // destroying WebContents, which they should not do.
  CHECK(!prevent_destruction_);

  // We usually record `max_loaded_frame_count_` in `DidFinishNavigation()`
  // for pages the user navigated away from other than the last one. We record
  // it for the last page here.
  if (first_primary_navigation_completed_) {
    RecordMaxFrameCountUMA(max_loaded_frame_count_);
  }

  FullscreenContentsSet(GetBrowserContext())->erase(this);

  touch_emulator_.reset();

  rwh_input_event_router_.reset();

  if (text_input_manager_ && text_input_manager_->HasObserver(this)) {
    text_input_manager_->RemoveObserver(this);
  }

  WebContentsImpl* outermost = GetOutermostWebContents();
  if (this != outermost && ContainsOrIsFocusedWebContents()) {
    // If the current WebContents is in focus, unset it.
    outermost->SetAsFocusedWebContentsIfNecessary();
  }

  if (GetOuterWebContents() &&
      GetOuterWebContents()->node_.IsUnownedInnerWebContents(this)) {
    GetOuterWebContents()->DetachUnownedInnerWebContents(this);
  }

  if (pointer_lock_widget_) {
    pointer_lock_widget_->RejectPointerLockOrUnlockIfNecessary(
        blink::mojom::PointerLockResult::kElementDestroyed);

    // Normally, the call above clears pointer_lock_widget_ pointers on the
    // entire WebContents chain, since it results in calling LostPointerLock()
    // when the pointer lock is already active. However, this doesn't work for
    // <webview> guests if the pointer lock request is still pending while the
    // <webview> is destroyed. Hence, ensure that all pointer lock widget
    // pointers are cleared. See https://crbug.com/1346245.
    SetPointerLockWidgetInParentChain(nullptr);
  }

  // Auto-detach any WebContents embedded in this one via SurfaceEmbed.
  // Swap to a local since Detach modifies surface_embed_children_.
  std::vector<base::WeakPtr<WebContents>> children;
  children.swap(surface_embed_children_);
  for (auto& child_weak : children) {
    if (WebContents* child = child_weak.get()) {
      SurfaceEmbedConnector::Detach(child);
    }
  }

  if (surface_embed_connector_) {
    ClearSurfaceEmbedConnector();
  }

  for (auto& itr : created_widgets_) {
    itr.second->DetachDelegate();
  }
  created_widgets_.clear();

  // Clear out any JavaScript state.
  CancelDialogManagerDialogs(/*reset_state=*/true);

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  color_chooser_holder_.reset();
#endif
  find_request_manager_.reset();

  // crbug.com/373898450: The `FrameTree` should outlive the animation manager.
  if (view_) {
    view_->DestroyBackForwardTransitionAnimationManager();
  }

  // Shutdown the primary FrameTree.
  primary_frame_tree_.Shutdown();

  // Shutdown the non-primary FrameTrees.
  //
  // Do this here rather than relying on the owner of the FrameTree to shutdown
  // on WebContentsDestroyed(), so that all the FrameTrees are shutdown at the
  // same time for consistency. Also, destroying a FrameTree results in other
  // observer functions like RenderFrameDeleted() being called, which are not
  // expected to be called after WebContentsDestroyed().
  //
  // Currently the only instances of the non-primary FrameTrees are for
  // prerendering. Shutdown them by destructing PrerenderHostRegistry.
  prerender_host_registry_.reset();

  // If audio is playing then notify external observers of the audio stream
  // disappearing.
  if (is_currently_audible_) {
    is_currently_audible_ = false;
    observers_.NotifyObservers(&WebContentsObserver::OnAudioStateChanged,
                               false);
    if (GetOuterWebContents()) {
      GetOuterWebContents()->OnAudioStateChanged();
    }
  }

  // |save_package_| is refcounted so make sure we clear the page before
  // we toss out our reference.
  if (save_package_) {
    save_package_->ClearPage();
  }

  observers_.NotifyObservers(&WebContentsObserver::WebContentsDestroyed);

#if BUILDFLAG(IS_ANDROID)
  // Destroy the WebContentsAndroid here, so that its observers still can access
  // `this`.
  ClearWebContentsAndroid();
#endif

  observers_.NotifyObservers(&WebContentsObserver::ResetWebContents);
  SetDelegate(nullptr);
}

std::unique_ptr<WebContentsImpl> WebContentsImpl::CreateWithOpener(
    const WebContents::CreateParams& params,
    RenderFrameHostImpl* opener_rfh) {
  OPTIONAL_TRACE_EVENT1("browser", "WebContentsImpl::CreateWithOpener",
                        "opener", opener_rfh);
  FrameTreeNode* opener = nullptr;
  if (opener_rfh) {
    opener = opener_rfh->frame_tree_node();
  }
  std::unique_ptr<WebContentsImpl> new_contents(
      new WebContentsImpl(params.browser_context));
  new_contents->SetOpenerForNewContents(opener, params.opener_suppressed);

  // If the opener is sandboxed, a new popup must inherit the opener's sandbox
  // flags, and these flags take effect immediately.  An exception is if the
  // opener's sandbox flags lack the PropagatesToAuxiliaryBrowsingContexts
  // bit (which is controlled by the "allow-popups-to-escape-sandbox" token).
  // See https://html.spec.whatwg.org/C/#attr-iframe-sandbox.
  FrameTreeNode* new_root = new_contents->GetPrimaryFrameTree().root();
  if (opener) {
    network::mojom::WebSandboxFlags opener_flags =
        opener_rfh->active_sandbox_flags();
    if (opener_rfh->IsSandboxed(network::mojom::WebSandboxFlags::
                                    kPropagatesToAuxiliaryBrowsingContexts)) {
      new_root->SetPendingFramePolicy(
          {opener_flags,
           {} /* container_policy */,
           {} /* required_document_policy */,
           blink::mojom::DeferredFetchPolicy::kDisabled});
    }
    new_root->SetInitialPopupURL(params.initial_popup_url);
    new_root->SetPopupCreatorOrigin(opener_rfh->GetLastCommittedOrigin());
  }

  // Apply starting sandbox flags.
  blink::FramePolicy frame_policy(new_root->pending_frame_policy());
  frame_policy.sandbox_flags |= params.starting_sandbox_flags;
  new_root->SetPendingFramePolicy(frame_policy);

  // This may be true even when opener is null, such as when opening blocked
  // popups.
  if (params.opened_by_another_window) {
    new_contents->opened_by_another_window_ = true;
  }

  WebContentsImpl* outer_web_contents = nullptr;
  if (params.guest_delegate) {
    // This makes |new_contents| act as a guest.
    // For more info, see comment above class BrowserPluginGuest.
    BrowserPluginGuest::CreateInWebContents(new_contents.get(),
                                            params.guest_delegate);
    outer_web_contents = static_cast<WebContentsImpl*>(
        params.guest_delegate->GetOwnerWebContents());
  }

  // Multi-network CCT relies on the following invariant: WebContents associated
  // with a CCT tab targeting a network will always have
  // WebContents::GetTargetNetwork == that target network. We MUST set the
  // target_network before WebContents initialization. Otherwise, the renderer,
  // during WebContents initialization, might create (and keep around)
  // URLLoaderFactories that won't load resources from the target network. For
  // WebContents created from Java (e.g., WebContents rendering a CCT), this is
  // guaranteed by the Java side setting
  // WebContents::CreateParams::target_network_ when that CCT is targeting a
  // network.
  new_contents->target_network_ = params.target_network;
  if (new_contents->target_network_ == net::handles::kInvalidNetworkHandle) {
    // For WebContents opened by another WebContents (e.g. through
    // window.open(), instead of from the UI / by the embedder),
    // WebContents::CreateParams::target_network_ won't be set, as the calling
    // code is not aware of multi-network CCT. Instead, to handle that, we pass
    // along the target network (if present) of the WebContents associated with
    // the opener frame.
    auto* web_contents = WebContentsImpl::FromRenderFrameHostImpl(opener_rfh);
    if (web_contents) {
      new_contents->target_network_ = web_contents->GetTargetNetwork();
    }
  }

  new_contents->Init(params, frame_policy);
  if (outer_web_contents) {
    outer_web_contents->InnerWebContentsCreated(new_contents.get());
  }
  return new_contents;
}

// static
std::vector<raw_ptr<WebContentsImpl>> WebContentsImpl::GetAllWebContents() {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::GetAllWebContents");
  std::vector<raw_ptr<WebContentsImpl>> result;
  std::unique_ptr<RenderWidgetHostIterator> widgets(
      RenderWidgetHostImpl::GetRenderWidgetHosts());
  while (RenderWidgetHost* rwh = widgets->GetNextHost()) {
    RenderViewHost* rvh = RenderViewHost::From(rwh);
    if (!rvh) {
      continue;
    }
    WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
    if (!web_contents || web_contents->IsBeingDestroyed()) {
      continue;
    }
    if (web_contents->GetPrimaryMainFrame()->GetRenderViewHost() != rvh) {
      continue;
    }
    // Because a WebContents can only have one current RVH at a time, there will
    // be no duplicate WebContents here.
    result.push_back(static_cast<WebContentsImpl*>(web_contents));
  }
  return result;
}

// static
WebContentsImpl* WebContentsImpl::FromFrameTreeNode(
    const FrameTreeNode* frame_tree_node) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::FromFrameTreeNode", "frame_tree_node",
                        static_cast<const void*>(frame_tree_node));
  return static_cast<WebContentsImpl*>(
      WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host()));
}

// static
WebContents* WebContentsImpl::FromRenderFrameHostID(
    GlobalRenderFrameHostId render_frame_host_id) {
  OPTIONAL_TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::FromRenderFrameHostID", "process_id",
                        render_frame_host_id.child_id, "frame_id",
                        render_frame_host_id.frame_routing_id);
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
  RenderFrameHost* render_frame_host =
      RenderFrameHost::FromID(render_frame_host_id);
  if (!render_frame_host) {
    return nullptr;
  }

  return WebContents::FromRenderFrameHost(render_frame_host);
}

// static
WebContents* WebContentsImpl::FromRenderFrameHostID(int render_process_host_id,
                                                    int render_frame_host_id) {
  return FromRenderFrameHostID(
      GlobalRenderFrameHostId(render_process_host_id, render_frame_host_id));
}

// static
WebContentsImpl* WebContentsImpl::FromOuterFrameTreeNode(
    const FrameTreeNode* frame_tree_node) {
  OPTIONAL_TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::FromOuterFrameTreeNode",
                        "frame_tree_node",
                        static_cast<const void*>(frame_tree_node));
  return WebContentsImpl::FromFrameTreeNode(frame_tree_node)
      ->node_.GetInnerWebContentsInFrame(frame_tree_node);
}

std::string WebContentsImpl::GetTitleForMediaControls() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::GetTitleForMediaControls");

  if (!delegate_) {
    return std::string();
  }
  return delegate_->GetTitleForMediaControls(this);
}

// Returns the NavigationController for the primary FrameTree, i.e. the one
// whose URL is shown in the omnibox. With MPArch we can have multiple
// FrameTrees in one WebContents and each has its own NavigationController.
// TODO(crbug.com/40165692): Make sure callers are aware of this.
NavigationControllerImpl& WebContentsImpl::GetController() {
  return primary_frame_tree_.controller();
}

const NavigationControllerImpl& WebContentsImpl::GetController() const {
  return primary_frame_tree_.controller();
}

BrowserContext* WebContentsImpl::GetBrowserContext() {
  return GetController().GetBrowserContext();
}

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

const WebContents::UniqueToken& WebContentsImpl::GetUniqueToken() const {
  return web_contents_token_;
}

const perfetto::Track& WebContentsImpl::GetTracingTrack() const {
  CHECK(tracing_track_);
  return tracing_track_->track();
}

const GURL& WebContentsImpl::GetURL() {
  return GetVisibleURL();
}

const GURL& WebContentsImpl::GetVisibleURL() {
  // We may not have a navigation entry yet.
  NavigationEntry* entry = GetController().GetVisibleEntry();
  return entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
}

const GURL& WebContentsImpl::GetLastCommittedURL() const {
  // We may not have a navigation entry yet.
  const NavigationEntry* entry = GetController().GetLastCommittedEntry();
  return entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
}

WebContentsDelegate* WebContentsImpl::GetDelegate() {
  return delegate_;
}

void WebContentsImpl::SetDelegate(WebContentsDelegate* delegate) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetDelegate", "delegate",
                        static_cast<void*>(delegate));
  // TODO(cbentzel): remove this debugging code?
  if (delegate == delegate_) {
    return;
  }
  const bool had_delegate = !!delegate_;
  if (had_delegate) {
    delegate_->Detach(this);
  }
  delegate_ = delegate;
  if (delegate_) {
    delegate_->Attach(this);
    // RenderFrameDevToolsAgentHost should not be told about the WebContents
    // until there is a `delegate_`.
    if (!had_delegate) {
      RenderFrameDevToolsAgentHost::AttachToWebContents(this);
    }
  }

  // Re-read values from the new delegate and apply them.
  if (view_) {
    view_->SetOverscrollControllerEnabled(CanOverscrollContent());
  }
}

void WebContentsImpl::OptOutFrameEviction(
    base::PassKey<FrameEvictionOptOutClient>) {
  if (opt_out_frame_eviction_) {
    return;
  }
  opt_out_frame_eviction_ = true;
  if (RenderWidgetHostViewBase* view =
          static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView())) {
    view->OptOutFrameEviction();
  }
}

const RenderFrameHostImpl* WebContentsImpl::GetPrimaryMainFrame() const {
  return primary_frame_tree_.root()->current_frame_host();
}

RenderFrameHostImpl* WebContentsImpl::GetPrimaryMainFrame() {
  return const_cast<RenderFrameHostImpl*>(
      std::as_const(*this).GetPrimaryMainFrame());
}

PageImpl& WebContentsImpl::GetPrimaryPage() {
  // We should not be accessing Page during the destruction of this WebContents,
  // as the Page has already been cleared.
  //
  // Please note that IsBeingDestroyed() should be checked to ensure that we
  // don't access Page related data that is going to be destroyed.
  CHECK(primary_frame_tree_.root()->current_frame_host());
  return primary_frame_tree_.root()->current_frame_host()->GetPage();
}

RenderFrameHostImpl* WebContentsImpl::GetFocusedFrame() {
  // If this method is called on an inner WebContents, don't return frames from
  // outside of the inner WebContents's subtree.
  if (!ContainsOrIsFocusedWebContents()) {
    return nullptr;
  }

  FrameTreeNode* focused_node = GetFocusedFrameTree()->GetFocusedFrame();
  if (!focused_node) {
    return nullptr;
  }

  // If an inner frame tree has focus, we should return a RenderFrameHost from
  // the inner frame tree and not the placeholder RenderFrameHost.
  DCHECK(focused_node->current_frame_host()
             ->inner_tree_main_frame_tree_node_id()
             .is_null());

  return focused_node->current_frame_host();
}

bool WebContentsImpl::IsPrerenderedFrame(FrameTreeNodeId frame_tree_node_id) {
  if (frame_tree_node_id.is_null()) {
    return false;
  }

  FrameTreeNode* frame_tree_node =
      FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (!frame_tree_node) {
    return false;
  }

  // In the case of inner frame trees in a prerender, the inner frame tree's
  // type, would not be FrameTree::Type::kPrerender. So if this is not the
  // outermost frame, we use the lifecycle state of the document that owns this.
  // TODO(1196715, 1232528): This relies on the LifecycleState being correct
  // in the case of inner frame trees.
  if (frame_tree_node->GetParentOrOuterDocumentOrEmbedder()) {
    return frame_tree_node->GetParentOrOuterDocumentOrEmbedder()
               ->lifecycle_state() ==
           RenderFrameHostImpl::LifecycleStateImpl::kPrerendering;
  }
  return frame_tree_node->GetFrameType() == FrameType::kPrerenderMainFrame;
}

RenderFrameHostImpl* WebContentsImpl::UnsafeFindFrameByFrameTreeNodeId(
    FrameTreeNodeId frame_tree_node_id) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::UnsafeFindFrameByFrameTreeNodeId",
                        "frame_tree_node_id", frame_tree_node_id);
  // Beware using this! The RenderFrameHost may have changed since the caller
  // obtained frame_tree_node_id.
  FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (!ftn) {
    return nullptr;
  }
  if (this != WebContents::FromRenderFrameHost(ftn->current_frame_host())) {
    return nullptr;
  }
  return ftn->current_frame_host();
}

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

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

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

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

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

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

void WebContentsImpl::ForEachRenderFrameHostImpl(
    base::FunctionRef<FrameIterationAction(RenderFrameHostImpl*)> on_frame,
    bool include_speculative) {
  // Since `RenderFrameHostImpl::ForEachRenderFrameHostImpl` will reach the
  // RenderFrameHosts descending from a specified root, it is enough to start
  // iteration from each of the outermost main frames to reach everything in
  // this WebContents. However, if iteration stops early in
  // `RenderFrameHostImpl::ForEachRenderFrameHostImpl`, we also need to stop
  // early by not iterating over additional outermost main frames.
  bool iteration_stopped = false;
  auto on_frame_with_termination =
      [on_frame, &iteration_stopped](RenderFrameHostImpl* rfh) {
        const auto action = on_frame(rfh);
        if (action == FrameIterationAction::kStop) {
          iteration_stopped = true;
        }
        return action;
      };

  for (auto* rfh : GetOutermostMainFrames()) {
    if (include_speculative) {
      rfh->ForEachRenderFrameHostImplIncludingSpeculativeWithAction(
          on_frame_with_termination);
    } else {
      rfh->ForEachRenderFrameHostImplWithAction(on_frame_with_termination);
    }

    if (iteration_stopped) {
      return;
    }
  }
}

void WebContentsImpl::ForEachFrameTree(
    FrameTreeIterationCallback on_frame_tree) {
  // Consider all outermost frame trees, and for each, iterate through their
  // FrameTreeNodes to find any inner frame trees.
  for (FrameTree* outermost_frame_tree : GetOutermostFrameTrees()) {
    FrameTree::NodeRange node_range =
        outermost_frame_tree->NodesIncludingInnerTreeNodes();
    FrameTree::NodeIterator node_iter = node_range.begin();
    while (node_iter != node_range.end()) {
      FrameTreeNode* node = *node_iter;
      if (FromFrameTreeNode(node) != this) {
        // Exclude any inner frame trees based on multi-WebContents
        // architecture.
        node_iter.AdvanceSkippingChildren();
      } else {
        if (node->IsMainFrame()) {
          on_frame_tree(node->frame_tree());
        }
        ++node_iter;
      }
    }
  }
}

std::vector<FrameTree*> WebContentsImpl::GetOutermostFrameTrees() {
  // If the WebContentsImpl is being destroyed, then we should not
  // perform the tree traversal.
  if (IsBeingDestroyed()) {
    return {};
  }

  std::vector<FrameTree*> result;
  result.push_back(&GetPrimaryFrameTree());

  const std::vector<FrameTree*> prerender_frame_trees =
      GetPrerenderHostRegistry()->GetPrerenderFrameTrees();
  result.insert(result.end(), prerender_frame_trees.begin(),
                prerender_frame_trees.end());

  return result;
}

std::vector<RenderFrameHostImpl*> WebContentsImpl::GetOutermostMainFrames() {
  // Do nothing if the WebContents is currently being initialized or destroyed.
  if (!GetPrimaryMainFrame()) {
    return {};
  }

  std::vector<RenderFrameHostImpl*> result;

  for (FrameTree* outermost_frame_tree : GetOutermostFrameTrees()) {
    DCHECK(outermost_frame_tree->GetMainFrame());
    result.push_back(outermost_frame_tree->GetMainFrame());
  }

  for (const auto& entry : GetController().GetBackForwardCache().GetEntries()) {
    result.push_back(entry->render_frame_host());
  }

  // In the case of inner WebContents, we still allow this method to be called,
  // but the semantics of the values being returned are "outermost
  // within this WebContents" as opposed to truly outermost. When this method is
  // called for an inner WebContents in a normal browser, we would not expect
  // any other outermost pages besides the primary page.
  //
  // Note that for an inner WebContents in a WebUIBrowser (detectable here when
  // the AttachUnownedInnerWebContents feature is enabled), `result.size()` may
  // sometimes exceed 1. For example, for WebUIBrowser, when a prerendering code
  // path is triggered, a prerender frame tree is generated, but the path to
  // activate or discard it does not run before this point.
  DCHECK(
      !GetOuterWebContents() || (result.size() == 1) ||
      base::FeatureList::IsEnabled(features::kAttachUnownedInnerWebContents));

  return result;
}

std::vector<RenderFrameHostImpl*>
WebContentsImpl::GetOutermostMainFramesForViewChange() {
  // Do nothing if the WebContents is currently being initialized or destroyed.
  if (!GetPrimaryMainFrame()) {
    return {};
  }

  std::vector<RenderFrameHostImpl*> result;

  result.push_back(GetPrimaryMainFrame());

  if (auto* speculative_rfh = GetPrimaryFrameTree()
                                  .root()
                                  ->render_manager()
                                  ->speculative_frame_host()) {
    result.push_back(speculative_rfh);
  }

  for (FrameTree* prerender_frame_tree :
       GetPrerenderHostRegistry()->GetPrerenderFrameTrees()) {
    DCHECK(prerender_frame_tree->GetMainFrame());
    result.push_back(prerender_frame_tree->GetMainFrame());
  }

  for (const auto& entry : GetController().GetBackForwardCache().GetEntries()) {
    result.push_back(entry->render_frame_host());
  }

  return result;
}

#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
void WebContentsImpl::DecrementForbidExternalPopupMenus() {
  DCHECK_GE(external_popup_menus_forbid_counter_, 1);
  external_popup_menus_forbid_counter_--;
  NotifyPreferencesChanged();
}
#endif  // BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)

void WebContentsImpl::ExecutePageBroadcastMethod(
    PageBroadcastMethodCallback callback) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ExecutePageBroadcastMethod");
  primary_frame_tree_.root()->render_manager()->ExecutePageBroadcastMethod(
      callback);
}

void WebContentsImpl::ExecutePageBroadcastMethodForAllPages(
    PageBroadcastMethodCallback callback) {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::ExecutePageBroadcastMethodForAllPages");
  ForEachFrameTree([callback](FrameTree& frame_tree) {
    frame_tree.root()->render_manager()->ExecutePageBroadcastMethod(callback);
  });
}

RenderViewHostImpl* WebContentsImpl::GetRenderViewHost() {
  return GetRenderManager()->current_frame_host()->render_view_host();
}

void WebContentsImpl::CancelActiveAndPendingDialogs() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::CancelActiveAndPendingDialogs");
  CancelDialogManagerDialogs(/*reset_state=*/false);
  if (browser_plugin_embedder_) {
    browser_plugin_embedder_->CancelGuestDialogs();
  }
}

void WebContentsImpl::ClosePage() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ClosePage");
  GetPrimaryMainFrame()->ClosePage(
      RenderFrameHostImpl::ClosePageSource::kBrowser);
}

RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() {
  return GetRenderManager()->GetRenderWidgetHostView();
}

RenderWidgetHostView* WebContentsImpl::GetTopLevelRenderWidgetHostView() {
  if (auto* connector = GetSurfaceEmbedConnector()) {
    return static_cast<SurfaceEmbedConnectorImpl*>(connector)
        ->GetRootRenderWidgetHostView();
  }
  if (WebContentsImpl* outer = GetOuterWebContents()) {
    return outer->GetTopLevelRenderWidgetHostView();
  }
  return GetRenderManager()->GetRenderWidgetHostView();
}

std::vector<RenderWidgetHostView*> WebContentsImpl::GetPopupWidgets() {
  std::vector<RenderWidgetHostView*> result;
  for (const auto& [_, host] : created_widgets_) {
    RenderWidgetHostViewBase* view = host->GetView();
    if (view && view->GetWidgetType() == WidgetType::kPopup) {
      result.push_back(view);
    }
  }
  return result;
}

RenderWidgetHost* WebContentsImpl::FindWidgetAtPoint(const gfx::PointF& point) {
  if (GetOuterWebContents()) {
    return GetOuterWebContents()->FindWidgetAtPoint(point);
  }
  gfx::PointF transformed_point;
  input::RenderWidgetHostViewInput* rwhvi =
      GetInputEventRouter()->GetRenderWidgetHostViewInputAtPoint(
          static_cast<RenderWidgetHostViewBase*>(
              GetTopLevelRenderWidgetHostView()),
          point, &transformed_point);

  RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From(
      static_cast<RenderWidgetHostViewBase*>(rwhvi)->GetRenderWidgetHost());
  if (!widget_host) {
    return nullptr;
  }
  return widget_host;
}

WebContentsView* WebContentsImpl::GetView() const {
  return view_.get();
}

void WebContentsImpl::OnScreensChange(bool is_multi_screen_changed) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnScreensChange",
                        "is_multi_screen_changed", is_multi_screen_changed);
  // Mac display info may originate from a remote process hosting the NSWindow;
  // this local process display::Screen signal should not trigger updates.
  // TODO(crbug.com/40165350): Unify screen info plumbing, caching, etc.
#if !BUILDFLAG(IS_MAC)
  // This updates Screen attributes and fires Screen.change events as needed,
  // propagating to all widgets through the VisualProperties update waterfall.
  // This is triggered by system changes, not renderer IPC, so explicitly check
  // that the RenderWidgetHostView is valid before sending an update.
  if (RenderWidgetHostViewBase* view =
          GetRenderViewHost()->GetWidget()->GetView()) {
    // Only update top-level views, as child frames will have their ScreenInfos
    // updated by the visual property flow.
    if (!view->IsRenderWidgetHostViewChildFrame()) {
      view->UpdateScreenInfo();
    }
  }
#endif  // !BUILDFLAG(IS_MAC)
}

void WebContentsImpl::OnScreenOrientationChange() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::OnScreenOrientationChange");
  DCHECK(screen_orientation_provider_);
  DidChangeScreenOrientation();
  screen_orientation_provider_->OnOrientationChange();
}

std::optional<SkColor> WebContentsImpl::GetThemeColor() {
  return GetPrimaryPage().theme_color();
}

std::optional<SkColor> WebContentsImpl::GetBackgroundColor() {
  return GetPrimaryPage().background_color();
}

void WebContentsImpl::SetPageBaseBackgroundColor(std::optional<SkColor> color) {
  if (page_base_background_color_ == color) {
    return;
  }
  page_base_background_color_ = color;
  ExecutePageBroadcastMethod([color](RenderViewHostImpl* rvh) {
    // Null `broadcast` can happen before view is created on the renderer
    // side, in which case this color will be sent in CreateView.
    if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
      broadcast->SetPageBaseBackgroundColor(color);
    }
  });
}

void WebContentsImpl::SetColorProviderSource(ui::ColorProviderSource* source) {
  ColorProviderSourceObserver::Observe(source);
}

ui::ColorProviderKey::ColorMode WebContentsImpl::GetColorMode() const {
  // A ColorProviderSource should always be set.
  auto* source = GetColorProviderSource();
  CHECK(source);
  return source->GetColorMode();
}

void WebContentsImpl::SetAccessibilityMode(ui::AXMode mode) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetAccessibilityMode",
                        "mode", mode.ToString(), "previous_mode",
                        accessibility_mode_.ToString());

  if (unrecoverable_accessibility_error_) {
    DUMP_WILL_BE_CHECK(accessibility_mode_.is_mode_off());
    return;
  }

  if (mode == accessibility_mode_) {
    return;
  }

  // Don't allow accessibility to be enabled for WebContents that are never
  // user-visible, like background pages.
  CHECK(!is_never_composited_);

  accessibility_mode_ = mode;

  // Update state for all frames in this tree and inner trees, including
  // speculative frame hosts and those in the back-forward cache. Take care not
  // to touch frame hosts that belong to another WebContents (e.g., guest views)
  // -- it is the responsibility of other WebContents' to update their own
  // frames. Note that content::BrowserAccessibilityState will propagate mode
  // flag changes that target the whole process (CreateScopedModeForProcess) or
  // a BrowserContext (CreateScopedModeForBrowserContext) to all relevant
  // WebContents, so inner WebContents will automatically receive the same mode
  // flag changes. In cases where mode flag changes apply to a specific
  // WebContents (via CreateScopedModeForWebContents), it is the responsibility
  // of the owner of the ScopedAccessibilityMode to choose whether or not to
  // create scopers for inner WebContents.
  ForEachRenderFrameHostImplIncludingSpeculativeWithAction(
      [this](RenderFrameHostImpl* frame_host) {
        if (WebContentsImpl::FromRenderFrameHostImpl(frame_host) == this) {
          frame_host->UpdateAccessibilityMode();
          return FrameIterationAction::kContinue;
        }
        return FrameIterationAction::kSkipChildren;
      });

  // Start recording if a request to record events has been received and this is
  // the first time that at least kNativeAPIs has been requested.
  if (pending_recording_ &&
      accessibility_mode_.has_mode(ui::AXMode::kNativeAPIs)) {
    PendingRecording pending = *std::exchange(pending_recording_, std::nullopt);
    StartRecordingAccessibilityEvents(pending.api_type,
                                      std::move(pending.callback));
  }
}

void WebContentsImpl::DidCapturedSurfaceControl() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  observers_.NotifyObservers(&WebContentsObserver::OnCapturedSurfaceControl);
}

void WebContentsImpl::OnFedCmFederatedLogin(
    webid::FederatedLoginResult result) {
  observers_.NotifyObservers(&WebContentsObserver::OnFedCmFederatedLogin,
                             result == webid::FederatedLoginResult::kSuccess);

  // OnFedCmFederatedLogin() may be invoked while the WebContents is being
  // destroyed, so be careful when trying to access the Page.
  if (IsBeingDestroyed()) {
    return;
  }

  webid::FederatedEmbedderLoginRequest* embedder_login_request =
      webid::FederatedEmbedderLoginRequest::Get(this);
  if (embedder_login_request) {
    embedder_login_request->OnFederatedResultReceived(result);
  }
}

void WebContentsImpl::ResetAccessibility() {
  // Reset accessibility for all frames in this tree and inner trees, including
  // speculative frame hosts and those in the back-forward cache. See comment in
  // `SetAccessibilityMode()` for more details.
  ForEachRenderFrameHostImplIncludingSpeculativeWithAction(
      [this](RenderFrameHostImpl* frame_host) {
        if (WebContentsImpl::FromRenderFrameHostImpl(frame_host) == this) {
          frame_host->AccessibilityReset();
          return FrameIterationAction::kContinue;
        }
        return FrameIterationAction::kSkipChildren;
      });
}

// Helper class used by WebContentsImpl::RequestAXTreeSnapshot.
// Handles the callbacks from parallel snapshot requests to each frame,
// and feeds the results to an AXTreeCombiner, which converts them into a
// single combined accessibility tree.
class AXTreeSnapshotCombiner : public base::RefCounted<AXTreeSnapshotCombiner> {
 public:
  AXTreeSnapshotCombiner(WebContents::AXTreeSnapshotCallback callback,
                         mojom::SnapshotAccessibilityTreeParamsPtr params)
      : callback_(std::move(callback)), params_(std::move(params)) {}

  WebContents::AXTreeSnapshotCallback AddFrame(bool is_root) {
    // Adds a reference to |this|.
    return base::BindOnce(&AXTreeSnapshotCombiner::ReceiveSnapshot, this,
                          is_root);
  }

  void ReceiveSnapshot(bool is_root, ui::AXTreeUpdate& snapshot) {
    combiner_.AddTree(snapshot, is_root);
  }

  void AXTreeSnapshotOnFrame(RenderFrameHostImpl* rfhi) {
    OPTIONAL_TRACE_EVENT0("content",
                          "AXTreeSnapshotCombiner::AXTreeSnapshotOnFrame");
    bool is_root = rfhi->AccessibilityIsRootFrame();
    rfhi->RequestAXTreeSnapshot(AddFrame(is_root), params_.Clone());
  }

 private:
  friend class base::RefCounted<AXTreeSnapshotCombiner>;

  // This is called automatically after the last call to ReceiveSnapshot
  // when there are no more references to this object.
  ~AXTreeSnapshotCombiner() {
    combiner_.Combine();
    CHECK(combiner_.combined());
    ui::AXTreeUpdate update = std::move(combiner_.combined().value());
    std::move(callback_).Run(update);
  }

  ui::AXTreeCombiner combiner_;
  WebContents::AXTreeSnapshotCallback callback_;
  mojom::SnapshotAccessibilityTreeParamsPtr params_;
};

void WebContentsImpl::RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
                                            ui::AXMode ax_mode,
                                            size_t max_nodes,
                                            base::TimeDelta timeout,
                                            AXTreeSnapshotPolicy policy) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RequestAXTreeSnapshot",
                        "mode", ax_mode.ToString());

  // Inline text boxes are not supported in snapshots, as they are extra noise
  // and expensive. If they are needed in the future, remove this line.
  ax_mode.set_mode(ui::AXMode::kInlineTextBoxes, false);

  // Send a request to each of the frames in parallel. Each one will return
  // an accessibility tree snapshot, and AXTreeSnapshotCombiner will combine
  // them into a single tree and call |callback| with that result, then
  // delete |combiner|.
  auto params = mojom::SnapshotAccessibilityTreeParams::New();
  params->ax_mode = ax_mode.flags();
  params->max_nodes = max_nodes;
  params->timeout = timeout;

  auto combiner = base::MakeRefCounted<AXTreeSnapshotCombiner>(
      std::move(callback), std::move(params));
  GetPrimaryMainFrame()->ForEachRenderFrameHostImplWithAction(
      [this, &combiner, policy](RenderFrameHostImpl* rfh) {
        switch (policy) {
          case AXTreeSnapshotPolicy::kAll:
            break;
          case AXTreeSnapshotPolicy::kSameOriginDirectDescendants:
            if (GetPrimaryMainFrame()->GetSiteInstance() !=
                    rfh->GetSiteInstance() ||
                rfh->IsFencedFrameRoot() ||
                !GetPrimaryMainFrame()
                     ->GetLastCommittedOrigin()
                     .IsSameOriginWith(rfh->GetLastCommittedOrigin())) {
              return FrameIterationAction::kSkipChildren;
            }
            break;
        }
        combiner->AXTreeSnapshotOnFrame(rfh);
        return FrameIterationAction::kContinue;
      });
}

void WebContentsImpl::NotifyViewportFitChanged(
    blink::mojom::ViewportFit value) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::NotifyViewportFitChanged",
                        "value", static_cast<int>(value));
  observers_.NotifyObservers(&WebContentsObserver::ViewportFitChanged, value);
}

void WebContentsImpl::NotifySafeAreaConstraintChanged(bool has_constraint) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::NotifySafeAreaConstraintChanged",
                        "has_constraint", has_constraint);
  observers_.NotifyObservers(&WebContentsObserver::SafeAreaConstraintChanged,
                             has_constraint);
}

FindRequestManager* WebContentsImpl::GetFindRequestManagerForTesting() {
  return GetOrCreateFindRequestManager();
}

void WebContentsImpl::UpdateZoom() {
  UpdateZoom(GetPrimaryMainFrame()->GetGlobalId());
}

void WebContentsImpl::UpdateZoom(const GlobalRenderFrameHostId& rfh_id) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::UpdateZoom");
  RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(rfh_id);
  RenderWidgetHostImpl* rwh = rfh->GetRenderWidgetHost();
  if (rwh->GetView()) {
    rwh->SynchronizeVisualProperties();
  }
}

void WebContentsImpl::UpdateZoomIfNecessary(const std::string& scheme,
                                            const std::string& host) {
  UpdateZoomIfNecessary(scheme, host, GetPrimaryMainFrame());
}

void WebContentsImpl::UpdateZoomIfNecessary(const std::string& scheme,
                                            const std::string& host,
                                            RenderFrameHostImpl* rfh) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::UpdateZoomIfNecessary",
                        "scheme", scheme, "host", host);

  // The following code expects that rfh has its own independent zoom.
  GURL url = HostZoomMap::GetURLForRenderFrameHost(rfh->GetGlobalId());
  if (host != net::GetHostOrSpecFromURL(url) ||
      (!scheme.empty() && !url.SchemeIs(scheme))) {
    return;
  }

  UpdateZoom(rfh->GetGlobalId());
}

std::vector<WebContentsImpl*> WebContentsImpl::GetWebContentsAndAllInner() {
  std::vector<WebContentsImpl*> all_contents(1, this);

  for (size_t i = 0; i != all_contents.size(); ++i) {
    for (auto* inner_contents : all_contents[i]->GetInnerWebContents()) {
      all_contents.push_back(static_cast<WebContentsImpl*>(inner_contents));
    }
  }

  return all_contents;
}

void WebContentsImpl::OnManifestUrlChanged(PageImpl& page) {
  std::optional<GURL> manifest_url = page.GetManifestUrl();
  if (!manifest_url.has_value()) {
    return;
  }

  if (!page.IsPrimary()) {
    return;
  }

  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::NotifyManifestUrlChanged",
                        "render_frame_host", &page.GetMainDocument(),
                        "manifest_url", manifest_url);
  observers_.NotifyObservers(&WebContentsObserver::DidUpdateWebManifestURL,
                             &page.GetMainDocument(), *manifest_url);
}

WebUI* WebContentsImpl::GetWebUI() {
  // There is no frame host if the navigation fails.
  if (!primary_frame_tree_.root()->current_frame_host()) {
    return nullptr;
  }

  return primary_frame_tree_.root()->current_frame_host()->web_ui();
}

void WebContentsImpl::SetAlwaysSendSubresourceNotifications() {
  if (!base::FeatureList::IsEnabled(
          features::kReduceSubresourceResponseStartedIPC)) {
    return;
  }

  if (GetSendSubresourceNotification()) {
    return;
  }

  // Updates all the renderers if the user allows certificate error or HTTP
  // exception, but doesn't update renderers when all exceptions are revoked
  // from all hosts since this causes superfluous IPCs.
  for (WebContentsImpl* web_contents : GetAllWebContents()) {
    DCHECK(!web_contents->GetSendSubresourceNotification());
    web_contents->renderer_preferences_.send_subresource_notification = true;
    web_contents->SyncRendererPrefs();
  }
}

bool WebContentsImpl::GetSendSubresourceNotification() {
  return GetRendererPrefs(GetRenderViewHost()).send_subresource_notification;
}

void WebContentsImpl::SetUserAgentOverride(
    const blink::UserAgentOverride& ua_override,
    bool override_in_new_tabs) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetUserAgentOverride",
                        "ua_override", ua_override.ua_string_override,
                        "override_in_new_tabs", override_in_new_tabs);
  DCHECK(!ua_override.ua_metadata_override.has_value() ||
         !ua_override.ua_string_override.empty());

  if (GetUserAgentOverride() == ua_override) {
    return;
  }

  if (!ua_override.ua_string_override.empty() &&
      !net::HttpUtil::IsValidHeaderValue(ua_override.ua_string_override)) {
    return;
  }

  should_override_user_agent_in_new_tabs_ = override_in_new_tabs;

  // Update any in-flight load requests with overrides for new tabs.
  if (delayed_load_url_params_.get()) {
    delayed_load_url_params_->override_user_agent =
        override_in_new_tabs ? NavigationController::UA_OVERRIDE_TRUE
                             : NavigationController::UA_OVERRIDE_FALSE;
  }

  renderer_preferences_.user_agent_override = ua_override;

  // Send the new override string to all renderers in the current page.
  SyncRendererPrefs();

  // Reload the page if a load is currently in progress to avoid having
  // different parts of the page loaded using different user agents.
  // No need to reload if the current entry matches that of the
  // NavigationRequest supplied to DidStartNavigation() as NavigationRequest
  // handles it.
  ForEachFrameTree([](FrameTree& frame_tree) {
    // For prerendering, we don't want to activate a prerendered page loaded
    // with a stale UA and will handle it even if it finishes loading.
    if (!frame_tree.IsLoadingIncludingInnerFrameTrees() &&
        !frame_tree.is_prerendering()) {
      return;
    }

    NavigationEntry* entry = frame_tree.controller().GetVisibleEntry();
    if (!entry || !entry->GetIsOverridingUserAgent()) {
      return;
    }
    if (frame_tree.root()->navigation_request() &&
        !frame_tree.root()->navigation_request()->ua_change_requires_reload()) {
      return;
    }
    if (frame_tree.is_prerendering()) {
      // Just cancel if the FrameTree is for prerendering, as prerendered
      // page may not allow another navigation including a reload, depending
      // on conditions.
      frame_tree.GetMainFrame()->CancelPrerendering(PrerenderCancellationReason(
          PrerenderFinalStatus::kUaChangeRequiresReload));
    } else {
      frame_tree.controller().Reload(ReloadType::BYPASSING_CACHE, true);
    }
  });

  observers_.NotifyObservers(&WebContentsObserver::UserAgentOverrideSet,
                             ua_override);
}

const blink::UserAgentOverride& WebContentsImpl::GetUserAgentOverride() {
  return renderer_preferences_.user_agent_override;
}

const blink::UserAgentOverride& WebContentsImpl::GetUserAgentOverride(
    FrameTree& frame_tree) {
  if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
          *frame_tree.GetMainFrame())) {
    return guest->GetRendererPrefs().user_agent_override;
  }
  return renderer_preferences_.user_agent_override;
}

bool WebContentsImpl::ShouldOverrideUserAgentForRendererInitiatedNavigation() {
  // Inherits the current entry setting if it exists and not initial.
  NavigationEntryImpl* current_entry = GetController().GetLastCommittedEntry();
  if (current_entry && !current_entry->IsInitialEntry()) {
    return current_entry->GetIsOverridingUserAgent();
  }
  // Otherwise, follows the setting from the last user-agent override. See
  // `override_in_new_tabs` in `SetUserAgentOverride()`.
  return should_override_user_agent_in_new_tabs_;
}

bool WebContentsImpl::IsWebContentsOnlyAccessibilityModeForTesting() {
  return accessibility_mode_ == ui::kAXModeWebContentsOnly;
}

bool WebContentsImpl::IsFullAccessibilityModeForTesting() {
  return accessibility_mode_ == ui::kAXModeDefaultForTests;
}

#if BUILDFLAG(IS_ANDROID)

void WebContentsImpl::SetDisplayCutoutSafeArea(gfx::Insets insets) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetDisplayCutoutSafeArea");
  if (safe_area_insets_host_) {
    safe_area_insets_host_->SetDisplayCutoutSafeArea(insets);
  }
}

void WebContentsImpl::ShowInterestInElement(int nodeID) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ShowInterestInElement");
  if (auto* rwhv = GetRenderWidgetHostView()) {
    rwhv->ShowInterestInElement(nodeID);
  }
}

#endif

const std::u16string& WebContentsImpl::GetTitle() {
  WebUI* our_web_ui =
      GetRenderManager()->speculative_frame_host()
          ? GetRenderManager()->speculative_frame_host()->web_ui()
          : GetRenderManager()->current_frame_host()->web_ui();
  if (our_web_ui) {
    // Don't override the title in view source mode.
    NavigationEntry* entry = GetController().GetVisibleEntry();
    if (!(entry && entry->IsViewSourceMode())) {
      // Give the Web UI the chance to override our title.
      const std::u16string& title = our_web_ui->GetOverriddenTitle();
      if (!title.empty()) {
        return title;
      }
    }
  }

  return GetNavigationEntryForTitle()->GetTitleForDisplay();
}

const std::optional<std::u16string>& WebContentsImpl::GetApplicationTitle() {
  return GetNavigationEntryForTitle()->GetApplicationTitle();
}

SiteInstanceImpl* WebContentsImpl::GetSiteInstance() {
  return GetRenderManager()->current_frame_host()->GetSiteInstance();
}

bool WebContentsImpl::IsLoading() {
  return primary_frame_tree_.IsLoadingIncludingInnerFrameTrees(
      /*exclude_ad_subframes=*/false);
}

bool WebContentsImpl::IsLoadingExcludingAdSubframes() const {
  return primary_frame_tree_.IsLoadingIncludingInnerFrameTrees(
      /*exclude_ad_subframes=*/true);
}

double WebContentsImpl::GetLoadProgress() {
  // TODO(crbug.com/40177943): Make this MPArch friendly considering primary
  // frame tree and its descendants.
  return primary_frame_tree_.GetLoadProgress();
}

bool WebContentsImpl::ShouldShowLoadingUI() {
  return primary_frame_tree_.GetLoadingState() ==
         LoadingState::LOADING_UI_REQUESTED;
}

bool WebContentsImpl::IsDocumentOnLoadCompletedInPrimaryMainFrame() {
  // TODO(mparch): This should be moved to Page, and callers should use it
  // directly.
  return GetPrimaryPage().is_on_load_completed_in_main_document();
}

bool WebContentsImpl::IsWaitingForResponse() {
  NavigationRequest* ongoing_navigation_request =
      primary_frame_tree_.root()->navigation_request();

  // An ongoing navigation request means we're waiting for a response.
  return ongoing_navigation_request != nullptr;
}

bool WebContentsImpl::HasUncommittedNavigationInPrimaryMainFrame() {
  return primary_frame_tree_.root()->HasNavigation();
}

const net::LoadStateWithParam& WebContentsImpl::GetLoadState() {
  return load_state_;
}

const std::u16string& WebContentsImpl::GetLoadStateHost() {
  return load_state_host_;
}

uint64_t WebContentsImpl::GetUploadSize() {
  return upload_size_;
}

uint64_t WebContentsImpl::GetUploadPosition() {
  return upload_position_;
}

const std::string& WebContentsImpl::GetEncoding() {
  return GetPrimaryPage().GetEncoding();
}

void WebContentsImpl::Discard(base::OnceClosure on_discarded_cb) {
  if (!base::FeatureList::IsEnabled(features::kWebContentsDiscard)) {
    NOTREACHED();
  }
  if (WasDiscarded()) {
    // TODO(crbug.com/441841249): Consider updating `on_discarded_cb` to return
    // a bool to indicate whether the operation completed successfully.
    return;
  }

  AboutToBeDiscarded(this);
  notify_disconnection_ = false;
  CancelAllPrerendering();
  primary_frame_tree_.Discard(std::move(on_discarded_cb));
  NotifyWasDiscarded();
}

bool WebContentsImpl::WasDiscarded() {
  return GetPrimaryFrameTree().root()->was_discarded();
}

void WebContentsImpl::SetWasDiscarded(bool was_discarded) {
  // It's set based on a tab and the setting value is started from a primary
  // tree and propagated to all children nodes including a fenced frame node.
  GetPrimaryFrameTree().root()->set_was_discarded();
}

base::ScopedClosureRunner WebContentsImpl::IncrementCapturerCount(
    const gfx::Size& capture_size,
    bool stay_hidden,
    bool stay_awake,
    bool is_activity) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::IncrementCapturerCount");
  DCHECK(!IsBeingDestroyed());
  if (stay_hidden) {
    // A hidden capture should not have side effect on the web contents, so it
    // should not pass a non-empty |capture_size| which will cause side effect.
    DCHECK(capture_size.IsEmpty());
    ++hidden_capturer_count_;
  } else {
    ++visible_capturer_count_;
  }

  if (stay_awake) {
    ++stay_awake_capturer_count_;
  }

  OnCapturerCountChanged();

  // Note: This provides a hint to upstream code to size the views optimally
  // for quality (e.g., to avoid scaling).
  if (!capture_size.IsEmpty() && preferred_size_for_capture_.IsEmpty()) {
    preferred_size_for_capture_ = capture_size;
    OnPreferredSizeChanged(preferred_size_);
  }

  if (!capture_wake_lock_ && stay_awake_capturer_count_) {
    if (auto* wake_lock_context = GetWakeLockContext()) {
      auto receiver = capture_wake_lock_.BindNewPipeAndPassReceiver();
      wake_lock_context->GetWakeLock(
          device::mojom::WakeLockType::kPreventDisplaySleepAllowDimming,
          device::mojom::WakeLockReason::kOther, "Capturing",
          std::move(receiver));
    }
  }

  if (capture_wake_lock_) {
    capture_wake_lock_->RequestWakeLock();
  }

  UpdateVisibilityAndNotifyPageAndView(GetVisibility(), is_activity);

  return base::ScopedClosureRunner(base::BindOnce(
      &WebContentsImpl::DecrementCapturerCount, weak_factory_.GetWeakPtr(),
      stay_hidden, stay_awake, is_activity));
}

bool WebContentsImpl::IsBeingCaptured() {
  return visible_capturer_count_ + hidden_capturer_count_ > 0;
}

bool WebContentsImpl::IsBeingVisiblyCaptured() {
  return visible_capturer_count_ > 0;
}

void WebContentsImpl::OnCapturerCountChanged() {
  view_->OnCapturerCountChanged();
  if (surface_embed_connector_) {
    surface_embed_connector_->SetKeepSurfaceAlive(IsBeingCaptured());
  }
}

#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
base::ScopedClosureRunner WebContentsImpl::ForbidExternalPopupMenus() {
  // Disable external popups when forbidden, and re-enable them after.
  external_popup_menus_forbid_counter_++;
  NotifyPreferencesChanged();
  return base::ScopedClosureRunner(
      base::BindOnce(&WebContentsImpl::DecrementForbidExternalPopupMenus,
                     weak_factory_.GetWeakPtr()));
}
#endif  // BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)

bool WebContentsImpl::IsAudioMuted() {
  return audio_stream_factory_ && audio_stream_factory_->IsMuted();
}

void WebContentsImpl::SetAudioMuted(bool mute) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetAudioMuted", "mute",
                        mute);
  DVLOG(1) << "SetAudioMuted(mute=" << mute << "), was " << IsAudioMuted()
           << " for WebContentsImpl@" << this;

  if (mute == IsAudioMuted()) {
    return;
  }

  GetAudioStreamFactory()->SetMuted(mute);

  // Notfiy all guest page holders that we are muting.
  ForEachFrameTree([mute](FrameTree& frame_tree) {
    if (frame_tree.is_guest()) {
      GuestPageHolderImpl* guest_page_holder =
          GuestPageHolderImpl::FromRenderFrameHost(
              *frame_tree.root()->current_frame_host());
      CHECK(guest_page_holder);
      guest_page_holder->SetAudioMutedFromWebContents(mute);
    }
  });

  observers_.NotifyObservers(&WebContentsObserver::DidUpdateAudioMutingState,
                             mute);
  // Notification for UI updates in response to the changed muting state.
  NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);
}

bool WebContentsImpl::IsCurrentlyAudible() {
  return is_currently_audible_;
}

bool WebContentsImpl::IsCapabilityActive(
    WebContentsCapabilityType capability_type) {
  switch (capability_type) {
    case WebContentsCapabilityType::kBluetoothConnected:
      return bluetooth_connected_device_count_ > 0;
    case WebContentsCapabilityType::kBluetoothScanning:
      return bluetooth_scanning_sessions_count_ > 0;
    case WebContentsCapabilityType::kSerial:
      return serial_active_frame_count_ > 0;
    case WebContentsCapabilityType::kHID:
      return hid_active_frame_count_ > 0;
    case WebContentsCapabilityType::kUSB:
      return usb_active_frame_count_ > 0;
    case WebContentsCapabilityType::kGeolocation:
      return geolocation_active_frame_count_ > 0;
  }
}

bool WebContentsImpl::HasFileSystemAccessHandles() {
  return file_system_access_handle_count_ > 0;
}

bool WebContentsImpl::HasPictureInPictureVideo() {
  return has_picture_in_picture_video_;
}

bool WebContentsImpl::HasPictureInPictureDocument() {
  return has_picture_in_picture_document_;
}

void WebContentsImpl::SetHasPictureInPictureCommon(
    bool has_picture_in_picture) {
  NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
  observers_.NotifyObservers(&WebContentsObserver::MediaPictureInPictureChanged,
                             has_picture_in_picture);

  // Picture-in-picture state can affect how we notify visibility for non-
  // visible pages.
  if (visibility_ != Visibility::VISIBLE) {
    UpdateVisibilityAndNotifyPageAndView(visibility_);
  }
}

void WebContentsImpl::DisallowCustomCursorScopeExpired() {
  --disallow_custom_cursor_scope_count_;
}

void WebContentsImpl::SetHasPictureInPictureVideo(
    bool has_picture_in_picture_video) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SetHasPictureInPictureVideo",
                        "has_pip_video", has_picture_in_picture_video);
  // If status of |this| is already accurate, there is no need to update.
  if (has_picture_in_picture_video == has_picture_in_picture_video_) {
    return;
  }
  has_picture_in_picture_video_ = has_picture_in_picture_video;
  SetHasPictureInPictureCommon(has_picture_in_picture_video);
}

void WebContentsImpl::SetHasPictureInPictureDocument(
    bool has_picture_in_picture_document) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SetHasPictureInPictureDocument",
                        "has_pip_document", has_picture_in_picture_document);
  // If status of |this| is already accurate, there is no need to update.
  if (has_picture_in_picture_document == has_picture_in_picture_document_) {
    return;
  }
  has_picture_in_picture_document_ = has_picture_in_picture_document;
  SetHasPictureInPictureCommon(has_picture_in_picture_document);
}

bool WebContentsImpl::IsCrashed() {
  switch (primary_main_frame_process_status_) {
    case base::TERMINATION_STATUS_PROCESS_CRASHED:
    case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
    case base::TERMINATION_STATUS_OOM:
    case base::TERMINATION_STATUS_EVICTED_FOR_MEMORY:
    case base::TERMINATION_STATUS_LAUNCH_FAILED:
#if BUILDFLAG(IS_CHROMEOS)
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
#if BUILDFLAG(IS_ANDROID)
    case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
#if BUILDFLAG(IS_WIN)
    case base::TERMINATION_STATUS_INTEGRITY_FAILURE:
#endif
      return true;
    case base::TERMINATION_STATUS_NORMAL_TERMINATION:
    case base::TERMINATION_STATUS_STILL_RUNNING:
      return false;
    case base::TERMINATION_STATUS_MAX_ENUM:
      NOTREACHED();
  }

  NOTREACHED();
}

void WebContentsImpl::SetPrimaryMainFrameProcessStatus(
    base::TerminationStatus status,
    int error_code) {
  OPTIONAL_TRACE_EVENT2("content",
                        "WebContentsImpl::SetPrimaryMainFrameProcessStatus",
                        "status", static_cast<int>(status), "old_status",
                        static_cast<int>(primary_main_frame_process_status_));
  if (status == primary_main_frame_process_status_) {
    return;
  }

  primary_main_frame_process_status_ = status;
  primary_main_frame_process_error_code_ = error_code;
  NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
}

base::TerminationStatus WebContentsImpl::GetCrashedStatus() {
  return primary_main_frame_process_status_;
}

int WebContentsImpl::GetCrashedErrorCode() {
  return primary_main_frame_process_error_code_;
}

bool WebContentsImpl::IsBeingDestroyed() {
  return is_being_destroyed_;
}

void WebContentsImpl::NotifyNavigationStateChanged(
    InvalidateTypes changed_flags) {
  TRACE_EVENT1("content,navigation",
               "WebContentsImpl::NotifyNavigationStateChanged", "changed_flags",
               static_cast<int>(changed_flags));
  // Notify the media observer of potential audibility changes.
  if (changed_flags & INVALIDATE_TYPE_AUDIO) {
    media_web_contents_observer_->MaybeUpdateAudibleState();
  }

  if (delegate_) {
    delegate_->NavigationStateChanged(this, changed_flags);
  }

  if (GetOuterWebContents()) {
    GetOuterWebContents()->NotifyNavigationStateChanged(changed_flags);
  }
}

void WebContentsImpl::OnVerticalScrollDirectionChanged(
    viz::VerticalScrollDirection scroll_direction) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnVerticalScrollDirectionChanged",
                        "scroll_direction", static_cast<int>(scroll_direction));
  observers_.NotifyObservers(
      &WebContentsObserver::DidChangeVerticalScrollDirection, scroll_direction);
}

int WebContentsImpl::GetVirtualKeyboardResizeHeight() {
  // Only consider a web contents to be insetted by the virtual keyboard if it
  // is in the currently active tab.
  if (GetVisibility() != Visibility::VISIBLE) {
    return 0;
  }

  // The only mode where the virtual keyboard causes the web contents to be
  // resized is kResizesContent.
  if (GetPrimaryPage().virtual_keyboard_mode() !=
      ui::mojom::VirtualKeyboardMode::kResizesContent) {
    return 0;
  }

  // The virtual keyboard never resizes content when fullscreened.
  if (IsFullscreen()) {
    return 0;
  }

  return GetDelegate() ? GetDelegate()->GetVirtualKeyboardHeight(this) : 0;
}

bool WebContentsImpl::ShouldDoLearning() {
  return !GetBrowserContext()->IsOffTheRecord();
}

void WebContentsImpl::OnAudioStateChanged() {
  // This notification can come from any embedded contents or from this
  // WebContents' stream monitor. Aggregate these signals to get the actual
  // state.
  //
  // Note that guests may not be attached as inner contents, and so may need to
  // be checked separately.  This intentionally does not do a recursive search,
  // since the state is aggregated in each inner WebContents and reflects that
  // whole subtree.
  bool is_currently_audible =
      audio_stream_monitor_.IsCurrentlyAudible() ||
      (browser_plugin_embedder_ &&
       browser_plugin_embedder_->AreAnyGuestsCurrentlyAudible()) ||
      std::ranges::any_of(GetInnerWebContents(),
                          [](WebContents* inner_contents) {
                            return inner_contents->IsCurrentlyAudible();
                          });
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnAudioStateChanged",
                        "is_currently_audible", is_currently_audible,
                        "was_audible", is_currently_audible_);
  if (is_currently_audible == is_currently_audible_) {
    return;
  }

  // Update internal state.
  is_currently_audible_ = is_currently_audible;
  was_ever_audible_ = was_ever_audible_ || is_currently_audible_;

  ExecutePageBroadcastMethod([is_currently_audible](RenderViewHostImpl* rvh) {
    if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
      broadcast->AudioStateChanged(is_currently_audible);
    }
  });

  // Notification for UI updates in response to the changed audio state.
  NotifyNavigationStateChanged(INVALIDATE_TYPE_AUDIO);

  // Ensure that audio state changes propagate from innermost to outermost
  // WebContents.
  if (GetOuterWebContents()) {
    GetOuterWebContents()->OnAudioStateChanged();
  }

  observers_.NotifyObservers(&WebContentsObserver::OnAudioStateChanged,
                             is_currently_audible_);
}

base::TimeTicks WebContentsImpl::GetLastActiveTimeTicks() {
  return last_active_time_ticks_;
}

base::Time WebContentsImpl::GetLastActiveTime() {
  return last_active_time_;
}

void WebContentsImpl::WasShown() {
  TRACE_EVENT0("content", "WebContentsImpl::WasShown");
  UpdateVisibilityAndNotifyPageAndView(Visibility::VISIBLE);
}

void WebContentsImpl::WasHidden() {
  TRACE_EVENT0("content", "WebContentsImpl::WasHidden");
  UpdateVisibilityAndNotifyPageAndView(Visibility::HIDDEN);
}

bool WebContentsImpl::HasRecentInteraction() {
  if (last_interaction_time_.is_null()) {
    return false;
  }

  static constexpr base::TimeDelta kMaxInterval = base::Seconds(5);
  base::TimeDelta delta = ui::EventTimeForNow() - last_interaction_time_;
  // Note: the expectation is that the caller is typically expecting an input
  // event, e.g. validating that a WebUI message that requires a gesture is
  // actually attached to a gesture.
  return delta <= kMaxInterval;
}

base::TimeTicks WebContentsImpl::GetLastInteractionTimeTicks() {
  return last_interaction_time_;
}

WebContents::ScopedIgnoreInputEvents WebContentsImpl::IgnoreInputEvents(
    std::optional<WebInputEventAuditCallback> audit_callback) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::IgnoreInputEvents");

  uint64_t callback_id = 0;
  if (audit_callback.has_value()) {
    do {
      callback_id = next_web_input_event_audit_callback_id_++;
    } while (web_input_event_audit_callbacks_.contains(callback_id));
    web_input_event_audit_callbacks_[callback_id] = std::move(*audit_callback);
  } else {
#if BUILDFLAG(IS_ANDROID)
    if (ignore_input_events_count_ == 0) {
      // Reset gesture detection before starting input suppression so that any
      // ongoing scroll gesture is correctly finished.
      //
      // TODO(crbug.com/362301376): This might be a side-effect of the
      // referenced bug. Revisit restoring the CHECK when it's resolved.
      if (auto* view = GetRenderWidgetHostView()) {
        static_cast<RenderWidgetHostViewBase*>(view)->ResetGestureDetection();
      }
    }
#endif
    ++ignore_input_events_count_;
  }

  // Bind weakly, since the token might outlive us.
  return ScopedIgnoreInputEvents(base::BindOnce(
      [](base::WeakPtr<WebContentsImpl> wc,
         std::optional<uint64_t> callback_id) {
        if (wc) {
          OPTIONAL_TRACE_EVENT0("content",
                                "WebContentsImpl::IgnoreInputEvents.Release");
          if (callback_id.has_value()) {
            auto it = wc->web_input_event_audit_callbacks_.find(*callback_id);
            CHECK(it != wc->web_input_event_audit_callbacks_.end());
            wc->web_input_event_audit_callbacks_.erase(it);
          } else {
#if BUILDFLAG(IS_ANDROID)
            // Reset gesture detection so that we don't continue to generate new
            // gestures from suppressed touches. These suppressed gestures would
            // otherwise confuse the event stream validator when input is
            // re-enabled.
            //
            // This needs to be done while input is still suppressed since
            // resetting can generate gesture end events for a gesture sequence
            // which was being suppressed.
            if (wc->ignore_input_events_count_ == 1) {
              if (auto* view = wc->GetRenderWidgetHostView()) {
                static_cast<RenderWidgetHostViewBase*>(view)
                    ->ResetGestureDetection();
              }
            }
#endif
            --wc->ignore_input_events_count_;
          }
        }
      },
      weak_factory_.GetWeakPtr(),
      audit_callback.has_value() ? std::make_optional<uint64_t>(callback_id)
                                 : std::nullopt));
}

bool WebContentsImpl::ShouldIgnoreInputEventsForTesting() {
  return ShouldIgnoreInputEvents();
}

bool WebContentsImpl::HasActiveEffectivelyFullscreenVideo() {
  return IsFullscreen() &&
         media_web_contents_observer_->HasActiveEffectivelyFullscreenVideo();
}

void WebContentsImpl::WriteIntoTrace(perfetto::TracedValue context) {
  auto dict = std::move(context).WriteDictionary();
  dict.Add("root_frame_tree_node_id",
           primary_frame_tree_.root()->frame_tree_node_id());
}

const base::Location& WebContentsImpl::GetCreatorLocation() {
  return creator_location_;
}

const std::optional<blink::mojom::PictureInPictureWindowOptions>&
WebContentsImpl::GetPictureInPictureOptions() const {
  return picture_in_picture_options_;
}

#if BUILDFLAG(IS_ANDROID)
ChildProcessImportance
WebContentsImpl::GetPrimaryMainFrameImportanceForTesting() {
  return GetPrimaryMainFrame()->GetRenderWidgetHost()->importance();
}

ChildProcessImportance
WebContentsImpl::GetPrimaryPageSubframeImportanceForTesting() {
  return primary_subframe_importance_;
}

void WebContentsImpl::SetPrimaryPageImportance(
    ChildProcessImportance main_frame_importance,
    ChildProcessImportance subframe_importance) {
  OPTIONAL_TRACE_EVENT2(
      "content", "WebContentsImpl::SetPrimaryPageImportance",
      "main_frame_importance", static_cast<int>(main_frame_importance),
      "subframe_importance", static_cast<int>(subframe_importance));
  CHECK(IsNotPerceptibleImportanceSupported() ||
        (main_frame_importance != ChildProcessImportance::NOT_PERCEPTIBLE &&
         subframe_importance != ChildProcessImportance::NOT_PERCEPTIBLE))
      << "Setter of ChildProcessImportance::NOT_PERCEPTIBLE should be aware of "
         "the support and avoid using NOT_PERCEPTIBLE if "
         "IsNotPerceptibleImportanceSupported() is false";
  CHECK(main_frame_importance >= subframe_importance);

  // Batch service binding updates for the renderer processes of the main frame
  // and the subframes.
  base::android::ScopedServiceBindingBatch scoped_service_binding_batch;

  if (subframe_importance != primary_subframe_importance_) {
    primary_subframe_importance_ = subframe_importance;
    ApplyPrimaryPageSubframeImportance();
  }

  GetPrimaryMainFrame()->GetRenderWidgetHost()->SetImportance(
      main_frame_importance);
}

void WebContentsImpl::ApplyPrimaryPageSubframeImportance() {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::ApplyPrimaryPageSubframeImportance",
      "importance", static_cast<int>(primary_subframe_importance_));
  for (FrameTreeNode* node : primary_frame_tree_.Nodes()) {
    if (node->IsMainFrame()) {
      continue;
    }
    if (auto* rwh = node->current_frame_host()->GetLocalRenderWidgetHost()) {
      rwh->SetImportance(primary_subframe_importance_);
    }
  }
}
#endif

void WebContentsImpl::WasOccluded() {
  TRACE_EVENT0("content", "WebContentsImpl::WasOccluded");
  UpdateVisibilityAndNotifyPageAndView(Visibility::OCCLUDED);
}

Visibility WebContentsImpl::GetVisibility() {
  return visibility_;
}

bool WebContentsImpl::NeedToFireBeforeUnloadOrUnloadEvents() {
  if (!notify_disconnection_) {
    return false;
  }

  // Don't fire if the main frame indicates that beforeunload and unload have
  // already executed (e.g., after receiving a ClosePage ACK) or should be
  // ignored.
  if (GetPrimaryMainFrame()->IsPageReadyToBeClosed()) {
    return false;
  }

  // Check whether any frame in the frame tree needs to run beforeunload or
  // unload-time event handlers.
  for (FrameTreeNode* node : primary_frame_tree_.Nodes()) {
    RenderFrameHostImpl* rfh = node->current_frame_host();

    // No need to run beforeunload/unload-time events if the RenderFrame isn't
    // live.
    if (!rfh->IsRenderFrameLive()) {
      continue;
    }
    bool should_run_before_unload_handler =
        rfh->GetSuddenTerminationDisablerState(
            blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler);
    bool should_run_unload_handler = rfh->GetSuddenTerminationDisablerState(
        blink::mojom::SuddenTerminationDisablerType::kUnloadHandler);
    bool should_run_page_hide_handler = rfh->GetSuddenTerminationDisablerState(
        blink::mojom::SuddenTerminationDisablerType::kPageHideHandler);
    auto* rvh = static_cast<RenderViewHostImpl*>(rfh->GetRenderViewHost());
    // If the tab is already hidden, we should not run visibilitychange
    // handlers.
    bool is_page_visible = rvh->GetPageLifecycleStateManager()
                               ->CalculatePageLifecycleState()
                               ->visibility == PageVisibilityState::kVisible;

    bool should_run_visibility_change_handler =
        is_page_visible && rfh->GetSuddenTerminationDisablerState(
                               blink::mojom::SuddenTerminationDisablerType::
                                   kVisibilityChangeHandler);
    if (should_run_before_unload_handler || should_run_unload_handler ||
        should_run_page_hide_handler || should_run_visibility_change_handler) {
      return true;
    }
  }

  return false;
}

void WebContentsImpl::DispatchBeforeUnload(bool auto_cancel) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DispatchBeforeUnload",
                        "auto_cancel", auto_cancel);
  auto before_unload_type =
      auto_cancel ? RenderFrameHostImpl::BeforeUnloadType::DISCARD
                  : RenderFrameHostImpl::BeforeUnloadType::TAB_CLOSE;
  GetPrimaryMainFrame()->DispatchBeforeUnload(before_unload_type, false);
}

bool WebContentsImpl::IsInnerWebContentsForGuest() {
  return IsGuest();
}

void WebContentsImpl::AttachInnerWebContents(
    std::unique_ptr<WebContents> inner_web_contents,
    RenderFrameHost* render_frame_host,
    bool is_full_page) {
  // Not reachable with MPArch based guest view.
  CHECK(!base::FeatureList::IsEnabled(features::kGuestViewMPArch));
  AttachInnerWebContentsImpl(inner_web_contents.release(), render_frame_host,
                             is_full_page,
                             /*should_take_ownership=*/true);
}

void WebContentsImpl::AttachUnownedInnerWebContents(
    base::PassKey<UnownedInnerWebContentsClient>,
    WebContents* inner_web_contents,
    RenderFrameHost* render_frame_host) {
  AttachInnerWebContentsImpl(inner_web_contents, render_frame_host,
                             /*is_full_page=*/false,
                             /*should_take_ownership=*/false);
}

void WebContentsImpl::AttachInnerWebContentsImpl(
    WebContents* inner_web_contents,
    RenderFrameHost* render_frame_host,
    bool is_full_page,
    bool should_take_ownership) {
  OPTIONAL_TRACE_EVENT("content", "WebContentsImpl::AttachInnerWebContents",
                       "inner_web_contents",
                       static_cast<void*>(inner_web_contents), "is_full_page",
                       is_full_page, "should_take_ownership",
                       should_take_ownership);
  WebContentsImpl* inner_web_contents_impl =
      static_cast<WebContentsImpl*>(inner_web_contents);
  DCHECK(!inner_web_contents_impl->node_.outer_web_contents());
  auto* render_frame_host_impl =
      static_cast<RenderFrameHostImpl*>(render_frame_host);
  DCHECK_EQ(this, WebContents::FromRenderFrameHost(render_frame_host_impl));
  DCHECK(render_frame_host_impl->GetParent());

  // Inner WebContents aren't supported with prerendering. See
  // https://crbug.com/40191159 for details.
  CHECK_NE(RenderFrameHostImpl::LifecycleStateImpl::kPrerendering,
           render_frame_host_impl->lifecycle_state());

  RenderFrameHostManager* inner_render_manager =
      inner_web_contents_impl->GetRenderManager();
  RenderFrameHostImpl* inner_main_frame =
      inner_render_manager->current_frame_host();
  auto* outer_render_manager =
      render_frame_host_impl->frame_tree_node()->render_manager();

  // Make |render_frame_host_impl| an outer delegate frame.
  render_frame_host_impl->set_inner_tree_main_frame_tree_node_id(
      inner_main_frame->frame_tree_node()->frame_tree_node_id());

  // When attaching a WebContents as an inner WebContents, we need to replace
  // the Webcontents' view with a WebContentsViewChildFrame.
  inner_web_contents_impl->view_ = std::make_unique<WebContentsViewChildFrame>(
      inner_web_contents_impl,
      GetContentClient()->browser()->GetWebContentsViewDelegate(
          inner_web_contents_impl),
      &inner_web_contents_impl->render_view_host_delegate_view_);

  // Update views for all outermost main frames impacted by the view change of
  // the inner WebContents, including speculative render frame host and the
  // ones in prerender and BFCache.
  for (auto* rfh :
       inner_web_contents_impl->GetOutermostMainFramesForViewChange()) {
    // On platforms where destroying the WebContents' view does not also destroy
    // the platform RenderWidgetHostView, we need to destroy it if it exists.
    // TODO(mcnee): Should all platforms' WebContentsView destroy the platform
    // RWHV?
    if (RenderWidgetHostViewBase* prev_rwhv =
            static_cast<RenderWidgetHostViewBase*>(rfh->GetView())) {
      if (!prev_rwhv->IsRenderWidgetHostViewChildFrame()) {
        prev_rwhv->Destroy();
      }
    }

    // When the WebContents being initialized has not already navigated, the
    // browser side Render{View,Frame}Host must be initialized and the
    // RenderWidgetHostView created. This is needed because the usual
    // initialization happens during the first navigation, but not all guest
    // types navigate before attaching. If the browser side is already
    // initialized, the calls below will just early return.
    auto* render_manager = rfh->frame_tree_node()->render_manager();
    RenderViewHostImpl* rvh = rfh->render_view_host();
    render_manager->InitRenderView(rfh->GetSiteInstance()->group(), rvh,
                                   /*proxy=*/nullptr,
                                   /*navigation_metrics_token=*/std::nullopt);
    if (!rfh->GetView()) {
      inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager(rvh);
    }
  }

  inner_web_contents_impl->RecursivelyUnregisterRenderWidgetHostViews();

  // Create a link to our outer WebContents.
  node_.AttachInnerWebContents(inner_web_contents, render_frame_host_impl,
                               should_take_ownership);

  // Create a proxy in top-level RenderFrameHostManager, pointing to the
  // SiteInstanceGroup of the outer WebContents. The proxy will be used to send
  // postMessage to the inner WebContents.
  auto* proxy =
      inner_main_frame->browsing_context_state()->CreateOuterDelegateProxy(
          render_frame_host_impl->GetSiteInstance()->group(),
          inner_main_frame->frame_tree_node(), blink::RemoteFrameToken());
  // Since the inner WebContents is created from the browser side we do
  // not have RemoteFrame mojo channels. New channels will be bound when the
  // `CreateView` IPC is sent.

  // When attaching a GuestView as an inner WebContents, there should already be
  // a live RenderFrame, which has to be swapped.
  if (render_frame_host_impl->IsRenderFrameLive()) {
    inner_render_manager->SwapOuterDelegateFrame(
        render_frame_host_impl, proxy,
        inner_main_frame->devtools_frame_token());

    inner_web_contents_impl->ReattachToOuterWebContentsFrame();
  }

  if (primary_frame_tree_.GetFocusedFrame() ==
      render_frame_host_impl->frame_tree_node()) {
    inner_web_contents_impl->SetFocusedFrame(
        inner_web_contents_impl->primary_frame_tree_.root(),
        render_frame_host_impl->GetSiteInstance()->group());
  }
  outer_render_manager->set_attach_inner_delegate_complete();

  // If the inner WebContents is full frame, give it focus.
  if (is_full_page) {
    // There should only ever be one inner WebContents when |is_full_page| is
    // true, and it is the one we just attached.
    DCHECK_EQ(1u, node_.GetInnerWebContents().size());
    inner_web_contents_impl->SetAsFocusedWebContentsIfNecessary();
  }

  // Synchronize visual properties so that the inner main frame's renderer
  // process immediately receives initial throttling status upon being attached
  // as an embedded main frame.
  if (auto* rwh = inner_web_contents_impl->GetPrimaryMainFrame()
                      ->GetRenderWidgetHost()) {
    rwh->SynchronizeVisualProperties();
  }

  observers_.NotifyObservers(&WebContentsObserver::InnerWebContentsAttached,
                             inner_web_contents_impl, render_frame_host);

  // Make sure that the inner web contents and its outer delegate get properly
  // linked via the embedding token now that inner web contents are attached.
  inner_main_frame->PropagateEmbeddingTokenToParentFrame();
}

void WebContentsImpl::DetachUnownedInnerWebContents(
    base::PassKey<UnownedInnerWebContentsClient>,
    WebContents* inner_web_contents) {
  DetachUnownedInnerWebContents(inner_web_contents);
}

void WebContentsImpl::DetachUnownedInnerWebContents(
    WebContents* inner_web_contents) {
  CHECK(base::FeatureList::IsEnabled(features::kAttachUnownedInnerWebContents));
  CHECK(node_.IsUnownedInnerWebContents(inner_web_contents));

  WebContentsImpl* inner_web_contents_impl =
      static_cast<WebContentsImpl*>(inner_web_contents);
  // Unregister and destroy RenderWidgetHostViewChildFrame.
  inner_web_contents_impl->RecursivelyUnregisterRenderWidgetHostViews();

  // RenderWidgetHostView are of type RenderWidgetHostViewChildFrame and they
  // need to be re-created with appropriate platform views.
  // Do this for all outermost main frames including speculative render frame
  // host and the ones in prerender and BFCache.
  std::vector<RenderViewHostImpl*> list_of_rvh_with_rwhv;
  for (auto* rfh :
       inner_web_contents_impl->GetOutermostMainFramesForViewChange()) {
    RenderViewHostImpl* rvh = rfh->render_view_host();
    if (rvh->GetWidget() && rvh->GetWidget()->GetView()) {
      // While in theory only child frame RWHVs should exist at this stage,
      // in practice, a race with navigation cleanup could result in a main
      // frame RWHV that is pending deletion still existing here.
      // This might happen when a WebContents is destroyed immediately
      // after it navigates and then attaches to an outer WebContents.
      if (rvh->GetWidget()->GetView()->IsRenderWidgetHostViewChildFrame()) {
        list_of_rvh_with_rwhv.push_back(rvh);
      }
      rvh->GetWidget()->GetView()->Destroy();
    }
  }

  // Destroy WebContentsViewChildFrame.
  inner_web_contents_impl->render_view_host_delegate_view_ = nullptr;
  inner_web_contents_impl->view_ = nullptr;

  // Reset proxy.
  RenderFrameHostManager* inner_render_manager =
      inner_web_contents_impl->GetRenderManager();
  RenderFrameHostImpl* inner_main_frame =
      inner_render_manager->current_frame_host();
  RenderFrameProxyHost* proxy = inner_render_manager->GetProxyToOuterDelegate();
  if (proxy) {
    inner_main_frame->browsing_context_state()->DeleteRenderFrameProxyHost(
        proxy->site_instance_group(),
        BrowsingContextState::ProxyAccessMode::kAllowOuterDelegate);
  }

  node_.DetachInnerWebContents(inner_web_contents_impl);

  // Recreate WebContentsView.
  inner_web_contents_impl->view_ = CreateWebContentsView(
      inner_web_contents_impl,
      GetContentClient()->browser()->GetWebContentsViewDelegate(
          inner_web_contents_impl),
      &inner_web_contents_impl->render_view_host_delegate_view_);
  inner_web_contents_impl->view_->CreateView(gfx::NativeView());

  // Recreate and register RenderWidgetHostView. Don't do this if the
  // WebContents is being destroyed because it will cause a CHECK failure in
  // SendScreenRects().
  if (!inner_web_contents_impl->IsBeingDestroyed()) {
    for (RenderViewHostImpl* rvh : list_of_rvh_with_rwhv) {
      inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager(rvh);
    }
    inner_web_contents_impl->RecursivelyRegisterRenderWidgetHostViews();
  }

  inner_main_frame->UpdateAXTreeData();
}

SurfaceEmbedConnector* WebContentsImpl::GetSurfaceEmbedConnector() const {
  return surface_embed_connector_.get();
}

void WebContentsImpl::SetSurfaceEmbedConnector(
    std::unique_ptr<SurfaceEmbedConnectorImpl> connector) {
  CHECK(connector);
  // Can't attach an inner WebContents into SurfaceEmbed.
  CHECK(!node_.outer_web_contents());

  // Replace view with a WebContentsViewChildFrame view for the WebContents
  // being embedded. The WebContentsViewChildFrame will connect visual to the
  // embedder using SurfaceEmbedConnector from the provided `this` WebContents
  // object.
  view_ = std::make_unique<WebContentsViewChildFrame>(
      this, GetContentClient()->browser()->GetWebContentsViewDelegate(this),
      &render_view_host_delegate_view_);

  // On platforms where destroying the WebContents' view does not also destroy
  // the platform RenderWidgetHostView, we need to destroy it if it exists.
  // TODO(mcnee): Should all platforms' WebContentsView destroy the platform
  // RWHV?
  // Do this for all outermost main frames including speculative render frame
  // host and the ones in prerender and BFCache.
  for (auto* rfh : GetOutermostMainFramesForViewChange()) {
    if (RenderWidgetHostViewBase* prev_rwhv =
            static_cast<RenderWidgetHostViewBase*>(rfh->GetView())) {
      if (!prev_rwhv->IsRenderWidgetHostViewChildFrame()) {
        prev_rwhv->Destroy();
      }
    }
  }

  // Must unregister before setting the connector as that will change the
  // TextInputManager and InputEventRouter for this WebContents. Without doing
  // this, child frames' RWHVs would fail to unregister from the previous
  // TextInputManager and InputEventRouter.
  RecursivelyUnregisterRenderWidgetHostViews();

  // The new connector must be set before creating RWHVs as they need to get the
  // new TextInputManager and InputEventRouter.
  surface_embed_connector_ = std::move(connector);

  // Recreate views for all outermost main frames impacted by the WebContents
  // `view_` change, including speculative render frame host and the ones in
  // prerender and BFCache.
  for (auto* rfh : GetOutermostMainFramesForViewChange()) {
    auto* render_manager = rfh->frame_tree_node()->render_manager();
    RenderViewHostImpl* rvh = rfh->render_view_host();
    render_manager->InitRenderView(rfh->GetSiteInstance()->group(), rvh,
                                   /*proxy=*/nullptr,
                                   /*navigation_metrics_token=*/std::nullopt);
    if (!rfh->GetView()) {
      CreateRenderWidgetHostViewForRenderManager(rvh);
    }
  }

  RecursivelyRegisterRenderWidgetHostViews();

  surface_embed_connector_->OnAttachedToParent();
}

void WebContentsImpl::ClearSurfaceEmbedConnector() {
  CHECK(surface_embed_connector_);

  surface_embed_connector_->OnDetachedFromParent();

  surface_embed_connector_->ClearFocusOnInnerWebContents();

  // Because there may be child frames, we need to unregister all RWHVs before
  // destroying main frames views which could prevent child frames from finding
  // the root view for unregistering observer from
  // TouchSelectionControllerClient, and before clearing the connector, which
  // will change the TextInputManager and InputEventRouter for this WebContents.
  RecursivelyUnregisterRenderWidgetHostViews();

  // RenderWidgetHostView of main frames that are of type
  // RenderWidgetHostViewChildFrame should be re-created with appropriate
  // platform views.
  // Do this for all outermost main frames including speculative render frame
  // host and the ones in prerender and BFCache.
  std::vector<RenderViewHostImpl*> list_of_rvh_with_rwhv;
  for (auto* rfh : GetOutermostMainFramesForViewChange()) {
    auto* rvh = rfh->render_view_host();
    if (rvh->GetWidget() && rvh->GetWidget()->GetView()) {
      if (rvh->GetWidget()->GetView()->IsRenderWidgetHostViewChildFrame()) {
        list_of_rvh_with_rwhv.push_back(rvh);
      }
      rvh->GetWidget()->GetView()->Destroy();
    }
  }

  if (!IsBeingDestroyed()) {
    render_view_host_delegate_view_ = nullptr;
    view_ = nullptr;
  }

  // The child main frame derives its embed-parent AX tree id from the
  // connector. Clear that id from the child's AX tree data *before* freeing the
  // connector. AccessibilityIsRootFrame() consults the connector, so if the
  // connector were freed first the frame would report itself as the AX root
  // while its tree data still carried the embedder's parent tree id.
  // Serializing in that window trips
  // BrowserAccessibilityManager::IsRootFrameManager()'s invariant that a root
  // tree has no parent tree id. Note AXTree::Unserialize() notifies observers
  // before it applies the new tree data, so the clear must happen while the
  // connector still makes the frame a non-root. This runs even during
  // destruction because the BrowserAccessibilityManagers are torn down after
  // this point and cannot have a stale embedder parent tree id.
  const bool had_embed_parent_ax_tree_id =
      surface_embed_connector_->GetParentAXTreeID() != ui::AXTreeIDUnknown();
  if (had_embed_parent_ax_tree_id) {
    primary_frame_tree_.root()->current_frame_host()->ClearEmbedderAXTreeData();
  }

  surface_embed_connector_.reset();

  // The frame is now a true AX root. Refresh so the renderer and platform
  // managers observe the final state. The tree data no longer carries the
  // embedder's parent tree id, so this no longer sees an inconsistent state.
  if (had_embed_parent_ax_tree_id && !IsBeingDestroyed()) {
    primary_frame_tree_.root()->current_frame_host()->UpdateAXTreeData();
  }

  // Recreate and register RenderWidgetHostView.
  if (!IsBeingDestroyed()) {
    // Recreate WebContentsView.
    view_ = CreateWebContentsView(
        this, GetContentClient()->browser()->GetWebContentsViewDelegate(this),
        &render_view_host_delegate_view_);
    view_->CreateView(gfx::NativeView());

    for (RenderViewHostImpl* rvh : list_of_rvh_with_rwhv) {
      CreateRenderWidgetHostViewForRenderManager(rvh);
    }
    RecursivelyRegisterRenderWidgetHostViews();
  }
}

void WebContentsImpl::AttachGuestPage(
    std::unique_ptr<GuestPageHolder> guest_page,
    RenderFrameHost* outer_render_frame_host) {
  CHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch));

  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::AttachGuestPage",
                        "guest_page", static_cast<void*>(guest_page.get()));
  auto* outer_render_frame_host_impl =
      static_cast<RenderFrameHostImpl*>(outer_render_frame_host);
  CHECK_EQ(this,
           WebContents::FromRenderFrameHost(outer_render_frame_host_impl));
  CHECK(outer_render_frame_host_impl->GetParent());

  // Guest pages aren't supported with prerendering. See
  // https://crbug.com/40191159 for details.
  CHECK_NE(RenderFrameHostImpl::LifecycleStateImpl::kPrerendering,
           outer_render_frame_host_impl->lifecycle_state());

  auto* guest_page_impl = static_cast<GuestPageHolderImpl*>(guest_page.get());
  outer_render_frame_host_impl->GetParent()->TakeGuestOwnership(
      base::WrapUnique(
          static_cast<GuestPageHolderImpl*>(guest_page.release())));

  FrameTree& inner_frame_tree = guest_page_impl->frame_tree();
  RenderFrameHostManager* inner_render_manager =
      inner_frame_tree.root()->render_manager();
  RenderFrameHostImpl* inner_main_frame =
      inner_render_manager->current_frame_host();
  RenderViewHostImpl* inner_render_view_host =
      inner_main_frame->render_view_host();
  auto* outer_render_manager =
      outer_render_frame_host_impl->frame_tree_node()->render_manager();

  if (RenderWidgetHostViewBase* prev_rwhv =
          inner_render_manager->GetRenderWidgetHostView()) {
    // Ensure that if there is an existing RenderWidgetHostView, it's valid to
    // embed.
    CHECK(prev_rwhv->IsRenderWidgetHostViewChildFrame());
    static_cast<RenderWidgetHostViewChildFrame*>(prev_rwhv)
        ->UnregisterFrameSinkId();
  }

  // Make `outer_render_frame_host_impl` the placeholder for the inner frame
  // tree.
  outer_render_frame_host_impl->set_inner_tree_main_frame_tree_node_id(
      inner_main_frame->frame_tree_node()->frame_tree_node_id());
  guest_page_impl->set_outer_frame_tree_node_id(
      outer_render_frame_host_impl->frame_tree_node()->frame_tree_node_id());

  // When the guest page being initialized has not already navigated, the
  // browser side Render{View,Frame}Host must be initialized and the
  // RenderWidgetHostView created. This is needed because the usual
  // initialization happens during the first navigation, but not all guest types
  // navigate before attaching. If the browser side is already initialized, the
  // call below will just early return.
  inner_render_manager->InitRenderView(
      inner_main_frame->GetSiteInstance()->group(), inner_render_view_host,
      /*proxy=*/nullptr, /*navigation_metrics_token=*/std::nullopt);

  // If we are reusing the RenderViewHost and it doesn't already have a
  // RenderWidgetHostView, we need to create one if this is the main frame.
  if (!inner_render_view_host->GetWidget()->GetView()) {
    // TODO(crbug.com/40162510): The RenderWidgetHostView should be created
    // *before* we create the renderer-side objects through InitRenderView().
    // Then we should remove the null-check for the RenderWidgetHostView in
    // RenderWidgetHostImpl::RendererWidgetCreated().
    CreateRenderWidgetHostViewForRenderManager(inner_render_view_host);
  }

  RenderFrameProxyHost* proxy =
      inner_main_frame->browsing_context_state()->CreateOuterDelegateProxy(
          outer_render_frame_host_impl->GetSiteInstance()->group(),
          inner_main_frame->frame_tree_node(), blink::RemoteFrameToken());
  // Since the inner page is created from the browser side we do
  // not have RemoteFrame mojo channels. New channels will be bound when the
  // `CreateView` IPC is sent.

  // When attaching a GuestView as an inner frame tree, there should already be
  // a live RenderFrame, which has to be swapped.
  if (outer_render_frame_host_impl->IsRenderFrameLive()) {
    inner_render_manager->SwapOuterDelegateFrame(
        outer_render_frame_host_impl, proxy,
        inner_main_frame->devtools_frame_token());
  }

  RenderWidgetHostViewBase* child_rwhvb =
      inner_render_manager->GetRenderWidgetHostView();
  CHECK(child_rwhvb);
  CHECK(child_rwhvb->IsRenderWidgetHostViewChildFrame());
  RenderWidgetHostViewChildFrame* child_rwhv =
      static_cast<RenderWidgetHostViewChildFrame*>(child_rwhvb);
  inner_render_manager->SetRWHViewForInnerFrameTree(child_rwhv);
  child_rwhv->RegisterFrameSinkId();

  outer_render_manager->set_attach_inner_delegate_complete();
  inner_main_frame->PropagateEmbeddingTokenToParentFrame();

  // Synchronize visual properties so that the guest main frame's renderer
  // process immediately receives initial throttling status upon being attached
  // as an embedded main frame.
  if (auto* rwh = inner_main_frame->GetRenderWidgetHost()) {
    rwh->SynchronizeVisualProperties();
  }

  // TODO(crbug.com/40202416): Determine if anything else is needed here.
}

void WebContentsImpl::RecursivelyRegisterRenderWidgetHostViews() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::RecursivelyRegisterRenderWidgetHostViews");

  auto* text_input_manager = GetTextInputManager();
  for (auto* view : GetRenderWidgetHostViewsInWebContentsTree()) {
    if (text_input_manager) {
      // Use RenderWidgetHostView::GetTextInputManager() for its side effects,
      // rather than registering the view directly; the view caches the
      // TextInputManager.
      //
      // TODO(crbug.com/40212969): This probably could be made more symmetrical
      // with unregistration. Getting rid of lazy registration might also allow
      // eliminating some special cases in TextInputManager.
      auto* text_input_manager_for_view = view->GetTextInputManager();
      DCHECK_EQ(text_input_manager, text_input_manager_for_view);
    }

    if (view->IsRenderWidgetHostViewChildFrame()) {
      static_cast<RenderWidgetHostViewChildFrame*>(view)->RegisterFrameSinkId();
    }
  }

  // surface_embed_children_ is not expected to change during registration.
  for (const auto& child_weak : surface_embed_children_) {
    if (auto* child = static_cast<WebContentsImpl*>(child_weak.get())) {
      child->RecursivelyRegisterRenderWidgetHostViews();
    }
  }
}

void WebContentsImpl::RecursivelyUnregisterRenderWidgetHostViews() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::RecursivelyUnregisterRenderWidgetHostViews");

  auto* text_input_manager = GetTextInputManager();
  for (auto* view : GetRenderWidgetHostViewsInWebContentsTree()) {
    if (text_input_manager) {
      // The RenderWidgetHostView will drop the cached TextInputManager itself.
      text_input_manager->Unregister(view);
    }

    if (view->IsRenderWidgetHostViewChildFrame()) {
      static_cast<RenderWidgetHostViewChildFrame*>(view)
          ->UnregisterFrameSinkId();
    }
  }

  // surface_embed_children_ is not expected to change during unregistration.
  for (const auto& child_weak : surface_embed_children_) {
    if (auto* child = static_cast<WebContentsImpl*>(child_weak.get())) {
      child->RecursivelyUnregisterRenderWidgetHostViews();
    }
  }
}

void WebContentsImpl::ReattachToOuterWebContentsFrame() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ReattachToOuterWebContentsFrame");
  DCHECK(node_.outer_web_contents());
  auto* render_manager = GetRenderManager();
  auto* child_rwhv = render_manager->GetRenderWidgetHostView();
  DCHECK(child_rwhv);
  DCHECK(child_rwhv->IsRenderWidgetHostViewChildFrame());
  render_manager->SetRWHViewForInnerFrameTree(
      static_cast<RenderWidgetHostViewChildFrame*>(child_rwhv));

  RecursivelyRegisterRenderWidgetHostViews();
  GetPrimaryMainFrame()->UpdateAXTreeData();
}

void WebContentsImpl::DidChangeVisibleSecurityState() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::DidChangeVisibleSecurityState");
  if (delegate_) {
    delegate_->VisibleSecurityStateChanged(this);
  }

  SCOPED_UMA_HISTOGRAM_TIMER(
      "WebContentsObserver.DidChangeVisibleSecurityState");
  observers_.NotifyObservers(
      &WebContentsObserver::DidChangeVisibleSecurityState);
}

const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences(
    RenderFrameHostImpl* main_frame) {
  OPTIONAL_TRACE_EVENT0("browser", "WebContentsImpl::ComputeWebPreferences");
  CHECK(main_frame->is_main_frame());

  blink::web_pref::WebPreferences prefs;

  // Sets the hardware-related fields in |prefs| that are slow to compute. The
  // fields are set from cache if available, otherwise recomputed.
  SlowWebPreferenceCache::GetInstance()->Load(&prefs);

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

  prefs.web_security_enabled =
      !command_line.HasSwitch(switches::kDisableWebSecurity);

  prefs.remote_fonts_enabled =
      !command_line.HasSwitch(switches::kDisableRemoteFonts);
  prefs.local_storage_enabled =
      !command_line.HasSwitch(switches::kDisableLocalStorage);

  prefs.webgl1_enabled = !command_line.HasSwitch(switches::kDisable3DAPIs) &&
                         !command_line.HasSwitch(switches::kDisableWebGL);
  prefs.webgl2_enabled = !command_line.HasSwitch(switches::kDisable3DAPIs) &&
                         !command_line.HasSwitch(switches::kDisableWebGL) &&
                         !command_line.HasSwitch(switches::kDisableWebGL2);

  prefs.allow_file_access_from_file_urls =
      command_line.HasSwitch(switches::kAllowFileAccessFromFiles);

  prefs.accelerated_2d_canvas_enabled =
      !command_line.HasSwitch(switches::kDisableAccelerated2dCanvas);
  prefs.canvas_2d_layers_enabled =
      command_line.HasSwitch(switches::kEnableCanvas2DLayers) ||
      base::FeatureList::IsEnabled(features::kEnableCanvas2DLayers);
  prefs.antialiased_2d_canvas_disabled =
      command_line.HasSwitch(switches::kDisable2dCanvasAntialiasing);
  prefs.antialiased_clips_2d_canvas_enabled =
      !command_line.HasSwitch(switches::kDisable2dCanvasClipAntialiasing);

  prefs.disable_ipc_flooding_protection =
      command_line.HasSwitch(switches::kDisableIpcFloodingProtection) ||
      command_line.HasSwitch(switches::kDisablePushStateThrottle);

  prefs.accelerated_video_decode_enabled =
      !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode);

  std::string autoplay_policy = media::GetEffectiveAutoplayPolicy(command_line);
  if (autoplay_policy == switches::autoplay::kNoUserGestureRequiredPolicy) {
    prefs.autoplay_policy =
        blink::mojom::AutoplayPolicy::kNoUserGestureRequired;
  } else if (autoplay_policy ==
             switches::autoplay::kUserGestureRequiredPolicy) {
    prefs.autoplay_policy = blink::mojom::AutoplayPolicy::kUserGestureRequired;
  } else if (autoplay_policy ==
             switches::autoplay::kDocumentUserActivationRequiredPolicy) {
    prefs.autoplay_policy =
        blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired;
  } else {
    NOTREACHED();
  }

  prefs.dont_send_key_events_to_javascript =
      base::FeatureList::IsEnabled(features::kDontSendKeyEventsToJavascript);

// TODO(dtapuska): Enable barrel button selection drag support on Android.
// crbug.com/758042
#if BUILDFLAG(IS_WIN)
  prefs.barrel_button_for_drag_enabled = true;
#endif  // BUILDFLAG(IS_WIN)

  prefs.enable_scroll_animator =
      command_line.HasSwitch(switches::kEnableSmoothScrolling) ||
      (!command_line.HasSwitch(switches::kDisableSmoothScrolling) &&
       gfx::Animation::ScrollAnimationsEnabledBySystem());

  prefs.prefers_reduced_motion = gfx::Animation::PrefersReducedMotion();

  const auto* const theme = ui::NativeTheme::GetInstanceForWeb();
  prefers_reduced_transparency_ = theme->prefers_reduced_transparency();
  inverted_colors_ = theme->inverted_colors();
  prefs.prefers_reduced_transparency = prefers_reduced_transparency_;
  prefs.inverted_colors = inverted_colors_;

  if (ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
          GetRenderViewHost()->GetProcess()->GetDeprecatedID())) {
    prefs.loads_images_automatically = true;
    prefs.javascript_enabled = true;
  }

  prefs.viewport_enabled = command_line.HasSwitch(switches::kEnableViewport);

#if BUILDFLAG(IS_ANDROID)
  // TODO(crbug.com/40925473): GetPrimaryDisplay() won't be correct for
  // externally connected displays. Get the display where Chrome is opened
  // instead.
  display::Display display = display::Screen::Get()->GetPrimaryDisplay();
  gfx::Size size = display.GetSizeInPixel();
  int min_width = size.width() < size.height() ? size.width() : size.height();
  int min_width_in_dp =
      static_cast<int>(min_width / display.device_scale_factor());
  if (prefs.viewport_enabled &&
      (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_AUTOMOTIVE ||
       (min_width_in_dp >= kAndroidMinimumTabletWidthDp &&
        ui::GetDeviceFormFactor() != ui::DEVICE_FORM_FACTOR_TV))) {
    prefs.viewport_style = blink::mojom::ViewportStyle::kDefault;
  }
#endif

#if BUILDFLAG(IS_ANDROID)
  bool is_request_android_desktop_site = false;
#endif

  if (GetController().GetVisibleEntry() &&
      GetController().GetVisibleEntry()->GetIsOverridingUserAgent()) {
#if BUILDFLAG(IS_ANDROID)
    // Only ignore viewport meta tag when Request Desktop Site is used, but not
    // in other situations where embedder changes to arbitrary mobile UA string.
    is_request_android_desktop_site =
        renderer_preferences_.user_agent_override.ua_metadata_override &&
        !renderer_preferences_.user_agent_override.ua_metadata_override->mobile;
    prefs.viewport_meta_enabled = !is_request_android_desktop_site;
#else
    prefs.viewport_meta_enabled = false;
#endif
  }

  prefs.spatial_navigation_enabled =
      command_line.HasSwitch(switches::kEnableSpatialNavigation);

  if (is_spatial_navigation_disabled_) {
    prefs.spatial_navigation_enabled = false;
  }

#if BUILDFLAG(IS_ANDROID)
  prefs.long_press_link_select_text = long_press_link_select_text_;

  if (base::FeatureList::IsEnabled(
          features::kRestrictOrientationLockToPhones)) {
    // Only lock fullscreen orientation if the provider allows it, and if the
    // prefs currently want to do so.  While current behavior happens to match
    // exactly with the provider claiming to support orientation lock, we still
    // want to give the cache the opportunity to turn it off for other reasons.
    // For example, if a foldable is folded, then the prefs cache should notify
    // us that the behavior may have changed.
    prefs.video_fullscreen_orientation_lock_enabled &=
        screen_orientation_provider_->IsOrientationLockSupported();
  }
#endif

#if BUILDFLAG(IS_ANDROID)
  prefs.stylus_handwriting_enabled = stylus_handwriting_enabled_;
#elif BUILDFLAG(IS_WIN)
  prefs.stylus_handwriting_enabled =
      stylus_handwriting::win::IsStylusHandwritingWinEnabled();
#endif

  prefs.disable_reading_from_canvas =
      command_line.HasSwitch(switches::kDisableReadingFromCanvas);

  prefs.strict_mixed_content_checking =
      command_line.HasSwitch(switches::kEnableStrictMixedContentChecking);

  const std::string blockable_mixed_content_group =
      base::FieldTrialList::FindFullName("BlockableMixedContent");
  prefs.strictly_block_blockable_mixed_content =
      blockable_mixed_content_group == "StrictlyBlockBlockableMixedContent";

  const std::string plugin_mixed_content_status =
      base::FieldTrialList::FindFullName("PluginMixedContentStatus");
  prefs.block_mixed_plugin_content =
      plugin_mixed_content_status == "BlockableMixedContent";

  prefs.v8_cache_options = GetV8CacheOptions();

  prefs.user_gesture_required_for_presentation = !command_line.HasSwitch(
      switches::kDisableGestureRequirementForPresentation);

  if (is_overlay_content_) {
    prefs.hide_download_ui = true;
  }

  // `media_controls_enabled` is `true` by default.
  if (has_persistent_video_) {
    prefs.media_controls_enabled = false;
  }

  // GuestViews in the same StoragePartition need to find each other's frames.
  prefs.renderer_wide_named_frame_lookup =
      IsGuest() || main_frame->frame_tree()->is_guest();

  if (command_line.HasSwitch(switches::kHideScrollbars)) {
    prefs.hide_scrollbars = true;
  }

  prefs.payment_request_enabled =
      base::FeatureList::IsEnabled(features::kWebPayments);

#if BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(features::kWebauthnDisabledOnAuto) &&
      base::android::device_info::is_automotive()) {
    prefs.disable_webauthn = true;
  }
#endif  // BUILDFLAG(IS_ANDROID)

  // For devices that have larger displays (e.g. tablets, desktops), they
  // require a different set of webpref settings that is different from smaller
  // form-factors such as phones in order to render appropriately (e.g.
  // viewport)
#if BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(
          blink::features::kAndroidDesktopWebPrefsLargeDisplays)) {
    bool apply_desktop_common_settings = false;
    switch (ui::GetDeviceFormFactor()) {
      case ui::DEVICE_FORM_FACTOR_DESKTOP:
        apply_desktop_common_settings = true;
        // There are no orientation changes in desktop mode.
        // Might need to revisit for convertible devices (i.e. clamshell ->
        // tablet).
        prefs.main_frame_resizes_are_orientation_changes = false;
        break;
      case ui::DEVICE_FORM_FACTOR_TABLET:
      case ui::DEVICE_FORM_FACTOR_XR:
        // Specific to large tablets (10") and XR devices, by default they
        // request desktop site, but can per-site optionally override it to
        // request mobile instead.
        if (is_request_android_desktop_site) {
          apply_desktop_common_settings = true;
        }
        break;
      case ui::DEVICE_FORM_FACTOR_AUTOMOTIVE:
      case ui::DEVICE_FORM_FACTOR_TV:
      case ui::DEVICE_FORM_FACTOR_FOLDABLE:
      case ui::DEVICE_FORM_FACTOR_PHONE:
        break;
    }

    if (apply_desktop_common_settings) {
      // Settings below matches up with desktop chrome

      // Set page scale factors to be similar to desktop.
      // The significant change compared to mobile is that we lock the min scale
      // to 1, so that we don't allow for a birds-eye-view zoom-out (this
      // matches desktop).
      prefs.default_minimum_page_scale_factor = 1.f;
      prefs.default_maximum_page_scale_factor = 4.f;

      // Ensure no further viewport scaling
      prefs.shrinks_viewport_contents_to_fit = false;
    }
  }

#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
  prefs.should_disable_external_popups =
      external_popup_menus_forbid_counter_ != 0;
#endif  // BUILDFLAG(IS_MAC) && BUILDFLAG(USE_EXTERNAL_POPUP_MENU)

  GetContentClient()->browser()->OverrideWebPreferences(
      this, *main_frame->GetSiteInstance(), &prefs);
  return prefs;
}

void WebContentsImpl::OnWebPreferencesChanged() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnWebPreferencesChanged");

  // This is defensive code to avoid infinite loops due to code run inside
  // SetWebPreferences() accidentally updating more preferences and thus
  // calling back into this code. See crbug.com/398751 for one past example.
  if (updating_web_preferences_) {
    return;
  }
  updating_web_preferences_ = true;
  SetWebPreferences(ComputeWebPreferences(GetPrimaryMainFrame()));

#if BUILDFLAG(IS_ANDROID)
  const bool force_enable_zoom_changed =
      (force_enable_zoom_ != web_preferences_->force_enable_zoom);
  force_enable_zoom_ = web_preferences_->force_enable_zoom;
  if (force_enable_zoom_changed) {
    for (FrameTreeNode* node : primary_frame_tree_.Nodes()) {
      RenderFrameHostImpl* rfh = node->current_frame_host();
      if (rfh->is_local_root()) {
        if (auto* rwh = rfh->GetRenderWidgetHost()) {
          rwh->SetForceEnableZoom(force_enable_zoom_);
        }
      }
    }
  }

  const bool enable_touchpad_overscroll_history_navigation_changed =
      (enable_touchpad_overscroll_history_navigation_ !=
       web_preferences_->enable_touchpad_overscroll_history_navigation);
  enable_touchpad_overscroll_history_navigation_ =
      web_preferences_->enable_touchpad_overscroll_history_navigation;
  if (enable_touchpad_overscroll_history_navigation_changed) {
    if (auto* rwhv = GetRenderWidgetHostView()) {
      static_cast<RenderWidgetHostViewBase*>(rwhv)
          ->SetTouchpadOverscrollHistoryNavigation(
              enable_touchpad_overscroll_history_navigation_);
    }
  }
#endif

  // Update inner WebContents.
  for (WebContents* inner : GetInnerWebContents()) {
    static_cast<WebContentsImpl*>(inner)->OnWebPreferencesChanged();
  }

  updating_web_preferences_ = false;
}

void WebContentsImpl::NotifyPreferencesChanged() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::NotifyPreferencesChanged");

  // Recompute the WebPreferences based on the current state of the WebContents,
  // etc. Note that OnWebPreferencesChanged will also call SetWebPreferences and
  // send the updated WebPreferences to all RenderViews for this WebContents.
  OnWebPreferencesChanged();
}

void WebContentsImpl::SyncRendererPrefs() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SyncRendererPrefs");

  ExecutePageBroadcastMethodForAllPages([this](RenderViewHostImpl* rvh) {
    rvh->SendRendererPreferencesToRenderer(GetRendererPrefs(rvh));
  });
}

void WebContentsImpl::OnCookiesAccessed(NavigationHandle* navigation,
                                        const CookieAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnCookiesAccessed",
                        "navigation_handle", navigation);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(NavigationHandle*,
                                    const CookieAccessDetails&) =
      &WebContentsObserver::OnCookiesAccessed;
  observers_.NotifyObservers(func, navigation, details);
}

void WebContentsImpl::OnCookiesAccessed(RenderFrameHostImpl* rfh,
                                        const CookieAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnCookiesAccessed",
                        "render_frame_host", rfh);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(RenderFrameHost*,
                                    const CookieAccessDetails&) =
      &WebContentsObserver::OnCookiesAccessed;
  observers_.NotifyObservers(func, rfh, details);
}

void WebContentsImpl::OnTrustTokensAccessed(
    NavigationHandle* navigation,
    const TrustTokenAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnTrustTokensAccessed",
                        "navigation_handle", navigation);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(NavigationHandle*,
                                    const TrustTokenAccessDetails&) =
      &WebContentsObserver::OnTrustTokensAccessed;
  observers_.NotifyObservers(func, navigation, details);
}

void WebContentsImpl::OnTrustTokensAccessed(
    RenderFrameHostImpl* rfh,
    const TrustTokenAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnTrustTokensAccessed",
                        "render_frame_host", rfh);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(RenderFrameHost*,
                                    const TrustTokenAccessDetails&) =
      &WebContentsObserver::OnTrustTokensAccessed;
  observers_.NotifyObservers(func, rfh, details);
}

void WebContentsImpl::OnSharedDictionaryAccessed(
    NavigationHandle* navigation,
    const network::mojom::SharedDictionaryAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnSharedDictionaryAccessed",
                        "navigation_handle", navigation);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(
      NavigationHandle*, const network::mojom::SharedDictionaryAccessDetails&) =
      &WebContentsObserver::OnSharedDictionaryAccessed;
  observers_.NotifyObservers(func, navigation, details);
}

void WebContentsImpl::OnSharedDictionaryAccessed(
    RenderFrameHostImpl* rfh,
    const network::mojom::SharedDictionaryAccessDetails& details) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnSharedDictionaryAccessed",
                        "render_frame_host", rfh);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(
      RenderFrameHost*, const network::mojom::SharedDictionaryAccessDetails&) =
      &WebContentsObserver::OnSharedDictionaryAccessed;
  observers_.NotifyObservers(func, rfh, details);
}

void WebContentsImpl::OnDeviceBoundSessionAccessed(
    NavigationHandle* navigation,
    const net::device_bound_sessions::SessionAccess& access) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnDeviceBoundSessionAccessed",
                        "navigation_handle", navigation);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(
      NavigationHandle*, const net::device_bound_sessions::SessionAccess&) =
      &WebContentsObserver::OnDeviceBoundSessionAccessed;
  observers_.NotifyObservers(func, navigation, access);
}

void WebContentsImpl::OnDeviceBoundSessionAccessed(
    RenderFrameHostImpl* rfh,
    const net::device_bound_sessions::SessionAccess& access) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnDeviceBoundSessionAccessed",
                        "render_frame_host", rfh);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(
      RenderFrameHost*, const net::device_bound_sessions::SessionAccess&) =
      &WebContentsObserver::OnDeviceBoundSessionAccessed;
  observers_.NotifyObservers(func, rfh, access);
}

void WebContentsImpl::NotifyStorageAccessed(
    RenderFrameHostImpl* rfh,
    blink::mojom::StorageTypeAccessed storage_type,
    bool blocked) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::NotifyStorageAccessed",
                        "render_frame_host", rfh);
  observers_.NotifyObservers(&WebContentsObserver::NotifyStorageAccessed, rfh,
                             storage_type, blocked);
}

void WebContentsImpl::OnVibrate(RenderFrameHostImpl* rfh) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnVibrate",
                        "render_frame_host", rfh);
  observers_.NotifyObservers(&WebContentsObserver::VibrationRequested);
}

void WebContentsImpl::Stop() {
  TRACE_EVENT0("content", "WebContentsImpl::Stop");
  ForEachFrameTree([](FrameTree& frame_tree) { frame_tree.StopLoading(); });
  GetPrerenderHostRegistry()->CancelAllHosts(PrerenderFinalStatus::kStop);
  observers_.NotifyObservers(&WebContentsObserver::NavigationStopped);
}

void WebContentsImpl::SetPageFrozen(bool frozen) {
  TRACE_EVENT1("content", "WebContentsImpl::SetPageFrozen", "frozen", frozen);

  // A visible page is never frozen.
  DCHECK_NE(Visibility::VISIBLE, GetVisibility());

  primary_frame_tree_.ForEachRenderViewHost(
      [&frozen](RenderViewHostImpl* rvh) { rvh->SetIsFrozen(frozen); });
}

std::unique_ptr<WebContents> WebContentsImpl::Clone() {
  TRACE_EVENT0("content", "WebContentsImpl::Clone");

  // We use our current SiteInstance since the cloned entry will use it anyway.
  // We pass our own opener so that the cloned page can access it if it was set
  // before.
  CreateParams create_params(GetBrowserContext(), GetSiteInstance());
  FrameTreeNode* opener = primary_frame_tree_.root()->opener();
  RenderFrameHostImpl* opener_rfh = nullptr;
  if (opener) {
    opener_rfh = opener->current_frame_host();
  }
  std::unique_ptr<WebContentsImpl> tc =
      CreateWithOpener(create_params, opener_rfh);
  tc->GetController().CopyStateFrom(&primary_frame_tree_.controller(), true);
  observers_.NotifyObservers(&WebContentsObserver::DidCloneToNewWebContents,
                             this, tc.get());
  return tc;
}

void WebContentsImpl::Init(const WebContents::CreateParams& params,
                           blink::FramePolicy primary_main_frame_policy) {
  const char* static_name = params.creator_location.function_name()
                                ? params.creator_location.function_name()
                                : "WebContents";
  tracing_track_.emplace(content::GetWebContentsTracingTrack(
      web_contents_token_, perfetto::StaticString{static_name}));

  EmitTracingSlice(params.main_frame_name);

  TRACE_EVENT0("content", "WebContentsImpl::Init");

  // Set initial autofill mode. Prefs may update this value later but for
  // pre-warmed WebContents that update happens too late.
  renderer_preferences_.uses_platform_autofill =
      params.initially_use_platform_autofill;

  is_never_composited_ = params.is_never_composited;

  privileged_params_ = params.privileged_params;

  creator_location_ = params.creator_location;
#if BUILDFLAG(IS_ANDROID)
  java_creator_location_ = params.java_creator_location;
#endif  // BUILDFLAG(IS_ANDROID)

  if (params.picture_in_picture_options.has_value()) {
    picture_in_picture_options_ = params.picture_in_picture_options;
    if (GetOpener()) {
      picture_in_picture_opener_ =
          FromRenderFrameHostImpl(GetOpener())->GetWeakPtr();
    }
  }

  CHECK(!(params.initially_hidden && params.initially_hidden_but_painting));
  initially_hidden_but_painting_ = params.initially_hidden_but_painting;
  // This is set before initializing the render manager since
  // RenderFrameHostManager::Init calls back into us via its delegate to ask if
  // it should be hidden.
  visibility_ = params.initially_hidden || initially_hidden_but_painting_
                    ? Visibility::HIDDEN
                    : Visibility::VISIBLE;
  GetController().SetActive(GetPageVisibilityState() !=
                            PageVisibilityState::kHidden);

  enable_wake_locks_ = params.enable_wake_locks;

  if (!params.last_active_time_ticks.is_null()) {
    last_active_time_ticks_ = params.last_active_time_ticks;
  }
  if (!params.last_active_time.is_null()) {
    last_active_time_ = params.last_active_time;
  }

  scoped_refptr<SiteInstanceImpl> site_instance =
      static_cast<SiteInstanceImpl*>(params.site_instance.get());
  if (!site_instance) {
    site_instance = SiteInstanceImpl::Create(params.browser_context);
  }
  if (params.desired_renderer_state == CreateParams::kNoRendererProcess) {
    site_instance->PreventAssociationWithSpareProcess();
  }

  // Iniitalize the primary FrameTree.
  // Note that GetOpener() is used here to get the opener for origin
  // inheritance, instead of other similar functions:
  // - GetOriginalOpener(), which would always return the main frame of the
  // opener, which might be different from the actual opener.
  // - FindOpenerRFH(), which will still return the opener frame if the
  // opener is suppressed (e.g. due to 'noopener'). The opener should not
  // be used for origin inheritance purposes in those cases, so this should
  // not pass the opener for those cases.
  primary_frame_tree_.Init(
      site_instance.get(), params.renderer_initiated_creation,
      params.main_frame_name, GetOpener(), primary_main_frame_policy,
      base::UnguessableToken::Create());

  std::unique_ptr<WebContentsViewDelegate> delegate =
      GetContentClient()->browser()->GetWebContentsViewDelegate(this);

  if (browser_plugin_guest_) {
    view_ = std::make_unique<WebContentsViewChildFrame>(
        this, std::move(delegate), &render_view_host_delegate_view_);
  } else {
    view_ = CreateWebContentsView(this, std::move(delegate),
                                  &render_view_host_delegate_view_);
  }
  CHECK(render_view_host_delegate_view_);
  CHECK(view_.get());

  // Set the accessibility mode after the view is created.
  if (!is_never_composited_) {
    BrowserAccessibilityStateImpl::GetInstance()->OnWebContentsInitialized(
        this);
  }

  view_->CreateView(params.context);

  screen_orientation_provider_ =
      std::make_unique<ScreenOrientationProvider>(this);

#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_TVOS))
  DateTimeChooser::CreateDateTimeChooser(this);
#endif

  SchedulerLoopQuarantineWebContentsObserver::MaybeCreateForWebContents(this);
  RedirectChainDetector::CreateForWebContents(this);
  BtmWebContentsObserver::MaybeCreateForWebContents(this);
  DeclarativePerformanceObserverCoordinator::CreateForWebContents(this);

  // BrowserPluginGuest::Init needs to be called after this WebContents has
  // a RenderWidgetHostViewChildFrame. That is, |view_->CreateView| above.
  if (browser_plugin_guest_) {
    browser_plugin_guest_->Init();
  }

  g_created_callbacks.Get().Notify(this);

  // Create the renderer process in advance if requested.
  if (params.desired_renderer_state ==
      CreateParams::kInitializeAndWarmupRendererProcess) {
    if (!GetRenderManager()->current_frame_host()->IsRenderFrameLive()) {
      GetRenderManager()->InitRenderView(
          site_instance->group(), GetRenderViewHost(), /*proxy=*/nullptr,
          /*navigation_metrics_token=*/std::nullopt);
    }
  }

  // Let the embedder know about the newly created and initialized WebContents.
  GetContentClient()->browser()->OnWebContentsCreated(this);

  // Ensure that observers are notified of the creation of this WebContents's
  // main RenderFrameHost. It must be done here for main frames, since the
  // NotifySwappedFromRenderManager expects view_ to already be created and that
  // happens after RenderFrameHostManager::Init.
  NotifySwappedFromRenderManager(nullptr,
                                 GetRenderManager()->current_frame_host());

  // Checks whether the associated ssl_manager has any certificate error or HTTP
  // exceptions for any host and updates the renderer preferences.
  if (base::FeatureList::IsEnabled(
          features::kReduceSubresourceResponseStartedIPC) &&
      GetController().ssl_manager()->HasAllowExceptionForAnyHost()) {
    renderer_preferences_.send_subresource_notification = true;
    SyncRendererPrefs();
  }
  base::trace_event::TraceSessionObserverList::AddObserver(this);
}

void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnWebContentsDestroyed");

  RemoveWebContentsDestructionObserver(web_contents);

  // Clear a pending contents that has been closed before being shown.
  for (auto iter = pending_contents_.begin(); iter != pending_contents_.end();
       ++iter) {
    if (iter->second.contents.get() != web_contents) {
      continue;
    }

    // Someone else has deleted the WebContents. That should never happen!
    // TODO(erikchen): Fix semantics here. https://crbug.com/832879.
    iter->second.contents.release();
    pending_contents_.erase(iter);
    return;
  }
  NOTREACHED();
}

void WebContentsImpl::OnRenderWidgetHostDestroyed(
    RenderWidgetHost* render_widget_host) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::OnRenderWidgetHostDestroyed");

  RemoveRenderWidgetHostDestructionObserver(render_widget_host);

  // Clear a pending widget that has been closed before being shown.
  size_t num_erased =
      std::erase_if(pending_widgets_, [render_widget_host](const auto& pair) {
        return pair.second == render_widget_host;
      });
  DCHECK_EQ(1u, num_erased);
}

PrerenderHostId WebContentsImpl::GetPrerenderHostId() {
  return PrerenderHostId();
}

void WebContentsImpl::AddWebContentsDestructionObserver(
    WebContentsImpl* web_contents) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::AddWebContentsDestructionObserver");

  if (!web_contents_destruction_observers_.contains(web_contents)) {
    web_contents_destruction_observers_[web_contents] =
        std::make_unique<WebContentsDestructionObserver>(this, web_contents);
  }
}

void WebContentsImpl::RemoveWebContentsDestructionObserver(
    WebContentsImpl* web_contents) {
  OPTIONAL_TRACE_EVENT0(
      TRACE_DISABLED_BY_DEFAULT("content.verbose"),
      "WebContentsImpl::RemoveWebContentsDestructionObserver");
  web_contents_destruction_observers_.erase(web_contents);
}

void WebContentsImpl::AddRenderWidgetHostDestructionObserver(
    RenderWidgetHost* render_widget_host) {
  OPTIONAL_TRACE_EVENT0(
      TRACE_DISABLED_BY_DEFAULT("content.verbose"),
      "WebContentsImpl::AddRenderWidgetHostDestructionObserver");

  DCHECK(
      !render_widget_host_destruction_observers_.contains(render_widget_host));

  render_widget_host_destruction_observers_.insert(
      {render_widget_host,
       std::make_unique<RenderWidgetHostDestructionObserver>(
           this, render_widget_host)});
}

void WebContentsImpl::RemoveRenderWidgetHostDestructionObserver(
    RenderWidgetHost* render_widget_host) {
  OPTIONAL_TRACE_EVENT0(
      TRACE_DISABLED_BY_DEFAULT("content.verbose"),
      "WebContentsImpl::RemoveRenderWidgetHostDestructionObserver");
  render_widget_host_destruction_observers_.erase(render_widget_host);
}

void WebContentsImpl::AddObserver(WebContentsObserver* observer) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::AddObserver");
  observers_.AddObserver(observer);
}

void WebContentsImpl::RemoveObserver(WebContentsObserver* observer) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::RemoveObserver");
  observers_.RemoveObserver(observer);
}

std::set<RenderWidgetHostViewBase*>
WebContentsImpl::GetRenderWidgetHostViewsInWebContentsTree() {
  std::set<RenderWidgetHostViewBase*> result;
  // Views for speculative render frame host, prerender and BFCache could also
  // be frame sink id owner and should move frame sink id registration from
  // inner WebContents to outer WebContents when WebContents is
  // attached/detached.
  auto* speculative_main_frame =
      GetPrimaryFrameTree().root()->render_manager()->speculative_frame_host();
  for (auto* main_frame : GetOutermostMainFramesForViewChange()) {
    if (main_frame == speculative_main_frame) {
      // Skip speculative main frame host as it will be handled by
      // main_frame->ForEachRenderFrameHostImplIncludingSpeculative.
      continue;
    }
    main_frame->ForEachRenderFrameHostImplIncludingSpeculative(
        [&result](RenderFrameHostImpl* rfh) {
          if (auto* view =
                  static_cast<RenderWidgetHostViewBase*>(rfh->GetView())) {
            result.insert(view);
          }
        });
  }
  return result;
}

void WebContentsImpl::Activate() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Activate");
  if (delegate_) {
    delegate_->ActivateContents(this);
  }
}

void WebContentsImpl::SetTopControlsShownRatio(
    RenderWidgetHostImpl* render_widget_host,
    float ratio) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetTopControlsShownRatio",
                        "render_widget_host", render_widget_host, "ratio",
                        ratio);
  if (!delegate_) {
    return;
  }

  RenderFrameHostImpl* rfh = GetPrimaryMainFrame();
  if (!rfh || render_widget_host != rfh->GetRenderWidgetHost()) {
    return;
  }

  delegate_->SetTopControlsShownRatio(this, ratio);
}

void WebContentsImpl::SetTopControlsGestureScrollInProgress(bool in_progress) {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::SetTopControlsGestureScrollInProgress",
      "in_progress", in_progress);
  if (delegate_) {
    delegate_->SetTopControlsGestureScrollInProgress(in_progress);
  }
}

void WebContentsImpl::RenderWidgetCreated(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RenderWidgetCreated",
                        "render_widget_host", render_widget_host);
  created_widgets_[render_widget_host->GetFrameSinkId()] = render_widget_host;
}

void WebContentsImpl::RenderWidgetDeleted(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RenderWidgetDeleted",
                        "render_widget_host", render_widget_host);
  // Note that IsBeingDestroyed() can return true at this point as
  // ~WebContentsImpl() calls RFHM::ClearRFHsPendingShutdown(), which might lead
  // us here.
  created_widgets_.erase(render_widget_host->GetFrameSinkId());

  if (IsBeingDestroyed()) {
    return;
  }

  if (render_widget_host == pointer_lock_widget_) {
    LostPointerLock(pointer_lock_widget_);
  }

  CancelKeyboardLock(render_widget_host);
}

void WebContentsImpl::RenderWidgetWasResized(
    RenderWidgetHostImpl* render_widget_host,
    bool width_changed) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::RenderWidgetWasResized",
                        "render_widget_host", render_widget_host,
                        "width_changed", width_changed);
  RenderFrameHostImpl* rfh = GetPrimaryMainFrame();
  if (!rfh || render_widget_host != rfh->GetRenderWidgetHost()) {
    return;
  }

  observers_.NotifyObservers(&WebContentsObserver::PrimaryMainFrameWasResized,
                             width_changed);
}

void WebContentsImpl::PreHandleDragUpdate(const DropData& drop_data,
                                          const gfx::PointF& client_pt) {
  if (delegate_) {
    delegate_->PreHandleDragUpdate(drop_data, client_pt);
  }
}

void WebContentsImpl::PreHandleDragExit() {
  if (delegate_) {
    delegate_->PreHandleDragExit();
  }
}

KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(
    const input::NativeWebKeyboardEvent& event) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::PreHandleKeyboardEvent");
  auto* outermost_contents = GetOutermostWebContents();
  // TODO(wjmaclean): Generalize this to forward all key events to the outermost
  // delegate's handler.
  if (outermost_contents != this && IsFullscreen() &&
      event.windows_key_code == ui::VKEY_ESCAPE) {
    // When an inner WebContents has focus and is fullscreen, redirect <esc>
    // key events to the outermost WebContents so it can be handled by that
    // WebContents' delegate.
    if (outermost_contents->PreHandleKeyboardEvent(event) ==
        KeyboardEventProcessingResult::HANDLED) {
      return KeyboardEventProcessingResult::HANDLED;
    }
  }
  return delegate_ ? delegate_->PreHandleKeyboardEvent(this, event)
                   : KeyboardEventProcessingResult::NOT_HANDLED;
}

bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::HandleMouseEvent");
  // Handle mouse button back/forward in the browser process after the render
  // process is done with the event. This ensures all renderer-initiated history
  // navigations can be treated consistently.
  if (event.GetType() == blink::WebInputEvent::Type::kMouseUp) {
    WebContentsImpl* outermost = GetOutermostWebContents();
    if (event.button == blink::WebPointerProperties::Button::kBack &&
        outermost->GetController().CanGoBack()) {
      outermost->GetController().GoBack();
      return true;
    } else if (event.button == blink::WebPointerProperties::Button::kForward &&
               outermost->GetController().CanGoForward()) {
      outermost->GetController().GoForward();
      return true;
    }
  }
  return false;
}

bool WebContentsImpl::HandleKeyboardEvent(
    const input::NativeWebKeyboardEvent& event) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::HandleKeyboardEvent");
  if (browser_plugin_embedder_ &&
      browser_plugin_embedder_->HandleKeyboardEvent(event)) {
    return true;
  }
  return delegate_ && delegate_->HandleKeyboardEvent(this, event);
}

bool WebContentsImpl::HandleWheelEvent(const blink::WebMouseWheelEvent& event) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::HandleWheelEvent");
#if !BUILDFLAG(IS_MAC)
  // On platforms other than Mac, control+mousewheel may change zoom. On Mac,
  // this isn't done for two reasons:
  //   -the OS already has a gesture to do this through pinch-zoom
  //   -if a user starts an inertial scroll, let's go, and presses control
  //      (i.e. control+tab) then the OS's buffered scroll events will come in
  //      with control key set which isn't what the user wants
  if (delegate_ && event.wheel_ticks_y &&
      event.event_action == blink::WebMouseWheelEvent::EventAction::kPageZoom) {
    // Count only integer cumulative scrolls as zoom events; this handles
    // smooth scroll and regular scroll device behavior.
    zoom_scroll_remainder_ += event.wheel_ticks_y;
    int whole_zoom_scroll_remainder_ = std::lround(zoom_scroll_remainder_);
    zoom_scroll_remainder_ -= whole_zoom_scroll_remainder_;
    if (whole_zoom_scroll_remainder_ != 0) {
      delegate_->ContentsZoomChange(whole_zoom_scroll_remainder_ > 0);
    }
    return true;
  }
#endif
  return false;
}

bool WebContentsImpl::PreHandleGestureEvent(
    const blink::WebGestureEvent& event) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::PreHandleGestureEvent");
  if (ignore_zoom_gestures_) {
    if (event.GetType() == blink::WebInputEvent::Type::kGestureDoubleTap) {
      return true;
    }

    // Disable pinch zooming in app windows.
    if (blink::WebInputEvent::IsPinchGestureEventType(event.GetType())) {
      // Only suppress pinch events that cause a scale change. We still
      // allow synthetic wheel events for touchpad pinch to go to the page.
      return !(event.SourceDevice() == blink::WebGestureDevice::kTouchpad &&
               event.NeedsWheelEvent());
    }
  }

  return delegate_ && delegate_->PreHandleGestureEvent(this, event);
}

input::RenderWidgetHostInputEventRouter*
WebContentsImpl::GetInputEventRouter() {
  if (!IsBeingDestroyed()) {
    if (GetOuterWebContents()) {
      return GetOuterWebContents()->GetInputEventRouter();
    }

    if (surface_embed_connector_) {
      return surface_embed_connector_->GetInputEventRouter();
    }

    if (!rwh_input_event_router_.get()) {
      rwh_input_event_router_ =
          MakeRefCounted<input::RenderWidgetHostInputEventRouter>(
              GetHostFrameSinkManager(), this);
    }
  }
  return rwh_input_event_router_.get();
}

void WebContentsImpl::GetRenderWidgetHostAtPointAsynchronously(
    RenderWidgetHostViewBase* root_view,
    const gfx::PointF& point,
    base::OnceCallback<void(base::WeakPtr<RenderWidgetHostViewBase>,
                            std::optional<gfx::PointF>)> callback) {
  GetInputEventRouter()->GetRenderWidgetHostAtPointAsynchronously(
      root_view, point, base::BindOnce(&RunCallback, std::move(callback)));
}

std::vector<RenderWidgetHostView*>
WebContentsImpl::GetRenderWidgetHostViewsForTests() {
  auto input_hosts =
      GetInputEventRouter()->GetRenderWidgetHostViewInputsForTests();
  std::vector<RenderWidgetHostView*> hosts;
  for (auto host : input_hosts) {
    hosts.push_back(static_cast<RenderWidgetHostViewBase*>(host));
  }
  return hosts;
}

RenderWidgetHostImpl* WebContentsImpl::GetFocusedRenderWidgetHost(
    RenderWidgetHostImpl* receiving_widget) {
  // Events for widgets other than the main frame (e.g., popup menus) should be
  // forwarded directly to the widget they arrived on.
  if (receiving_widget != GetPrimaryMainFrame()->GetRenderWidgetHost()) {
    return receiving_widget;
  }

  // Return nullptr if the focused frame tree is outside of this WebContents.
  // This function should be called mostly when the focused frame is inside this
  // WebContents and we should drop events if it is not.
  // DevTools code might call this function when the focused frame is outside of
  // this WebContents, and it behaves the same whether we return nullptr or
  // receiving_widget.
  if (!GetFocusedFrameTree()) {
    return nullptr;
  }

  // If the focused WebContents is a guest WebContents, then get the focused
  // frame in the embedder WebContents instead.
  FrameTreeNode* focused_frame = GetFocusedFrameTree()->GetFocusedFrame();

  if (!focused_frame) {
    return receiving_widget;
  }

  // The view may be null if a subframe's renderer process has crashed while
  // the subframe has focus.  Drop the event in that case.  Do not give
  // it to the main frame, so that the user doesn't unexpectedly type into the
  // wrong frame if a focused subframe renderer crashes while they type.
  RenderWidgetHostView* view = focused_frame->current_frame_host()->GetView();
  if (!view) {
    return nullptr;
  }

  return RenderWidgetHostImpl::From(view->GetRenderWidgetHost());
}

RenderWidgetHostImpl* WebContentsImpl::GetRenderWidgetHostWithPageFocus() {
  FrameTree* focused_frame_tree = GetFocusedFrameTree();
  if (!focused_frame_tree) {
    return nullptr;
  }

  return focused_frame_tree->root()
      ->current_frame_host()
      ->GetRenderWidgetHost();
}

bool WebContentsImpl::CanEnterFullscreenMode(
    RenderFrameHostImpl* requesting_frame) {
  // It's possible that this WebContents was spawned while blocking UI was on
  // the screen, or that it was downstream from a WebContents when UI was
  // blocked. Therefore, disqualify it from fullscreen if it or any upstream
  // WebContents has an active blocker.
  return delegate_ &&
         std::ranges::all_of(GetAllOpeningWebContents(this),
                             [](auto opener) {
                               return opener->fullscreen_blocker_count_ == 0;
                             }) &&
         delegate_->CanEnterFullscreenModeForTab(requesting_frame);
}

void WebContentsImpl::EnterFullscreenMode(
    RenderFrameHostImpl* requesting_frame,
    const blink::mojom::FullscreenOptions& options) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
  DCHECK(CanEnterFullscreenMode(requesting_frame));
  DCHECK(requesting_frame->IsActive());
  CHECK(
      ContainsOrIsFocusedWebContents() ||
      GetContentClient()->browser()->IsFullscreenAllowedForUnfocusedWebContents(
          this));
  // Ensure the window is made active to take input focus. The window may be
  // inactive when sites request fullscreen via capability delegation, consume
  // transient activation from a gesture made before another window was focused,
  // or if the site has been granted the Automatic Fullscreen content setting.
  // Note: requests by inactive tabs of multi-tab windows are rejected earlier.
  Activate();

  // When WebView is the `delegate_` we can end up with VisualProperties changes
  // synchronously. Notify the view ahead so it can handle the transition.
  if (auto* view = GetRenderWidgetHostView()) {
    static_cast<RenderWidgetHostViewBase*>(view)->EnterFullscreenMode(options);
  }

  if (delegate_) {
    // This may spin the message loop and destroy this object.
    // See crbug.com/1506535, crbug.com/498752242.
    base::WeakPtr<WebContents> weak_ptr = GetWeakPtr();
    delegate_->EnterFullscreenModeForTab(requesting_frame, options);
    if (!weak_ptr) {
      return;
    }

    if (keyboard_lock_widget_) {
      delegate_->RequestKeyboardLock(this, esc_key_locked_);
    }
  }

  observers_.NotifyObservers(
      &WebContentsObserver::DidToggleFullscreenModeForTab, IsFullscreen(),
      false);
  FullscreenContentsSet(GetBrowserContext())->insert(this);
}

void WebContentsImpl::ExitFullscreenMode(bool will_cause_resize) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ExitFullscreenMode",
                        "will_cause_resize", will_cause_resize);
  // When WebView is the `delegate_` we can end up with VisualProperties changes
  // synchronously. Notify the view ahead so it can handle the transition.
  if (auto* view = GetRenderWidgetHostView()) {
    static_cast<RenderWidgetHostViewBase*>(view)->ExitFullscreenMode();
  }

  GetFullscreenUserData(GetBrowserContext())
      ->last_exits()
      ->insert_or_assign(GetPrimaryMainFrame()->GetLastCommittedOrigin(),
                         base::TimeTicks::Now());

  if (delegate_) {
    // This may spin the message loop and destroy this object.
    // See crbug.com/1506535, crbug.com/498752242.
    base::WeakPtr<WebContents> weak_ptr = GetWeakPtr();
    delegate_->ExitFullscreenModeForTab(this);
    if (!weak_ptr) {
      return;
    }

    if (keyboard_lock_widget_) {
      delegate_->CancelKeyboardLockRequest(this);
    }
  }

  // The fullscreen state is communicated to the renderer through a resize
  // message. If the change in fullscreen state doesn't cause a view resize
  // then we must ensure web contents exit the fullscreen state by explicitly
  // sending a resize message. This is required for the situation of the browser
  // moving the view into a "browser fullscreen" state and then the contents
  // entering "tab fullscreen". Exiting the contents "tab fullscreen" then won't
  // have the side effect of the view resizing, hence the explicit call here is
  // required.
  if (!will_cause_resize) {
    if (RenderWidgetHostView* rwhv = GetRenderWidgetHostView()) {
      if (RenderWidgetHost* render_widget_host = rwhv->GetRenderWidgetHost()) {
        render_widget_host->SynchronizeVisualProperties();
      }
    }
  }

  current_fullscreen_frame_id_ = GlobalRenderFrameHostId();

  observers_.NotifyObservers(
      &WebContentsObserver::DidToggleFullscreenModeForTab, IsFullscreen(),
      will_cause_resize);

  if (safe_area_insets_host_) {
    safe_area_insets_host_->DidExitFullscreen();
  }

  FullscreenContentsSet(GetBrowserContext())->erase(this);
}

void WebContentsImpl::FullscreenStateChanged(
    RenderFrameHostImpl* rfh,
    bool is_fullscreen,
    blink::mojom::FullscreenOptionsPtr options) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::FullscreenStateChanged",
                        "render_frame_host", rfh, "is_fullscreen",
                        is_fullscreen);

  GetView()->FullscreenStateChanged(is_fullscreen);

  if (is_fullscreen) {
    if (options.is_null()) {
      ReceivedBadMessage(rfh->GetProcess(),
                         bad_message::WCI_INVALID_FULLSCREEN_OPTIONS);
      return;
    }

    if (delegate_) {
      delegate_->FullscreenStateChangedForTab(rfh, *options);
    }

    if (bool was_inserted = fullscreen_frames_.insert(rfh).second;
        was_inserted) {
      FullscreenFrameSetUpdated();
    }
    return;
  }

  // If |rfh| is no longer in fullscreen, remove it and any descendants.
  // See https://fullscreen.spec.whatwg.org.
  size_t size_before_deletion = fullscreen_frames_.size();
  std::erase_if(fullscreen_frames_, [&](RenderFrameHostImpl* current) {
    while (current) {
      if (current == rfh) {
        return true;
      }
      // We only look for direct parents. fencedframes will not enter
      // fullscreen.
      current = current->GetParent();
    }
    return false;
  });

  if (size_before_deletion != fullscreen_frames_.size()) {
    FullscreenFrameSetUpdated();
  }
}

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
bool WebContentsImpl::CanUseWindowingControls(
    RenderFrameHostImpl* requesting_frame) {
  return GetDelegate() &&
         GetDelegate()->CanUseWindowingControls(requesting_frame);
}

void WebContentsImpl::Maximize() {
  if (!GetDelegate()) {
    return;
  }
  GetDelegate()->MaximizeFromWebAPI();
}

void WebContentsImpl::Minimize() {
  if (!GetDelegate()) {
    return;
  }
  GetDelegate()->MinimizeFromWebAPI();
}

void WebContentsImpl::Restore() {
  if (!GetDelegate()) {
    return;
  }
  GetDelegate()->RestoreFromWebAPI();
}

void WebContentsImpl::SetResizable(bool resizable) {
  if (!GetDelegate()) {
    return;
  }
  GetDelegate()->SetResizableFromWebAPI(resizable);
}
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)

// TODO(laurila, crbug.com/1466855): Map into new `ui::DisplayState` enum
// instead of `ui::mojom::WindowShowState`.
ui::mojom::WindowShowState WebContentsImpl::GetWindowShowState() {
  return GetDelegate() ? GetDelegate()->GetWindowShowState()
                       : ui::mojom::WindowShowState::kDefault;
}

DevicePostureProviderImpl* WebContentsImpl::GetDevicePostureProvider() {
  return DevicePostureProviderImpl::GetOrCreate(this);
}

bool WebContentsImpl::GetResizable() {
  return GetDelegate() && GetDelegate()->GetCanResize();
}

bool WebContentsImpl::GetIsAlwaysOnTop() {
  return GetDelegate() && GetDelegate()->GetIsAlwaysOnTop();
}

void WebContentsImpl::FullscreenFrameSetUpdated() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::FullscreenFrameSetUpdated");
  if (fullscreen_frames_.empty()) {
    current_fullscreen_frame_id_ = GlobalRenderFrameHostId();
    return;
  }

  // Find the current fullscreen frame and call the observers.
  // If frame A is fullscreen, then frame B goes into inner fullscreen, then B
  // exits fullscreen - that will result in A being fullscreen.
  RenderFrameHostImpl* new_fullscreen_frame = *std::max_element(
      fullscreen_frames_.begin(), fullscreen_frames_.end(), FrameCompareDepth);

  // If we have already notified observers about this frame then we should not
  // fire the observers again.
  if (new_fullscreen_frame->GetGlobalId() == current_fullscreen_frame_id_) {
    return;
  }
  current_fullscreen_frame_id_ = new_fullscreen_frame->GetGlobalId();

  observers_.NotifyObservers(&WebContentsObserver::DidAcquireFullscreen,
                             new_fullscreen_frame);
  if (safe_area_insets_host_) {
    safe_area_insets_host_->DidAcquireFullscreen(new_fullscreen_frame);
  }
}

PageVisibilityState WebContentsImpl::CalculatePageVisibilityState(
    Visibility visibility) const {
  // Only hide the page if there are no entities capturing screenshots
  // or video (e.g. mirroring or WebXR). If there are, apply the correct state
  // of kHidden or kHiddenButPainting.
  bool web_contents_visible_in_vr = false;
#if BUILDFLAG(ENABLE_VR)
  web_contents_visible_in_vr =
      XRRuntimeManagerImpl::GetImmersiveSessionWebContents() == this;
#endif

  // If there are entities in Picture-in-Picture mode, don't activate the
  // "disable rendering" optimization, since it still needs to drive the video,
  // and possibly other elements on the page like canvas, to keep the picture in
  // picture window up to date.
  if (visibility == Visibility::VISIBLE || visible_capturer_count_ > 0 ||
      web_contents_visible_in_vr) {
    return PageVisibilityState::kVisible;
  } else if ((!did_first_set_visible_ && initially_hidden_but_painting_) ||
             hidden_capturer_count_ > 0 || has_picture_in_picture_video_ ||
             has_picture_in_picture_document_) {
    return PageVisibilityState::kHiddenButPainting;
  }
  return PageVisibilityState::kHidden;
}

PageVisibilityState WebContentsImpl::GetPageVisibilityState() const {
  return CalculatePageVisibilityState(visibility_);
}

void WebContentsImpl::UpdateVisibilityAndNotifyPageAndView(
    Visibility new_visibility,
    bool is_activity) {
  DCHECK(!IsBeingDestroyed());

  if (GetOuterWebContents()) {
    new_visibility =
        std::min(new_visibility, GetOuterWebContents()->GetVisibility());
    RenderFrameProxyHost* proxy = GetRenderManager()->GetProxyToOuterDelegate();
    if (proxy && proxy->cross_process_frame_connector()) {
      new_visibility =
          std::min(new_visibility,
                   FrameVisibilityToVisibility(
                       proxy->cross_process_frame_connector()->visibility()));
    }
  }

  PageVisibilityState page_visibility =
      CalculatePageVisibilityState(new_visibility);

  TRACE_EVENT1("content",
               "WebContentsImpl::UpdateVisibilityAndNotifyPageAndView",
               "page_visibility", page_visibility);

  // True if the instance is being hidden or revealed.
  const bool hide_or_reveal = (visibility_ == Visibility::HIDDEN) !=
                              (new_visibility == Visibility::HIDDEN);

  if (new_visibility != visibility_ ||
      (new_visibility == Visibility::VISIBLE && !did_first_set_visible_)) {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.OnVisibilityWillChange");
    observers_.NotifyObservers(&WebContentsObserver::OnVisibilityWillChange,
                               new_visibility);
  }

  // Send ax modes to renderers before they start painting if they are being
  // revealed.
  if (!is_never_composited_ && hide_or_reveal &&
      new_visibility != Visibility::HIDDEN) {
    BrowserAccessibilityStateImpl::GetInstance()->OnWebContentsRevealed(this);
  }

  // Prerendering relies on overriding FrameTree::Delegate::IsHidden,
  // while for other frame trees FrameTree::Delegate::IsHidden
  // resolves to WebContents' visibility, so we avoid Prerender RennderViewHosts
  // here.
  ForEachRenderViewHostTypes view_mask =
      static_cast<ForEachRenderViewHostTypes>(
          ForEachRenderViewHostTypes::kBackForwardCacheViews |
          ForEachRenderViewHostTypes::kActiveViews);

  RenderViewHostIterationCallback update_frame_tree_visibility =
      base::BindRepeating(
          [](PageVisibilityState page_visibility,
             RenderViewHostImpl* render_view_host) {
            // If we are a guest frame tree, allow the visibility (ie
            // display:none) to override the page visibility.
            if (render_view_host->frame_tree()->is_guest()) {
              RenderFrameProxyHost* proxy = render_view_host->frame_tree()
                                                ->root()
                                                ->render_manager()
                                                ->GetProxyToOuterDelegate();
              if (proxy && proxy->cross_process_frame_connector()->IsHidden()) {
                page_visibility = PageVisibilityState::kHidden;
              }
            }

            render_view_host->SetFrameTreeVisibility(page_visibility);
          },
          page_visibility);

  if (page_visibility != PageVisibilityState::kHidden) {
    // This shows the Page before showing the individual RenderWidgets, as
    // RenderWidgets will work to produce compositor frames and handle input
    // as soon as they are shown. But the Page and other classes do not expect
    // to be producing frames when the Page is hidden. So we make sure the Page
    // is shown first.
    ForEachRenderViewHost(view_mask, update_frame_tree_visibility);
  }

  SetPrimaryMainFrameViewVisibility(new_visibility);

  if (page_visibility == PageVisibilityState::kHidden) {
    // Similar to when showing the page, we only hide the page after
    // hiding the individual RenderWidgets.
    ForEachRenderViewHost(view_mask, update_frame_tree_visibility);
  }

  // Make sure to call SetVisibilityAndNotifyObservers(VISIBLE) before notifying
  // the CrossProcessFrameConnector.
  if (is_activity && new_visibility == Visibility::VISIBLE) {
    last_active_time_ticks_ = base::TimeTicks::Now();
    last_active_time_ = base::Time::Now();
  }
  SetVisibilityAndNotifyObservers(new_visibility);

  // Inner WebContents are skipped in ForEachRenderViewHost() above, which
  // causes inner WebContents to not be notified of visibility changes.
  for (WebContents* inner : GetInnerWebContents()) {
    static_cast<WebContentsImpl*>(inner)->UpdateVisibilityAndNotifyPageAndView(
        new_visibility, is_activity);
  }

  if (!is_never_composited_ && hide_or_reveal &&
      new_visibility == Visibility::HIDDEN) {
    BrowserAccessibilityStateImpl::GetInstance()->OnWebContentsHidden(this);
  }

  // We cannot show a page or capture video unless there is a valid renderer
  // associated with this web contents. The navigation controller for this page
  // must be set to active (allowing navigation to complete, a renderer and its
  // associated views to be created, etc.) if any of these conditions holds.
  //
  // Previously, it was possible for browser-side code to try to capture video
  // from a restored tab (for a variety of reasons, including the browser
  // creating preview thumbnails) and the tab would never actually load. By
  // keying this behavior off of |page_visibility| instead of just
  // |new_visibility| we avoid this case. See crbug.com/1020782 for more
  // context.
  //
  // SetActive() can immediately begin a navigation, synchronously emitting the
  // relevant events. Ensure this is done after the WebContents has successfully
  // transitioned to the visible state. See crbug.com/383189046 for details.
  GetController().SetActive(page_visibility != PageVisibilityState::kHidden);

  // DelegateWasShown() keeps track of crash metrics. This is safe to call for
  // GuestViews, and inner frame trees. Call this only after visibility changes
  // and navigations have been processed to ensure clients have all information
  // necessary to log the correct crash metrics.
  if (page_visibility != PageVisibilityState::kHidden) {
    for (FrameTreeNode* node :
         FrameTree::SubtreeAndInnerTreeNodes(GetPrimaryMainFrame())) {
      if (RenderFrameProxyHost* proxy_to_parent_or_outer_delegate =
              node->render_manager()->GetProxyToParentOrOuterDelegate()) {
        proxy_to_parent_or_outer_delegate->cross_process_frame_connector()
            ->DelegateWasShown();
      }
    }
  }
}

void WebContentsImpl::SetPrimaryMainFrameViewVisibility(Visibility visibility) {
  PageVisibilityState page_visibility =
      CalculatePageVisibilityState(visibility);

  // A crashed frame might be covered by a sad tab. See docs on SadTabHelper
  // exactly when it is or isn't. Either way, don't make it visible.
  bool view_is_visible =
      !IsCrashed() && page_visibility != PageVisibilityState::kHidden;

  TRACE_EVENT1("content", "WebContentsImpl::SetPrimaryMainFrameViewVisibility",
               "view_is_visible", view_is_visible);

  // |GetRenderWidgetHostView()| can be null if the user middle clicks a link to
  // open a tab in the background, then closes the tab before selecting it.
  // This is because closing the tab calls WebContentsImpl::Destroy(), which
  // removes the |GetRenderViewHost()|; then when we actually destroy the
  // window, OnWindowPosChanged() notices and calls WasHidden() (which
  // calls us).
  if (auto* view =
          static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView())) {
    if (view_is_visible) {
      view->ShowWithVisibility(page_visibility);
    } else if (visibility == Visibility::HIDDEN) {
      view->Hide();
    } else {
      view->WasOccluded();
    }
  }

  SetVisibilityForChildViews(view_is_visible);
}

#if BUILDFLAG(IS_ANDROID)
void WebContentsImpl::UpdateUserGestureCarryoverInfo() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::UpdateUserGestureCarryoverInfo");
  if (delegate_) {
    delegate_->UpdateUserGestureCarryoverInfo(this);
  }
}
#endif

bool WebContentsImpl::IsFullscreen() {
  return delegate_ && delegate_->IsFullscreenForTabOrPending(this);
}

bool WebContentsImpl::ShouldShowStaleContentOnEviction() {
  return GetDelegate() && GetDelegate()->ShouldShowStaleContentOnEviction(this);
}

blink::mojom::DisplayMode WebContentsImpl::GetDisplayMode() const {
  return delegate_ ? delegate_->GetDisplayMode(this)
                   : blink::mojom::DisplayMode::kBrowser;
}

blink::mojom::ApplicationContext WebContentsImpl::GetApplicationContext()
    const {
  return delegate_ ? delegate_->GetApplicationContext(this)
                   : blink::mojom::ApplicationContext::kNone;
}

void WebContentsImpl::RequestToLockPointer(
    RenderWidgetHostImpl* render_widget_host,
    bool user_gesture,
    bool last_unlocked_by_target) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RequestPointerLock",
                        "render_widget_host", render_widget_host);
  if (render_widget_host->frame_tree()->is_fenced_frame()) {
    // The renderer should have checked and disallowed the request for fenced
    // frames in PointerLockController and dispatched pointerlockerror. Ignore
    // the request and mark it as bad if it didn't happen for some reason.
    ReceivedBadMessage(render_widget_host->GetProcess(),
                       bad_message::WCI_REQUEST_LOCK_MOUSE_FENCED_FRAME);
    return;
  }
  if (HasPointerLockWidgetInParentChain()) {
    render_widget_host->GotResponseToPointerLockRequest(
        blink::mojom::PointerLockResult::kAlreadyLocked);
    return;
  }

  bool widget_in_frame_tree = false;
  for (FrameTreeNode* node : primary_frame_tree_.Nodes()) {
    if (node->current_frame_host()->GetRenderWidgetHost() ==
        render_widget_host) {
      widget_in_frame_tree = true;
      break;
    }
  }

  WebContentsDelegate* delegate = GetFirstWebContentsDelegate();
  if (widget_in_frame_tree && delegate) {
    SetPointerLockWidgetInParentChain(render_widget_host);
    observers_.NotifyObservers(&WebContentsObserver::PointerLockRequested);
    delegate->RequestPointerLock(this, user_gesture, last_unlocked_by_target);
  } else {
    render_widget_host->GotResponseToPointerLockRequest(
        blink::mojom::PointerLockResult::kWrongDocument);
  }
}

bool WebContentsImpl::IsWaitingForPointerLockPrompt(
    RenderWidgetHostImpl* render_widget_host) {
  WebContentsDelegate* delegate = GetFirstWebContentsDelegate();
  if (!delegate || (pointer_lock_widget_ != render_widget_host)) {
    return false;
  }
  return delegate->IsWaitingForPointerLockPrompt(this);
}

void WebContentsImpl::LostPointerLock(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::LostPointerLock",
                        "render_widget_host", render_widget_host);
  CHECK(pointer_lock_widget_);

  if (WebContentsImpl::FromRenderWidgetHostImpl(pointer_lock_widget_) != this) {
    return pointer_lock_widget_->delegate()->LostPointerLock(
        render_widget_host);
  }

  pointer_lock_widget_->SendPointerLockLost();
  SetPointerLockWidgetInParentChain(nullptr);

  WebContentsDelegate* delegate = GetFirstWebContentsDelegate();
  if (delegate) {
    delegate->LostPointerLock();
  }
}

bool WebContentsImpl::IsPointerLockSandboxedForWidget(
    RenderWidgetHostImpl* render_widget_host) {
  // Check the sandbox flags of the frame that owns the requesting widget.
  // It is ok to only check the top-most frame of the widget, because any
  // subframes within the widget will be at least as restrictive as it. Any
  // additional restrictions imposed on subframes of the widget cannot be
  // enforced by the browser process, because they share a renderer process
  // with the top-most frame of the widget.
  // Note: crbug.com/492211919
  for (FrameTreeNode* node : GetPrimaryFrameTree().Nodes()) {
    RenderFrameHostImpl* rfh = node->current_frame_host();
    if (rfh && rfh->GetRenderWidgetHost() == render_widget_host) {
      return rfh->IsSandboxed(network::mojom::WebSandboxFlags::kPointerLock);
    }
  }
  return false;
}

bool WebContentsImpl::HasPointerLock(RenderWidgetHostImpl* render_widget_host) {
  if (auto* connector = GetSurfaceEmbedConnector()) {
    return static_cast<SurfaceEmbedConnectorImpl*>(connector)->HasPointerLock(
        render_widget_host);
  }
  // To verify if the mouse is locked, the mouse_lock_widget_ needs to be
  // assigned to the widget that requested the mouse lock, and the top-level
  // platform RenderWidgetHostView needs to hold the mouse lock from the OS.
  auto* widget_host = GetTopLevelRenderWidgetHostView();
  return pointer_lock_widget_ == render_widget_host && widget_host &&
         widget_host->IsPointerLocked();
}

RenderWidgetHostImpl* WebContentsImpl::GetPointerLockWidget() {
  if (auto* connector = GetSurfaceEmbedConnector()) {
    return static_cast<SurfaceEmbedConnectorImpl*>(connector)
        ->GetPointerLockWidget();
  }
  auto* widget_host = GetTopLevelRenderWidgetHostView();
  if (widget_host && widget_host->IsPointerLocked()) {
    return pointer_lock_widget_;
  }

  return nullptr;
}

bool WebContentsImpl::RequestKeyboardLock(
    RenderWidgetHostImpl* render_widget_host,
    bool esc_key_locked) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::RequestKeyboardLock",
                        "render_widget_host", render_widget_host,
                        "esc_key_locked", esc_key_locked);
  DCHECK(render_widget_host);
  if (WebContentsImpl::FromRenderWidgetHostImpl(render_widget_host) != this) {
    NOTREACHED();
  }

  // KeyboardLock is only supported when called by the top-level browsing
  // context and is not supported in embedded content scenarios such as
  // GuestView guests (<webview> tags, PDF viewer). However, some embedders
  // (e.g. WebUIBrowserWindow) attach top-level tabs as inner WebContents and
  // opt in via AllowKeyboardLockForInnerContents().
  if (GetOuterWebContents() &&
      (!delegate_ || !delegate_->AllowKeyboardLockForInnerContents(this))) {
    render_widget_host->GotResponseToKeyboardLockRequest(false);
    return false;
  }

  esc_key_locked_ = esc_key_locked;
  keyboard_lock_widget_ = render_widget_host;

  if (delegate_) {
    observers_.NotifyObservers(&WebContentsObserver::KeyboardLockRequested);
    delegate_->RequestKeyboardLock(this, esc_key_locked_);
  }
  return true;
}

void WebContentsImpl::CancelKeyboardLock(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::CancelKeyboardLockRequest",
                        "render_widget_host", render_widget_host);
  if (!keyboard_lock_widget_ || render_widget_host != keyboard_lock_widget_) {
    return;
  }

  RenderWidgetHostImpl* old_keyboard_lock_widget = keyboard_lock_widget_;
  keyboard_lock_widget_ = nullptr;

  if (delegate_) {
    delegate_->CancelKeyboardLockRequest(this);
  }

  old_keyboard_lock_widget->CancelKeyboardLock();
}

RenderWidgetHostImpl* WebContentsImpl::GetKeyboardLockWidget() {
  return keyboard_lock_widget_;
}

bool WebContentsImpl::OnRenderFrameProxyVisibilityChanged(
    RenderFrameProxyHost* render_frame_proxy_host,
    blink::mojom::FrameVisibility visibility) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnRenderFrameProxyVisibilityChanged",
                        "visibility", static_cast<int>(visibility));

  // Check that we are responsible for the RenderFrameProxyHost by checking that
  // its FrameTreeNode matches our `primary_frame_tree_` root. Otherwise this is
  // not a visibility change that affects all frames in an inner WebContents.
  if (render_frame_proxy_host->frame_tree_node() !=
      primary_frame_tree_.root()) {
    return false;
  }

  // Not reachable with MPArch based guests.
  CHECK(!base::FeatureList::IsEnabled(features::kGuestViewMPArch));

  DCHECK(GetOuterWebContents());

  Visibility capped_visibility =
      std::min(FrameVisibilityToVisibility(visibility),
               GetOuterWebContents()->GetVisibility());

  switch (capped_visibility) {
    case Visibility::VISIBLE:
      WasShown();
      break;
    case Visibility::HIDDEN:
      WasHidden();
      break;
    case Visibility::OCCLUDED:
      WasOccluded();
      break;
  }
  return true;
}

FrameTree* WebContentsImpl::CreateNewWindow(
    RenderFrameHostImpl* opener,
    const mojom::CreateNewWindowParams& params,
    bool is_new_browsing_instance,
    bool has_user_gesture,
    SessionStorageNamespace* session_storage_namespace) {
  TRACE_EVENT2("browser,content,navigation", "WebContentsImpl::CreateNewWindow",
               "opener", opener, "params", params);
  DCHECK(opener);

  // Block Document Picture-in-Picture window creation if the delegate reports
  // that the OS currently prevents it (e.g., Android in app fullscreen).
  // `NEW_PICTURE_IN_PICTURE` is specific to Document PiP and does not affect
  // traditional video PiP or regular popups.
  if (params.disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE &&
      delegate_ && delegate_->IsDocumentPictureInPictureBlockedBySystem()) {
    return nullptr;
  }

  if (active_file_chooser_) {
    // Do not allow opening a new window or tab while a file select is active
    // file chooser to avoid user confusion over which tab triggered the file
    // chooser.
    opener->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kWarning,
        "window.open blocked due to active file chooser.");
    return nullptr;
  }

  content::ChildProcessId render_process_id = opener->GetProcess()->GetID();
  SiteInstanceImpl* source_site_instance = opener->GetSiteInstance();
  const auto& partition_config =
      source_site_instance->GetSecurityPrincipal().GetStoragePartitionConfig();

  {
    StoragePartition* partition =
        GetBrowserContext()->GetStoragePartition(source_site_instance);
    DOMStorageContextWrapper* dom_storage_context =
        static_cast<DOMStorageContextWrapper*>(
            partition->GetDOMStorageContext());
    SessionStorageNamespaceImpl* session_storage_namespace_impl =
        static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace);
    CHECK(session_storage_namespace_impl->IsFromContext(dom_storage_context));
  }

  // TODO(crbug.com/40202416): Support a way for MPArch guests to support this.
  if (delegate_ &&
      delegate_->IsWebContentsCreationOverridden(
          opener, source_site_instance, params.window_container_type,
          opener->GetLastCommittedURL(), params.frame_name,
          params.target_url)) {
    auto* web_contents_impl =
        static_cast<WebContentsImpl*>(delegate_->CreateCustomWebContents(
            opener, source_site_instance, is_new_browsing_instance,
            opener->GetLastCommittedURL(), params.frame_name, params.target_url,
            params.disposition, *params.features, partition_config,
            session_storage_namespace));
    if (!web_contents_impl) {
      return nullptr;
    }
    web_contents_impl->is_popup_ =
        params.disposition == WindowOpenDisposition::NEW_POPUP;
    return &web_contents_impl->GetPrimaryFrameTree();
  }

  bool renderer_started_hidden =
      params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB;

  bool is_guest =
      base::FeatureList::IsEnabled(features::kGuestViewMPArch)
          ? opener->GetOutermostMainFrame()->frame_tree()->is_guest()
          : IsGuest();
  // While some guest types do not have a guest SiteInstance, the ones that
  // don't all override WebContents creation above.
  CHECK_EQ(source_site_instance->GetSecurityPrincipal().IsGuest(), is_guest);

  // We usually create the new window in the same BrowsingInstance (group of
  // script-related windows), by passing in the current SiteInstance.  However,
  // if the opener is being suppressed, we need to ensure that the new
  // SiteInstance is created in a new BrowsingInstance.
  scoped_refptr<SiteInstance> site_instance;
  if (params.opener_suppressed) {
    if (is_guest) {
      // For guests, noopener windows can be created in a new BrowsingInstance
      // as long as they preserve the guest's StoragePartition.
      site_instance =
          SiteInstance::CreateForGuest(GetBrowserContext(), partition_config);
    } else {
      site_instance = SiteInstance::Create(GetBrowserContext());
    }
  } else {
    site_instance = source_site_instance;
  }

  if (is_guest && base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
    if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(*opener)) {
      return guest->CreateNewWindow(
          params.disposition, params.target_url, params.frame_name,
          std::move(site_instance),
          !params.opener_suppressed ? opener : nullptr);
    }
    return nullptr;
  }

  // Create the new web contents. This will automatically create the new
  // WebContentsView. In the future, we may want to create the view separately.
  CreateParams create_params(GetBrowserContext(), site_instance.get());
  create_params.main_frame_name = params.frame_name;
  create_params.opener_id =
      GlobalRenderFrameHostId(render_process_id, opener->GetRoutingID());
  create_params.opener_suppressed = params.opener_suppressed;
  create_params.initially_hidden = renderer_started_hidden;
  create_params.initial_popup_url = params.target_url;

  // Even though all codepaths leading here are in response to a renderer
  // trying to open a new window, if the new window ends up in a different
  // browsing instance, then the RenderViewHost, RenderWidgetHost,
  // RenderFrameHost constellation is effectively browser initiated
  // the opener's process will not given the routing IDs for the new
  // objects.
  create_params.renderer_initiated_creation = !is_new_browsing_instance;

  if (params.pip_options) {
    create_params.picture_in_picture_options = *(params.pip_options);
  }

  // Check whether there is an available prerendered page for this navigation if
  // this is not for guest. If it exists, take WebContents pre-created for
  // hosting the prerendered page instead of creating new WebContents.
  // TODO(crbug.com/40234240): Instead of filtering out the guest case here,
  // check it and drop prerender requests before starting prerendering.
  std::unique_ptr<WebContentsImpl> new_contents;
  if (!is_guest) {
    new_contents =
        GetPrerenderHostRegistry()->TakePreCreatedWebContentsForNewTabIfExists(
            params, create_params);
    if (new_contents) {
      // The SiteInstance of the pre-created WebContents should be in a
      // different BrowsingInstance from the source SiteInstance, while they
      // should be in the same StoragePartition.
      SiteInstanceImpl* new_site_instance = new_contents->GetSiteInstance();
      DCHECK(!new_site_instance->IsRelatedSiteInstance(source_site_instance));
      DCHECK_EQ(
          new_site_instance->GetSecurityPrincipal().GetStoragePartitionConfig(),
          source_site_instance->GetSecurityPrincipal()
              .GetStoragePartitionConfig());
    }
  }

  if (!new_contents) {
    if (!is_guest) {
      create_params.context = view_->GetNativeView();
      new_contents = WebContentsImpl::Create(create_params);
    } else {
      new_contents =
          GetBrowserPluginGuest()->CreateNewGuestWindow(create_params);
    }
    new_contents->GetController().SetSessionStorageNamespace(
        partition_config, session_storage_namespace);
  }

  auto* new_contents_impl = new_contents.get();
  new_contents_impl->is_popup_ =
      params.disposition == WindowOpenDisposition::NEW_POPUP;

  // Sets the newly created WebContents WindowOpenDisposition.
  new_contents_impl->original_window_open_disposition_ = params.disposition;

  // If the new frame has a name, make sure any SiteInstances that can find
  // this named frame have proxies for it.  Must be called after
  // SetSessionStorageNamespace, since this calls CreateRenderView, which uses
  // GetSessionStorageNamespace.
  if (!params.frame_name.empty()) {
    new_contents_impl->GetRenderManager()->CreateProxiesForNewNamedFrame(
        new_contents_impl->GetPrimaryMainFrame()->browsing_context_state());
  }

  // Save the window for later if we're not suppressing the opener (since it
  // will be shown immediately).
  if (!params.opener_suppressed) {
    if (!is_guest) {
      WebContentsView* new_view = new_contents_impl->view_.get();

      // TODO(brettw): It seems bogus that we have to call this function on the
      // newly created object and give it one of its own member variables.
      RenderWidgetHostViewBase* widget_view = new_view->CreateViewForWidget(
          new_contents_impl->GetRenderViewHost()->GetWidget());
      view_->SetOverscrollControllerEnabled(CanOverscrollContent());
      if (!renderer_started_hidden) {
        // RenderWidgets for frames always initialize as hidden. If the renderer
        // created this window as visible, then we show it here.
        widget_view->ShowWithVisibility(PageVisibilityState::kVisible);
      }
    }
    // Save the created window associated with the route so we can show it
    // later.
    //
    // TODO(ajwong): This should be keyed off the RenderFrame routing id or the
    // FrameTreeNode id instead of the routing id of the Widget for the main
    // frame.  https://crbug.com/545684
    int32_t main_frame_routing_id = new_contents_impl->GetPrimaryMainFrame()
                                        ->GetRenderWidgetHost()
                                        ->GetRoutingID();
    GlobalRoutingID id(render_process_id, main_frame_routing_id);
    pending_contents_[id] =
        CreatedWindow(std::move(new_contents), params.target_url);
    AddWebContentsDestructionObserver(new_contents_impl);
  }

  if (delegate_) {
    delegate_->WebContentsCreated(
        this,
        GlobalRenderFrameHostId(render_process_id, opener->GetRoutingID()),
        params.frame_name, params.target_url, new_contents_impl);
  }

  observers_.NotifyObservers(&WebContentsObserver::DidOpenRequestedURL,
                             new_contents_impl, opener, params.target_url,
                             params.referrer.To<Referrer>(), params.disposition,
                             ui::PAGE_TRANSITION_LINK,
                             false,  // started_from_context_menu
                             true);  // renderer_initiated

  if (!params.opener_suppressed) {
    return &new_contents_impl->GetPrimaryFrameTree();
  }

  // When the opener is suppressed, the original renderer cannot access the
  // new window.  As a result, we need to show and navigate the window here.
  bool was_blocked = false;
  base::WeakPtr<WebContentsImpl> weak_new_contents =
      new_contents_impl->weak_factory_.GetWeakPtr();
  base::WeakPtr<WebContentsImpl> weak_this = weak_factory_.GetWeakPtr();
  WebContentsImpl* contents_to_load = new_contents_impl;
  if (delegate_) {
    WebContents* web_contents_navigated = delegate_->AddNewContents(
        this, std::move(new_contents), params.target_url, params.disposition,
        *params.features, has_user_gesture, &was_blocked);

    if (!weak_this) {
      // `this` may be deleted after AddNewContents() due to a nested message
      // loop (e.g. the window hosting the opener is closed). See
      // crbug.com/527676561.
      return nullptr;
    }

    if (base::FeatureList::IsEnabled(features::kPwaNavigationCapturing)) {
      // The delegate may delete |new_contents_impl| during AddNewContents().
      // If that occurs and there isn't a replacement contents returned, exit.
      // Otherwise, use the replacement web contents that was navigated in.
      if (web_contents_navigated != nullptr && weak_new_contents) {
        CHECK(web_contents_navigated == weak_new_contents.get());
      }

      if (!weak_new_contents) {
        if (web_contents_navigated == nullptr) {
          return nullptr;
        }
        contents_to_load =
            static_cast<WebContentsImpl*>(web_contents_navigated);
      }
    } else if (!weak_new_contents) {
      return nullptr;
    }
  }

  if (!was_blocked) {
    std::unique_ptr<NavigationController::LoadURLParams> load_params =
        std::make_unique<NavigationController::LoadURLParams>(
            params.target_url);
    load_params->initiator_origin = opener->GetLastCommittedOrigin();
    load_params->initiator_process_id = render_process_id;
    load_params->initiator_frame_token = opener->GetFrameToken();
    load_params->initiator_navigation_state =
        opener->CreateInitiatorStateFromCurrentFrame();
    // Avoiding setting |load_params->source_site_instance| when
    // |opener_suppressed| is true, because in that case we do not want to use
    // the old SiteInstance and/or BrowsingInstance.  See also the test here:
    // NewPopupCOOP_SameOriginPolicyAndCrossOriginIframeSetsNoopener.
    load_params->referrer = params.referrer.To<Referrer>();
    load_params->transition_type = ui::PAGE_TRANSITION_LINK;
    load_params->is_renderer_initiated = true;
    load_params->was_opener_suppressed = true;
    load_params->has_user_gesture = has_user_gesture;
    load_params->is_form_submission = params.is_form_submission;
    if (params.form_submission_post_data) {
      load_params->load_type = NavigationController::LOAD_TYPE_HTTP_POST;
      load_params->post_data = params.form_submission_post_data;
      load_params->post_content_type = params.form_submission_post_content_type;
    }
    load_params->override_user_agent =
        contents_to_load->should_override_user_agent_in_new_tabs_
            ? NavigationController::UA_OVERRIDE_TRUE
            : NavigationController::UA_OVERRIDE_FALSE;
    load_params->download_policy = params.download_policy;
    load_params->started_by_ad = params.started_by_ad;

    if (delegate_ && !is_guest &&
        !delegate_->ShouldResumeRequestsForCreatedWindow()) {
      // We are in asynchronous add new contents path, delay navigation.
      DCHECK(!contents_to_load->delayed_open_url_params_);
      contents_to_load->delayed_load_url_params_ = std::move(load_params);
    } else {
      contents_to_load->GetController().LoadURLWithParams(*load_params.get());
      if (!is_guest) {
        contents_to_load->Focus();
      }
    }
  }

  if (weak_new_contents) {
    return &new_contents_impl->GetPrimaryFrameTree();
  }
  return nullptr;
}

RenderWidgetHostImpl* WebContentsImpl::CreateNewPopupWidget(
    base::SafeRef<SiteInstanceGroup> site_instance_group,
    int32_t route_id,
    mojo::PendingAssociatedReceiver<blink::mojom::PopupWidgetHost>
        blink_popup_widget_host,
    mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
    mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget,
    GlobalRenderFrameHostId creator_frame_id) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::CreateNewPopupWidget",
                        "route_id", route_id);

  if (visibility_ != Visibility::VISIBLE) {
    // Don't create popups for hidden tabs. http://crbug.com/1521345
    return nullptr;
  }

  RenderWidgetHostImpl* widget_host = RenderWidgetHostFactory::CreateSelfOwned(
      &primary_frame_tree_, this, site_instance_group, route_id, IsHidden(),
      creator_frame_id);

  widget_host->BindWidgetInterfaces(std::move(blink_widget_host),
                                    std::move(blink_widget));
  widget_host->BindPopupWidgetInterface(std::move(blink_popup_widget_host));
  RenderWidgetHostViewBase* widget_view =
      static_cast<RenderWidgetHostViewBase*>(
          view_->CreateViewForChildWidget(widget_host));
  if (!widget_view) {
    return nullptr;
  }
  widget_view->SetWidgetType(WidgetType::kPopup);

  // Save the created widget associated with the route so we can show it later.
  pending_widgets_.insert(
      {GlobalRoutingID(site_instance_group->process()->GetID(), route_id),
       widget_host});
  AddRenderWidgetHostDestructionObserver(widget_host);

  return widget_host;
}

int64_t WebContentsImpl::AdjustWindowRect(gfx::Rect* bounds,
                                          RenderFrameHostImpl* opener) {
  // Auto-resize can override other mechanisms for enforcing min/max window size
  // for some modals and popups to fit the size of their contents. Unframed
  // apps shouldn't have overlap with auto-resize mode windows.
  if (!(GetRenderWidgetHostView() &&
        static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView())
            ->IsAutoResizeEnabled())) {
    // For unframed apps the minimum size is
    // `blink::kMinimumUnframedWindowSize` instead of the default
    // `blink::kMinimumWindowSize`.
    int minimum_size =
        GetDisplayMode() == blink::mojom::DisplayMode::kUnframed &&
                IsWindowManagementGranted(opener)
            ? blink::kMinimumUnframedWindowSize
            : blink::kMinimumWindowSize;
    AdjustWindowRectForMinimum(bounds, minimum_size);
  }

  int64_t display_id = display::kInvalidDisplayId;
  if (*bounds != gfx::Rect()) {
    display_id = AdjustWindowRectForDisplay(bounds, opener);
  }
  return display_id;
}

WebContents* WebContentsImpl::ShowCreatedWindow(
    RenderFrameHostImpl* opener,
    int main_frame_widget_route_id,
    WindowOpenDisposition disposition,
    const blink::mojom::WindowFeatures& window_features,
    bool user_gesture) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::ShowCreatedWindow",
                        "opener", opener, "main_frame_widget_route_id",
                        main_frame_widget_route_id);

  if (GuestPageHolderImpl::FromRenderFrameHost(*opener)) {
    // opened from a guest with MPArch, we don't need to do anything.
    return nullptr;
  }

  // This method is the renderer requesting an existing top level window to
  // show a new top level window that the renderer created. Each top level
  // window is associated with a WebContents. In this case it was created
  // earlier but showing it was deferred until the renderer requested for it
  // to be shown. We find that previously created WebContents here.
  // TODO(danakj): Why do we defer this show step until the renderer asks for it
  // when it will always do so. What needs to happen in the renderer before we
  // reach here?
  std::optional<CreatedWindow> owned_created = GetCreatedWindow(
      opener->GetProcess()->GetID(), main_frame_widget_route_id);

  // The browser may have rejected the request to make a new window, or the
  // renderer could be requesting to show a previously shown window (occurs when
  // mojom::CreateNewWindowStatus::kReuse is used). Ignore the request then.
  if (!owned_created || !owned_created->contents) {
    return nullptr;
  }

  if (active_file_chooser_) {
    // Do not allow opening a new window or tab while a file select is active
    // file chooser to avoid user confusion over which tab triggered the file
    // chooser.
    opener->AddMessageToConsole(
        blink::mojom::ConsoleMessageLevel::kWarning,
        "window.open blocked due to active file chooser.");
    return nullptr;
  }

  WebContentsImpl* created = owned_created->contents.get();

  // This uses the delegate for the WebContents where the window was created
  // from, to control how to show the newly created window.
  WebContentsDelegate* delegate = GetDelegate();

  // Individual members of |window_features.bounds| may be 0 to indicate that
  // the window.open() feature string did not specify a value. This code does
  // not distinguish between an unspecified value and 0.
  // Assume that if any single value is non-zero, all values should be used.
  // TODO(crbug.com/40092782): Utilize window_features.has_x and others.
  blink::mojom::WindowFeatures adjusted_features = window_features;
  int64_t display_id = AdjustWindowRect(&adjusted_features.bounds, opener);

  // Drop fullscreen when opening a WebContents to prohibit deceptive behavior.
  // Only drop fullscreen on the specific destination display, if it is known.
  // This supports sites using cross-screen window management capabilities to
  // retain fullscreen and open a window on another screen.
  if (!ForSecurityDropFullscreen(display_id)) {
    return nullptr;
  }

  // The delegate can be null in tests.
  if (!delegate) {
    return nullptr;
  }

  // Mark the web contents as pending resume, then immediately do the resume if
  // the delegate wants it.
  created->is_resume_pending_ = true;
  if (delegate->ShouldResumeRequestsForCreatedWindow()) {
    created->ResumeLoadingCreatedWebContents();
  }

  return delegate->AddNewContents(this, std::move(owned_created->contents),
                                  std::move(owned_created->target_url),
                                  disposition, adjusted_features, user_gesture,
                                  nullptr);
}

void WebContentsImpl::ShowCreatedWidget(ChildProcessId process_id,
                                        int widget_route_id,
                                        const gfx::Rect& initial_rect,
                                        const gfx::Rect& initial_anchor_rect) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::ShowCreatedWidget",
                        "process_id", process_id.value(), "widget_route_id",
                        widget_route_id);
  RenderWidgetHostViewBase* widget_host_view =
      static_cast<RenderWidgetHostViewBase*>(
          GetCreatedWidget(process_id, widget_route_id));
  if (!widget_host_view) {
    return;
  }

  RenderWidgetHostImpl* rwh = GetPrimaryMainFrame()->GetRenderWidgetHost();
  if (base::FeatureList::IsEnabled(
          blink::features::kBlockSelectPopupUnfocusedWindow) &&
      !rwh->is_active()) {
    // If the OS window isn't focused, then don't open select element popups for
    // it: https://issues.chromium.org/issues/365089001
    widget_host_view->host()->ShutdownAndDestroyWidget(true);
    return;
  }

  // GetOutermostWebContents() returns |this| if there are no outer WebContents.
  auto* outer_web_contents = GetOuterWebContents();
  auto* outermost_web_contents = GetOutermostWebContents();
  auto* surface_embed_connector = static_cast<SurfaceEmbedConnectorImpl*>(
      outermost_web_contents->GetSurfaceEmbedConnector());
  RenderWidgetHostView* view =
      surface_embed_connector
          ? surface_embed_connector->GetRootRenderWidgetHostView()
          : outermost_web_contents->GetRenderWidgetHostView();
  // It's not entirely obvious why we need the transform only in the case where
  // the outer webcontents is not the same as the outermost webcontents. It may
  // be due to the fact that oopifs that are children of the mainframe get
  // correct values for their screenrects, but deeper cross-process frames do
  // not. Hopefully this can be resolved with https://crbug.com/928825.
  // Handling these cases separately is needed for http://crbug.com/1015298.
  bool needs_transform = this != outermost_web_contents &&
                         outermost_web_contents != outer_web_contents;

  gfx::Rect transformed_rect(initial_rect);
  gfx::Rect transformed_anchor_rect(initial_anchor_rect);
  RenderWidgetHostView* this_view = GetRenderWidgetHostView();
  if (needs_transform) {
    // We need to transform the coordinates of initial_rect.
    gfx::Point origin =
        this_view->TransformPointToRootCoordSpace(initial_rect.origin());
    gfx::Point bottom_right =
        this_view->TransformPointToRootCoordSpace(initial_rect.bottom_right());
    transformed_rect =
        gfx::Rect(origin.x(), origin.y(), bottom_right.x() - origin.x(),
                  bottom_right.y() - origin.y());

    origin =
        this_view->TransformPointToRootCoordSpace(initial_anchor_rect.origin());
    bottom_right = this_view->TransformPointToRootCoordSpace(
        initial_anchor_rect.bottom_right());
    transformed_anchor_rect =
        gfx::Rect(origin.x(), origin.y(), bottom_right.x() - origin.x(),
                  bottom_right.y() - origin.y());
  }

  RenderWidgetHostImpl* render_widget_host_impl = widget_host_view->host();

  // A background tab cannot show a popup over the active tab.
  if (GetVisibility() != Visibility::VISIBLE) {
    render_widget_host_impl->ShutdownAndDestroyWidget(true);
    return;
  }

  auto permission_exclusion_area_bounds =
      PermissionControllerImpl::FromBrowserContext(GetBrowserContext())
          ->GetExclusionAreaBoundsInScreen(outermost_web_contents);
  if (permission_exclusion_area_bounds &&
      permission_exclusion_area_bounds->Intersects(transformed_rect)) {
    render_widget_host_impl->ShutdownAndDestroyWidget(true);
    return;
  }

  widget_host_view->InitAsPopup(view, transformed_rect,
                                transformed_anchor_rect);

  // Renderer-owned popup widgets wait for the renderer to request for them
  // to be shown. We signal that this condition is satisfied by calling Init().
  render_widget_host_impl->Init();
}

std::optional<CreatedWindow> WebContentsImpl::GetCreatedWindow(
    ChildProcessId process_id,
    int main_frame_widget_route_id) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::GetCreatedWindow",
                        "process_id", process_id.value(),
                        "main_frame_widget_route_id",
                        main_frame_widget_route_id);

  auto key = GlobalRoutingID(process_id, main_frame_widget_route_id);
  auto iter = pending_contents_.find(key);

  // Certain systems can block the creation of new windows. If we didn't succeed
  // in creating one, just return NULL.
  if (iter == pending_contents_.end()) {
    return std::nullopt;
  }

  CreatedWindow result = std::move(iter->second);
  WebContentsImpl* new_contents = result.contents.get();
  pending_contents_.erase(key);
  RemoveWebContentsDestructionObserver(new_contents);

  // Don't initialize the guest WebContents immediately.
  if (new_contents->IsGuest()) {
    return result;
  }

  if (!new_contents->GetPrimaryMainFrame()
           ->GetProcess()
           ->IsInitializedAndNotDead() ||
      !new_contents->GetPrimaryMainFrame()->GetView()) {
    return std::nullopt;
  }

  return result;
}

RenderWidgetHostView* WebContentsImpl::GetCreatedWidget(
    ChildProcessId process_id,
    int route_id) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::GetCreatedWidget",
                        "process_id", process_id.value(), "route_id", route_id);

  auto iter = pending_widgets_.find(GlobalRoutingID(process_id, route_id));
  if (iter == pending_widgets_.end()) {
    DCHECK(false);
    return nullptr;
  }

  RenderWidgetHost* widget_host = iter->second;
  pending_widgets_.erase(GlobalRoutingID(process_id, route_id));
  RemoveRenderWidgetHostDestructionObserver(widget_host);

  if (!widget_host->GetProcess()->IsInitializedAndNotDead()) {
    // The view has gone away or the renderer crashed. Nothing to do.
    return nullptr;
  }

  return widget_host->GetView();
}

void WebContentsImpl::CreateMediaPlayerHostForRenderFrameHost(
    RenderFrameHostImpl* frame_host,
    mojo::PendingAssociatedReceiver<media::mojom::MediaPlayerHost> receiver) {
  media_web_contents_observer()->BindMediaPlayerHost(frame_host->GetGlobalId(),
                                                     std::move(receiver));
}

void WebContentsImpl::RequestMediaAccessPermission(
    RenderFrameHostImpl* render_frame_host,
    const MediaStreamRequest& request,
    MediaResponseCallback callback) {
  OPTIONAL_TRACE_EVENT2("content",
                        "WebContentsImpl::RequestMediaAccessPermission",
                        "render_process_id", request.render_process_id,
                        "render_frame_id", request.render_frame_id);

  if (GuestPageHolderImpl* guest =
          render_frame_host
              ? GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)
              : nullptr) {
    if (auto* delegate = guest->delegate()) {
      delegate->GuestRequestMediaAccessPermission(request, std::move(callback));
      return;
    }
    std::move(callback).Run(
        blink::mojom::StreamDevicesSet(),
        blink::mojom::MediaStreamRequestResult::
            FAILED_DUE_TO_SHUTDOWN_NO_GUEST_PAGE_HOLDER_DELEGATE,
        std::unique_ptr<MediaStreamUI>());
    return;
  }

  if (delegate_) {
    delegate_->RequestMediaAccessPermission(this, request, std::move(callback));
    return;
  }
  std::move(callback).Run(blink::mojom::StreamDevicesSet(),
                          blink::mojom::MediaStreamRequestResult::
                              FAILED_DUE_TO_SHUTDOWN_WEB_CONTENTS_NO_DELEGATE,
                          std::unique_ptr<MediaStreamUI>());
}

void WebContentsImpl::ProcessSelectAudioOutput(
    const SelectAudioOutputRequest& request,
    SelectAudioOutputCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (delegate_) {
    delegate_->ProcessSelectAudioOutput(request, std::move(callback));
  } else {
    std::move(callback).Run(
        base::unexpected(content::SelectAudioOutputError::kNotSupported));
  }
}

bool WebContentsImpl::CheckMediaAccessPermission(
    RenderFrameHostImpl* render_frame_host,
    const url::Origin& security_origin,
    blink::mojom::MediaStreamType type) {
  OPTIONAL_TRACE_EVENT2("content",
                        "WebContentsImpl::CheckMediaAccessPermission",
                        "render_frame_host", render_frame_host,
                        "security_origin", security_origin);

  DCHECK(type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
         type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);

  if (auto* guest =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    if (auto* delegate = guest->delegate()) {
      return delegate->GuestCheckMediaAccessPermission(render_frame_host,
                                                       security_origin, type);
    }
    return false;
  }
  return delegate_ && delegate_->CheckMediaAccessPermission(
                          render_frame_host, security_origin, type);
}

void WebContentsImpl::OnCaptureHandleConfigUpdate(Page& page) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  // Only broadcast to tab-level observers if the update comes from the primary
  // page.
  if (!page.IsPrimary()) {
    return;
  }

  const auto& config = page.GetCaptureHandleConfig();
  const url::Origin& origin = page.GetMainDocument().GetLastCommittedOrigin();
  if (config != last_notified_capture_handle_config_ ||
      (origin != last_notified_capture_handle_origin_ &&
       config.expose_origin)) {
    last_notified_capture_handle_config_ = config;
    last_notified_capture_handle_origin_ = origin;
    observers_.NotifyObservers(
        &WebContentsObserver::OnCaptureHandleConfigUpdate, config);
  }
}

bool WebContentsImpl::IsJavaScriptDialogShowing() const {
  return is_showing_javascript_dialog_;
}

bool WebContentsImpl::ShouldIgnoreUnresponsiveRenderer() {
  // Suppress unresponsive renderers if the command line asks for it.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          input::switches::kDisableHangMonitor) ||
      !base::FeatureList::IsEnabled(input::features::kRendererHangWatcher)) {
    return true;
  }

  if (IsBeingDestroyed()) {
    return true;
  }

  if (suppress_unresponsive_renderer_count_ > 0) {
    return true;
  }

  // Ignore unresponsive renderers if the debugger is attached to them since the
  // unresponsiveness might be a result of the renderer sitting on a breakpoint.
  //
#if BUILDFLAG(IS_WIN)
  // Check if a windows debugger is attached to the renderer process.
  base::ProcessHandle process_handle =
      GetPrimaryMainFrame()->GetProcess()->GetProcess().Handle();
  BOOL debugger_present = FALSE;
  if (CheckRemoteDebuggerPresent(process_handle, &debugger_present) &&
      debugger_present) {
    return true;
  }
#endif  // BUILDFLAG(IS_WIN)

  // TODO(pfeldman): Fix this to only return true if the renderer is *actually*
  // sitting on a breakpoint. https://crbug.com/684202
  return DevToolsAgentHost::IsDebuggerAttached(this);
}

ui::AXMode WebContentsImpl::GetAccessibilityMode() {
  return accessibility_mode_;
}

void WebContentsImpl::NotifyAccessibilityParentChanged() {
  GetPrimaryMainFrame()->UpdateAXTreeData();
}

void WebContentsImpl::AXTreeIDForMainFrameHasChanged() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::AXTreeIDForMainFrameHasChanged");

  RenderWidgetHostViewBase* rwhv =
      static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView());
  if (rwhv) {
    rwhv->SetMainFrameAXTreeID(GetPrimaryMainFrame()->GetAXTreeID());
  }

  observers_.NotifyObservers(
      &WebContentsObserver::AXTreeIDForMainFrameHasChanged);
}

void WebContentsImpl::ProcessAccessibilityUpdatesAndEvents(
    ui::AXUpdatesAndEvents& details) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::AccessibilityEventReceived");

  // First, supply the data to consumers that won't change it.
  observers_.NotifyObservers(&WebContentsObserver::AccessibilityEventReceived,
                             details);

  // Next, supply the data to consumers that may change it or who need to avoid
  // extra copying. Note that this also includes those who will pass to mojo
  // pipes not taking const.
  // TODO(accessibility): add support for this. WebContentsDelegate isn't the
  // right abstraction since it contains many other side effects.
}

void WebContentsImpl::AccessibilityLocationChangesReceived(
    const ui::AXTreeID& tree_id,
    ui::AXLocationAndScrollUpdates& details) {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::AccessibilityLocationChangesReceived");
  observers_.NotifyObservers(
      &WebContentsObserver::AccessibilityLocationChangesReceived, tree_id,
      details);
}

ui::AXNode* WebContentsImpl::GetAccessibilityRootNode() {
  ui::BrowserAccessibilityManager* manager =
      GetRootBrowserAccessibilityManager();
  if (!manager || !manager->ax_tree()) {
    return nullptr;
  }
  return manager->ax_tree()->root();
}

std::string WebContentsImpl::DumpAccessibilityTree(
    bool internal,
    std::vector<ui::AXPropertyFilter> property_filters) {
  ui::AXApiType::Type api_type =
      internal ? ui::AXApiType::Type(ui::AXApiType::kBlink)
               : AXInspectFactory::DefaultPlatformFormatterType();
  return DumpAccessibilityTree(api_type, property_filters);
}

std::string WebContentsImpl::DumpAccessibilityTree(
    ui::AXApiType::Type api_type,
    std::vector<ui::AXPropertyFilter> property_filters) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DumpAccessibilityTree");
  auto* ax_mgr = GetOrCreateRootBrowserAccessibilityManager();
  // Since for Web Content we get the AXTree updates through the renderer at a
  // point after the manager is created, there are cases where at this point in
  // the lifecycle the AXTree associated with `ax_mgr` does not have a valid
  // tree ID. As such, if this is the case we return an empty string early. If
  // we don't have this check, there will be a scenario where we then try to get
  // the manager using the ID (which at this point is invalid) which leads to a
  // crash. See https://crbug.com/1405036.
  if (!ax_mgr || !ax_mgr->HasValidTreeID()) {
    return "-";
  }

  // Developer mode: crash immediately on any accessibility fatal error.
  // This only runs during integration tests, or if a developer is
  // using an inspection tool, e.g. chrome://accessibility.
  ui::AXTreeManager::AlwaysFailFast();
  DCHECK(std::ranges::contains(AXInspectFactory::SupportedApis(), api_type));
  std::unique_ptr<ui::AXTreeFormatter> formatter =
      AXInspectFactory::CreateFormatter(api_type);

  formatter->SetPropertyFilters(property_filters);
  return formatter->Format(ax_mgr->GetBrowserAccessibilityRoot());
}

void WebContentsImpl::RecordAccessibilityEvents(
    bool start_recording,
    std::optional<ui::AXEventCallback> callback) {
  RecordAccessibilityEvents(AXInspectFactory::DefaultPlatformRecorderType(),
                            start_recording, callback);
}
void WebContentsImpl::RecordAccessibilityEvents(
    ui::AXApiType::Type api_type,
    bool start_recording,
    std::optional<ui::AXEventCallback> callback) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::RecordAccessibilityEvents");

  // Only pass a callback to RecordAccessibilityEvents when starting to record.
  DCHECK_EQ(start_recording, callback.has_value());
  if (start_recording) {
    // TODO(grt): Do we need to do the same for all inner WebContentses?.
    recording_mode_ =
        BrowserAccessibilityState::GetInstance()
            ->CreateScopedModeForWebContents(this, ui::kAXModeBasic);
    if (accessibility_mode_.has_mode(ui::AXMode::kNativeAPIs)) {
      StartRecordingAccessibilityEvents(api_type, *std::move(callback));
    } else {
      // The WebContents must be hidden. Defer starting the recording until
      // accessibility is enabled once it is shown.
      pending_recording_ = PendingRecording{.api_type = api_type,
                                            .callback = *std::move(callback)};
    }
  } else {
    pending_recording_.reset();
    if (event_recorder_) {
      event_recorder_->WaitForDoneRecording();
      event_recorder_.reset(nullptr);
    }
    recording_mode_.reset();
  }
}

void WebContentsImpl::StartRecordingAccessibilityEvents(
    ui::AXApiType::Type api_type,
    ui::AXEventCallback callback) {
  auto* ax_mgr = GetOrCreateRootBrowserAccessibilityManager();
  CHECK(ax_mgr);
  base::ProcessId pid = base::Process::Current().Pid();
  gfx::AcceleratedWidget widget = ax_mgr->GetBrowserAccessibilityRoot()
                                      ->GetTargetForNativeAccessibilityEvent();

  DCHECK(std::ranges::contains(AXInspectFactory::SupportedApis(), api_type));
  event_recorder_ = content::AXInspectFactory::CreateRecorder(
      api_type, ax_mgr, pid, ui::AXTreeSelector(widget));
  event_recorder_->ListenToEvents(std::move(callback));
}

void WebContentsImpl::UnrecoverableAccessibilityError() {
  SetAccessibilityMode(ui::AXMode::kNone);
  unrecoverable_accessibility_error_ = true;
}

device::mojom::GeolocationContext* WebContentsImpl::GetGeolocationContext() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::GetGeolocationContext");
  if (delegate_) {
    auto* installed_webapp_context =
        delegate_->GetInstalledWebappGeolocationContext();
    if (installed_webapp_context) {
      return installed_webapp_context;
    }
  }

  if (!geolocation_context_) {
    GetDeviceService().BindGeolocationContext(
        geolocation_context_.BindNewPipeAndPassReceiver());
  }
  return geolocation_context_.get();
}

device::mojom::WakeLockContext* WebContentsImpl::GetWakeLockContext() {
  if (!enable_wake_locks_) {
    return nullptr;
  }
  if (!wake_lock_context_host_) {
    wake_lock_context_host_ = std::make_unique<WakeLockContextHost>(this);
  }
  return wake_lock_context_host_->GetWakeLockContext();
}

#if BUILDFLAG(IS_ANDROID) || (BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_IOS_TVOS))
void WebContentsImpl::GetNFC(
    RenderFrameHostImpl* render_frame_host,
    mojo::PendingReceiver<device::mojom::NFC> receiver) {
  if (!nfc_host_) {
    nfc_host_ = std::make_unique<NFCHost>(this);
  }
  nfc_host_->GetNFC(render_frame_host, std::move(receiver));
}
#endif

void WebContentsImpl::SendScreenRects() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SendScreenRects");

  DCHECK(!IsBeingDestroyed());

  GetPrimaryMainFrame()->ForEachRenderFrameHostImpl(
      [](RenderFrameHostImpl* render_frame_host) {
        if (render_frame_host->is_local_root()) {
          render_frame_host->GetRenderWidgetHost()->SendScreenRects();
        }
      });
}

void WebContentsImpl::SendActiveState(bool active) {
  DCHECK(!IsBeingDestroyed());

  // Replicate the active state to all LocalRoots.
  GetPrimaryMainFrame()->ForEachRenderFrameHostImpl(
      [active](RenderFrameHostImpl* render_frame_host) {
        if (render_frame_host->is_local_root()) {
          render_frame_host->GetRenderWidgetHost()->SetActive(active);
        }
      });
}

TextInputManager* WebContentsImpl::GetTextInputManager() {
  if (suppress_ime_events_for_testing_) {
    return nullptr;
  }

  if (GetOuterWebContents()) {
    return GetOuterWebContents()->GetTextInputManager();
  }

  if (surface_embed_connector_) {
    return surface_embed_connector_->GetTextInputManager();
  }

  if (!text_input_manager_ && !browser_plugin_guest_) {
    text_input_manager_ = std::make_unique<TextInputManager>();
  }

  return text_input_manager_.get();
}

bool WebContentsImpl::IsWidgetForPrimaryMainFrame(
    RenderWidgetHostImpl* render_widget_host) {
  return render_widget_host == GetPrimaryMainFrame()->GetRenderWidgetHost();
}

ui::BrowserAccessibilityManager*
WebContentsImpl::GetRootBrowserAccessibilityManager() {
  RenderFrameHostImpl* rfh =
      static_cast<RenderFrameHostImpl*>(GetPrimaryMainFrame());
  return rfh ? rfh->browser_accessibility_manager() : nullptr;
}

ui::BrowserAccessibilityManager*
WebContentsImpl::GetOrCreateRootBrowserAccessibilityManager() {
  RenderFrameHostImpl* rfh =
      static_cast<RenderFrameHostImpl*>(GetPrimaryMainFrame());
  return rfh ? rfh->GetOrCreateBrowserAccessibilityManager() : nullptr;
}

void WebContentsImpl::ExecuteEditCommand(
    const std::string& command,
    const std::optional<std::u16string>& value) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ExecuteEditCommand");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  input_handler->ExecuteEditCommand(command, value);
}

void WebContentsImpl::MoveRangeSelectionExtent(const gfx::Point& extent) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MoveRangeSelectionExtent");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  input_handler->MoveRangeSelectionExtent(extent);
}

void WebContentsImpl::SelectRange(const gfx::Point& base,
                                  const gfx::Point& extent) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SelectRange");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  input_handler->SelectRange(base, extent);
}

void WebContentsImpl::SelectAroundCaret(
    blink::mojom::SelectionGranularity granularity,
    bool should_show_handle,
    bool should_show_context_menu) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SelectAroundCaret");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->SelectAroundCaret(granularity, should_show_handle,
                                   should_show_context_menu, base::DoNothing());
}

void WebContentsImpl::MoveCaret(const gfx::Point& extent) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::MoveCaret");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  input_handler->MoveCaret(extent);
}

base::UnguessableToken WebContentsImpl::GetCompositorFrameSinkGroupingId()
    const {
  const WebContentsImpl* root =
      const_cast<WebContentsImpl*>(this)->GetOutermostWebContents();
  return root->compositor_frame_sink_grouping_id_;
}

void WebContentsImpl::AdjustSelectionByCharacterOffset(
    int start_adjust,
    int end_adjust,
    bool show_selection_menu) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::AdjustSelectionByCharacterOffset");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  using blink::mojom::SelectionMenuBehavior;
  input_handler->AdjustSelectionByCharacterOffset(
      start_adjust, end_adjust,
      show_selection_menu ? SelectionMenuBehavior::kShow
                          : SelectionMenuBehavior::kHide);
}

const std::optional<gfx::Rect> WebContentsImpl::GetTextSelectionBounds(
    RenderFrameHost* render_frame_host) const {
  if (text_input_manager_ && render_frame_host) {
    auto* view =
        static_cast<RenderWidgetHostViewBase*>(render_frame_host->GetView());
    auto* root_view = static_cast<RenderWidgetHostViewBase*>(
        render_frame_host->GetOutermostMainFrame()->GetView());
    if (view && root_view) {
      const auto* region = text_input_manager_->GetSelectionRegion(view);
      if (region) {
        gfx::Rect bounds = region->bounding_box;
        if (!bounds.IsEmpty()) {
          gfx::Point origin = bounds.origin();
          origin += root_view->GetViewBounds().OffsetFromOrigin();
          return gfx::Rect(origin, bounds.size());
        }
      }
    }
  }
  return std::nullopt;
}

const std::optional<gfx::Rect> WebContentsImpl::GetFocusSelectionBounds(
    RenderFrameHost* render_frame_host) const {
  if (text_input_manager_ && render_frame_host) {
    auto* view =
        static_cast<RenderWidgetHostViewBase*>(render_frame_host->GetView());
    RenderWidgetHostViewBase* root_view = view->GetRootView();
    if (view && root_view) {
      const auto* region = text_input_manager_->GetSelectionRegion(view);
      if (region && region->focus.type() != gfx::SelectionBound::EMPTY) {
        gfx::Point start = region->focus.edge_start_rounded();
        gfx::Point end = region->focus.edge_end_rounded();
        gfx::Rect bounds = gfx::BoundingRect(start, end);
        gfx::Point origin = bounds.origin();
        origin += root_view->GetViewBounds().OffsetFromOrigin();
        return gfx::Rect(origin, bounds.size());
      }
    }
  }
  return std::nullopt;
}

base::CallbackListSubscription
WebContentsImpl::RegisterFocusSelectionBoundsChanged(
    FocusSelectionBoundsChangedCallback callback) {
  if (!text_input_manager_) {
    // Although we could use `GetTextInputManager()`, we'd also need a lookup
    // that only returns any existing manager during destruction, and current
    // callers don't need this.
    NOTIMPLEMENTED();
    return {};
  }
  if (!text_input_manager_->HasObserver(this)) {
    text_input_manager_->AddObserver(this);
  }
  focus_selection_bounds_changed_callback_list_.set_removal_callback(
      base::BindRepeating(
          &WebContentsImpl::OnFocusSelectionBoundsChangedSubscriptionRemoved,
          base::Unretained(this)));

  return focus_selection_bounds_changed_callback_list_.Add(std::move(callback));
}

void WebContentsImpl::OnFocusSelectionBoundsChangedSubscriptionRemoved() {
  if (focus_selection_bounds_changed_callback_list_.empty() &&
      text_input_manager_ && text_input_manager_->HasObserver(this)) {
    text_input_manager_->RemoveObserver(this);
  }
}

void WebContentsImpl::OnSelectionBoundsChanged(
    TextInputManager* text_input_manager,
    RenderWidgetHostViewBase* updated_view) {
  focus_selection_bounds_changed_callback_list_.Notify(updated_view);
}

void WebContentsImpl::ResizeDueToAutoResize(
    RenderWidgetHostImpl* render_widget_host,
    const gfx::Size& new_size) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ResizeDueToAutoResize",
                        "render_widget_host", render_widget_host);

  // If we are a guest frame tree and the RenderWidgetHost is the same as the
  // guest's main frame widget, then send the guest a notification about the
  // resize.
  if (render_widget_host->frame_tree()->is_guest() &&
      render_widget_host->frame_tree()->GetMainFrame()->GetRenderWidgetHost() ==
          render_widget_host) {
    if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
            *render_widget_host->frame_tree()->GetMainFrame())) {
      guest->delegate()->GuestResizeDueToAutoResize(new_size);
    }
    return;
  }

  if (render_widget_host != GetRenderViewHost()->GetWidget()) {
    return;
  }

  if (delegate_) {
    delegate_->ResizeDueToAutoResize(this, new_size);
  }
}

WebContents* WebContentsImpl::OpenURL(
    const OpenURLParams& params,
    base::OnceCallback<void(content::NavigationHandle&)>
        navigation_handle_callback) {
  TRACE_EVENT1("content", "WebContentsImpl::OpenURL", "url", params.url);
#if DCHECK_IS_ON()
  DCHECK(params.Valid());
#endif

  if (!delegate_) {
    // Embedder can delay setting a delegate on new WebContents with
    // WebContentsDelegate::ShouldResumeRequestsForCreatedWindow. In the mean
    // time, navigations, including the initial one, that goes through OpenURL
    // should be delayed until embedder is ready to resume loading.
    delayed_open_url_params_ = std::make_unique<OpenURLParams>(params);
    delayed_navigation_handle_callback_ = std::move(navigation_handle_callback);

    // If there was a navigation deferred when creating the window through
    // CreateNewWindow, drop it in favor of this navigation.
    delayed_load_url_params_.reset();

    return nullptr;
  }

  RenderFrameHostImpl* source_render_frame_host =
      RenderFrameHostImpl::FromID(GlobalRenderFrameHostId(
          params.source_render_process_id, params.source_render_frame_id));

  // Prevent frames that are not active (e.g. a prerendering page) from opening
  // new windows, tabs, popups, etc.
  if (params.disposition != WindowOpenDisposition::CURRENT_TAB &&
      source_render_frame_host && !source_render_frame_host->IsActive()) {
    return nullptr;
  }

  if (params.frame_tree_node_id) {
    if (auto* frame_tree_node =
            FrameTreeNode::GloballyFindByID(params.frame_tree_node_id)) {
      // If a frame tree node ID is specified and it exists, ensure it is for a
      // node within this WebContents. Note: this WebContents could be hosting
      // multiple frame trees (e.g. prerendering) so it's not enough to check
      // against this->primary_frame_tree_. Check against page_delegate(), which
      // is always a WebContentsImpl, while delegate() may be implemented by
      // something else such as for prerendered frame trees.
      FrameTree& frame_tree = frame_tree_node->frame_tree();
      CHECK_EQ(frame_tree.page_delegate(), this);

      // Prerendering and fenced frame navigations are hidden from embedders.
      // If the navigation is targeting a frame in a prerendering or fenced
      // frame tree, we shouldn't run that navigation through the embedder
      // delegate. Embedder implementations of
      // `WebContentsDelegate::OpenURLFromTab` assume that the primary
      // frame tree Navigation controller should be used for navigating.
      // Instead, we just navigate directly on the relevant frame
      // tree.
      if (frame_tree.is_prerendering() ||
          frame_tree_node->IsInFencedFrameTree()) {
        DCHECK_EQ(params.disposition, WindowOpenDisposition::CURRENT_TAB);
        frame_tree.controller().LoadURLWithParams(
            NavigationController::LoadURLParams(params));
        return this;
      }
    } else {
      // If the node doesn't exist it was probably removed from its frame tree.
      // In that case, abort since continuing would navigate the root frame.
      return nullptr;
    }
  }

  if (source_render_frame_host &&
      base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
    if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
            *source_render_frame_host)) {
      if (auto* delegate = guest->delegate()) {
        delegate->GuestOpenURL(params, std::move(navigation_handle_callback));
      }
      return nullptr;
    }
  }

  WebContents* new_contents = delegate_->OpenURLFromTab(
      this, params, std::move(navigation_handle_callback));

  if (source_render_frame_host && params.source_site_instance) {
    CHECK_EQ(source_render_frame_host->GetSiteInstance(),
             params.source_site_instance.get());
  }

  if (new_contents && source_render_frame_host && new_contents != this) {
    observers_.NotifyObservers(
        &WebContentsObserver::DidOpenRequestedURL, new_contents,
        source_render_frame_host, params.url, params.referrer,
        params.disposition, params.transition, params.started_from_context_menu,
        params.is_renderer_initiated);
  }

  return new_contents;
}

void WebContentsImpl::SetHistoryIndexAndLengthForView(
    RenderViewHost* render_view_host,
    int history_index,
    int history_length) {
  OPTIONAL_TRACE_EVENT2(
      "content", "WebContentsImpl::SetHistoryIndexAndLengthForView",
      "history_index", history_index, "history_length", history_length);
  if (auto& broadcast = static_cast<RenderViewHostImpl*>(render_view_host)
                            ->GetAssociatedPageBroadcast()) {
    broadcast->SetHistoryIndexAndLength(history_index, history_length);
  }
}

void WebContentsImpl::ReloadFocusedFrame() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ReloadFocusedFrame");
  RenderFrameHost* focused_frame = GetFocusedFrame();
  if (!focused_frame) {
    return;
  }

  focused_frame->Reload();
}

void WebContentsImpl::Undo() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Undo");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Undo();
  RecordAction(base::UserMetricsAction("Undo"));
}

void WebContentsImpl::Redo() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Redo");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Redo();
  RecordAction(base::UserMetricsAction("Redo"));
}

void WebContentsImpl::Cut() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Cut");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Cut();
  RecordAction(base::UserMetricsAction("Cut"));
}

void WebContentsImpl::Copy() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Copy");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Copy();
  RecordAction(base::UserMetricsAction("Copy"));
}

void WebContentsImpl::CopyToFindPboard() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::CopyToFindPboard");
#if BUILDFLAG(IS_MAC)
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  // Windows/Linux don't have the concept of a find pasteboard.
  input_handler->CopyToFindPboard();
  RecordAction(base::UserMetricsAction("CopyToFindPboard"));
#endif
}

void WebContentsImpl::CenterSelection() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::CenterSelection");
#if BUILDFLAG(IS_MAC)
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->CenterSelection();
#endif
}

void WebContentsImpl::Paste() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Paste");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Paste();
  observers_.NotifyObservers(&WebContentsObserver::OnPaste);
  RecordAction(base::UserMetricsAction("Paste"));
}

void WebContentsImpl::PasteAndMatchStyle() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::PasteAndMatchStyle");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->PasteAndMatchStyle();
  observers_.NotifyObservers(&WebContentsObserver::OnPaste);
  RecordAction(base::UserMetricsAction("PasteAndMatchStyle"));
}

void WebContentsImpl::Delete() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Delete");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Delete();
  RecordAction(base::UserMetricsAction("DeleteSelection"));
}

void WebContentsImpl::SelectAll() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SelectAll");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->SelectAll();
  RecordAction(base::UserMetricsAction("SelectAll"));
}

void WebContentsImpl::CollapseSelection() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::CollapseSelection");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->CollapseSelection();
}

void WebContentsImpl::ScrollToTopOfDocument() {
  ExecuteEditCommand("ScrollToBeginningOfDocument", std::nullopt);
}

void WebContentsImpl::ScrollToBottomOfDocument() {
  ExecuteEditCommand("ScrollToEndOfDocument", std::nullopt);
}

void WebContentsImpl::Replace(const std::u16string& word) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Replace");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->Replace(word);
}

void WebContentsImpl::ReplaceMisspelling(const std::u16string& word) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ReplaceMisspelling");
  auto* input_handler = GetFocusedFrameWidgetInputHandler();
  if (!input_handler) {
    return;
  }

  last_interaction_time_ = ui::EventTimeForNow();
  input_handler->ReplaceMisspelling(word);
}

void WebContentsImpl::NotifyContextMenuClosed(const GURL& link_followed) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::NotifyContextMenuClosed");
  RenderFrameHost* focused_frame = GetFocusedFrame();
  if (!focused_frame) {
    return;
  }

  if (context_menu_client_) {
    context_menu_client_->ContextMenuClosed(link_followed);
  }

  context_menu_client_.reset();
}

void WebContentsImpl::ExecuteCustomContextMenuCommand(
    int action,
    const GURL& link_followed) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::ExecuteCustomContextMenuCommand",
                        "action", action);
  RenderFrameHost* focused_frame = GetFocusedFrame();
  if (!focused_frame) {
    return;
  }

  if (context_menu_client_) {
    context_menu_client_->CustomContextMenuAction(action);
  }
}

void WebContentsImpl::SetDragSource(
    const DragId& drag_id,
    const GlobalRenderFrameHostToken& source_rfh_token) {
  DragSourceMap::GetOrCreate(GetBrowserContext())
      ->SetDragSource(drag_id, source_rfh_token);
}

gfx::NativeView WebContentsImpl::GetNativeView() {
  return view_->GetNativeView();
}

gfx::NativeView WebContentsImpl::GetContentNativeView() {
  return view_->GetContentNativeView();
}

gfx::NativeWindow WebContentsImpl::GetTopLevelNativeWindow() {
  return view_->GetTopLevelNativeWindow();
}

gfx::Rect WebContentsImpl::GetViewBounds() {
  return view_->GetViewBounds();
}

gfx::Rect WebContentsImpl::GetContainerBounds() {
  return view_->GetContainerBounds();
}

DropData* WebContentsImpl::GetDropData() {
  return view_->GetDropData();
}

void WebContentsImpl::Focus() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Focus");
  view_->Focus();
}

void WebContentsImpl::SetInitialFocus() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetInitialFocus");
  view_->SetInitialFocus();
}

void WebContentsImpl::StoreFocus() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::StoreFocus");
  view_->StoreFocus();
}

void WebContentsImpl::RestoreFocus() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::RestoreFocus");
  view_->RestoreFocus();
}

void WebContentsImpl::FocusThroughTabTraversal(bool reverse) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::FocusThroughTabTraversal",
                        "reverse", reverse);
  view_->FocusThroughTabTraversal(reverse);
}

bool WebContentsImpl::IsSavable() {
  // WebKit creates Document object when MIME type is application/xhtml+xml,
  // so we also support this MIME type.
  std::string mime_type = GetContentsMimeType();
  return mime_type == "text/html" || mime_type == "text/xml" ||
         mime_type == "application/xhtml+xml" || mime_type == "text/plain" ||
         mime_type == "text/css" ||
         blink::IsSupportedJavascriptMimeType(mime_type);
}

void WebContentsImpl::OnSavePage() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnSavePage");
  // If we can not save the page, try to download it.
  if (!IsSavable()) {
    SaveFrame(GetLastCommittedURL(), Referrer(), GetPrimaryMainFrame());
    return;
  }

  Stop();

  // Create the save package and possibly prompt the user for the name to save
  // the page as. The user prompt is an asynchronous operation that runs on
  // another thread.
  save_package_ = new SavePackage(GetPrimaryPage());
  save_package_->GetSaveInfo();
}

// Used in automated testing to bypass prompting the user for file names.
// Instead, the names and paths are hard coded rather than running them through
// file name sanitation and extension / mime checking.
bool WebContentsImpl::SavePage(const base::FilePath& main_file,
                               const base::FilePath& dir_path,
                               SavePageType save_type) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SavePage", "main_file",
                        main_file, "dir_path", dir_path);
  // Stop the page from navigating.
  Stop();

  save_package_ =
      new SavePackage(GetPrimaryPage(), save_type, main_file, dir_path);
  return save_package_->Init(SavePackageDownloadCreatedCallback());
}

void WebContentsImpl::SaveFrame(const GURL& url,
                                const Referrer& referrer,
                                RenderFrameHost* rfh) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SaveFrame");
  SaveFrameWithHeaders(url, referrer, std::string(), std::u16string(), rfh,
                       /*is_subresource=*/false);
}

void WebContentsImpl::SaveFrameWithHeaders(
    const GURL& url,
    const Referrer& referrer,
    const std::string& headers,
    const std::u16string& suggested_filename,
    RenderFrameHost* rfh,
    bool is_subresource) {
  DCHECK(rfh);
  auto& rfhi = *static_cast<RenderFrameHostImpl*>(rfh);

  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SaveFrameWithHeaders",
                        "url", url, "headers", headers);
  // Check and see if the guest can handle this.
  if (delegate_) {
    WebContents* guest_web_contents = nullptr;
    if (browser_plugin_embedder_) {
      BrowserPluginGuest* guest = browser_plugin_embedder_->GetFullPageGuest();
      if (guest) {
        guest_web_contents = guest->GetWebContents();
      }
    } else if (browser_plugin_guest_) {
      guest_web_contents = this;
    }

    if (guest_web_contents && delegate_->GuestSaveFrame(guest_web_contents)) {
      return;
    }
  }

  if (!GetLastCommittedURL().is_valid()) {
    return;
  }
  if (delegate_ && delegate_->SaveFrame(url, referrer, rfh)) {
    return;
  }

  int64_t post_id = -1;
  if (rfhi.is_main_frame() && !is_subresource) {
    NavigationEntry* entry =
        rfhi.frame_tree()->controller().GetLastCommittedEntry();
    if (entry) {
      post_id = entry->GetPostID();
    }
  }
  net::NetworkTrafficAnnotationTag traffic_annotation =
      net::DefineNetworkTrafficAnnotation("download_web_contents_frame", R"(
        semantics {
          sender: "Save Page Action"
          description:
            "Saves the given frame's URL to the local file system."
          trigger:
            "The user has triggered a save operation on the frame through a "
            "context menu or other mechanism."
          data: "None."
          destination: WEBSITE
        }
        policy {
          cookies_allowed: YES
          cookies_store: "user"
          setting:
            "This feature cannot be disabled by settings, but it's is only "
            "triggered by user request."
          policy_exception_justification: "Not implemented."
        })");
  auto params = rfh->CreateDownloadUrlParameters(url, traffic_annotation);
  params->set_referrer(referrer.url);
  params->set_referrer_policy(
      Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
  params->set_post_id(post_id);
  if (post_id >= 0) {
    params->set_method("POST");
  }
  params->set_prompt(true);

  if (!headers.empty()) {
    for (download::DownloadUrlParameters::RequestHeadersNameValuePair
             key_value : ParseDownloadHeaders(headers)) {
      params->add_request_header(key_value.first, key_value.second);
    }
  }
  params->set_prefer_cache(true);
  params->set_suggested_name(suggested_filename);
  params->set_download_source(download::DownloadSource::WEB_CONTENTS_API);
  params->set_isolation_info(rfhi.ComputeIsolationInfoForNavigation(url));

  // TODO(crbug.com/382291442): Remove feature guarding once launched.
  if (base::FeatureList::IsEnabled(
          network::features::kPopulatePermissionsPolicyOnRequest)) {
    params->set_permissions_policy(rfhi.GetPermissionsPolicy());
  }

  FrameTreeNode* frame_tree_node = rfhi.frame_tree_node();
  FrameNavigationEntry* frame_navigation_entry =
      frame_tree_node->frame_tree()
          .controller()
          .GetLastCommittedEntry()
          ->GetFrameEntry(frame_tree_node);
  if (frame_navigation_entry) {
    // Replay the original initiator, rather than using the current frame origin
    params->set_initiator(frame_navigation_entry->initiator_origin());
  }

  GetBrowserContext()->GetDownloadManager()->DownloadUrl(std::move(params));
}

void WebContentsImpl::GenerateMHTML(
    const MHTMLGenerationParams& params,
    base::OnceCallback<void(int64_t)> callback) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::GenerateMHTML");
  MHTMLGenerationManager::GetInstance()->SaveMHTML(this, params,
                                                   std::move(callback));
}

const std::string& WebContentsImpl::GetContentsMimeType() {
  return GetPrimaryPage().GetContentsMimeType();
}

blink::RendererPreferences* WebContentsImpl::GetMutableRendererPrefs() {
  return &renderer_preferences_;
}

void WebContentsImpl::DragSourceEndedAt(float client_x,
                                        float client_y,
                                        float screen_x,
                                        float screen_y,
                                        ui::mojom::DragOperation operation,
                                        RenderWidgetHost* source_rwh) {
  OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                        "WebContentsImpl::DragSourceEndedAt");
  if (source_rwh) {
    source_rwh->DragSourceEndedAt(gfx::PointF(client_x, client_y),
                                  gfx::PointF(screen_x, screen_y), operation,
                                  base::DoNothing());
  }
}

void WebContentsImpl::LoadStateChanged(network::mojom::LoadInfoPtr load_info) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::LoadStateChanged", "host",
                        load_info->host, "load_state", load_info->load_state);

  // If the new load state isn't progressed as far as the current loading state
  // or both are sending an upload and the upload is smaller, return early
  // discarding the new load state.
  if (load_info_timestamp_ + kUpdateLoadStatesInterval > load_info->timestamp &&
      (load_state_.state > load_info->load_state ||
       (load_state_.state == load_info->load_state &&
        load_state_.state == net::LOAD_STATE_SENDING_REQUEST &&
        upload_size_ > load_info->upload_size))) {
    return;
  }

  load_info_timestamp_ = load_info->timestamp;
  std::u16string host16 = url_formatter::IDNToUnicode(load_info->host);
  // Drop no-op updates.
  if (load_state_.state == load_info->load_state &&
      load_state_.param == load_info->state_param &&
      upload_position_ == load_info->upload_position &&
      upload_size_ == load_info->upload_size && load_state_host_ == host16) {
    return;
  }
  load_state_ = net::LoadStateWithParam(
      static_cast<net::LoadState>(load_info->load_state),
      load_info->state_param);
  load_state_host_ = host16;
}

void WebContentsImpl::SetVisibilityAndNotifyObservers(Visibility visibility) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SetVisibilityAndNotifyObservers",
                        "visibility", static_cast<int>(visibility));
  const Visibility previous_visibility = visibility_;
  visibility_ = visibility;

  // Notify observers if the visibility changed or if WasShown() is being called
  // for the first time.
  if (visibility != previous_visibility ||
      (visibility == Visibility::VISIBLE && !did_first_set_visible_)) {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.OnVisibilityChanged");
    observers_.NotifyObservers(&WebContentsObserver::OnVisibilityChanged,
                               visibility);
  }
}

void WebContentsImpl::OnStart(const perfetto::DataSourceBase::StartArgs&) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  EmitTracingSlice(base::UTF16ToUTF8(GetTitle()));
}

void WebContentsImpl::NotifyWebContentsFocused(
    RenderWidgetHost* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::NotifyWebContentsFocused",
                        "render_widget_host", render_widget_host);
  observers_.NotifyObservers(&WebContentsObserver::OnWebContentsFocused,
                             render_widget_host);
}

void WebContentsImpl::NotifyWebContentsLostFocus(
    RenderWidgetHost* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::NotifyWebContentsLostFocus",
                        "render_widget_host", render_widget_host);
  observers_.NotifyObservers(&WebContentsObserver::OnWebContentsLostFocus,
                             render_widget_host);
}

void WebContentsImpl::SystemDragEnded(RenderWidgetHost* source_rwh) {
  if (delegate_) {
    delegate_->HandleDragEnded();
  }

  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SystemDragEnded",
                        "render_widget_host", source_rwh);
  if (source_rwh) {
    source_rwh->DragSourceSystemDragEnded();
  }
}

void WebContentsImpl::SetClosedByUserGesture(bool value) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetClosedByUserGesture",
                        "value", value);
  closed_by_user_gesture_ = value;
}

bool WebContentsImpl::GetClosedByUserGesture() {
  return closed_by_user_gesture_;
}

int WebContentsImpl::GetMinimumZoomPercent() {
  return minimum_zoom_percent_;
}

int WebContentsImpl::GetMaximumZoomPercent() {
  return maximum_zoom_percent_;
}

void WebContentsImpl::SetPageScale(float scale_factor) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetPageScale",
                        "scale_factor", scale_factor);
  GetPrimaryMainFrame()->GetAssociatedLocalMainFrame()->SetScaleFactor(
      scale_factor);
}

void WebContentsImpl::SetIgnoreZoomGestures(bool ignore) {
  ignore_zoom_gestures_ = ignore;
}

gfx::Size WebContentsImpl::GetPreferredSize() {
  return IsBeingCaptured() ? preferred_size_for_capture_ : preferred_size_;
}

bool WebContentsImpl::GotResponseToPointerLockRequest(
    blink::mojom::PointerLockResult result) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::GotResponseToPointerLockRequest");
  if (pointer_lock_widget_) {
    auto* web_contents =
        WebContentsImpl::FromRenderWidgetHostImpl(pointer_lock_widget_);
    if (web_contents != this) {
      return web_contents->GotResponseToPointerLockRequest(result);
    }

    if (pointer_lock_widget_->GotResponseToPointerLockRequest(result)) {
      return true;
    }

    if (pointer_lock_widget_ && pointer_lock_widget_->GetView() &&
        pointer_lock_widget_->GetView()->IsPointerLocked()) {
      return true;
    }
  }

  SetPointerLockWidgetInParentChain(nullptr);

  return false;
}

void WebContentsImpl::GotPointerLockPermissionResponse(bool allowed) {
  GotResponseToPointerLockRequest(
      allowed ? blink::mojom::PointerLockResult::kSuccess
              : blink::mojom::PointerLockResult::kPermissionDenied);
}

void WebContentsImpl::DropPointerLockForTesting() {
  if (pointer_lock_widget_) {
    pointer_lock_widget_->RejectPointerLockOrUnlockIfNecessary(
        blink::mojom::PointerLockResult::kUnknownError);
    SetPointerLockWidgetInParentChain(nullptr);
  }
}

bool WebContentsImpl::GotResponseToKeyboardLockRequest(bool allowed) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::GotResponseToKeyboardLockRequest",
                        "allowed", allowed);
  if (!keyboard_lock_widget_) {
    return false;
  }
  // Exits early here if GotResponseToKeyboardLockRequest() was called from
  // the dtor for `this`.
  if (WebContentsImpl::FromRenderWidgetHostImpl(keyboard_lock_widget_) !=
      this) {
    return false;
  }
  // KeyboardLock is only supported when called by the top-level browsing
  // context and is not supported in embedded content scenarios such as
  // GuestView guests (<webview> tags, PDF viewer). However, some embedders
  // (e.g. WebUIBrowserWindow) attach top-level tabs as inner WebContents and
  // opt in via AllowKeyboardLockForInnerContents().
  if (GetOuterWebContents() &&
      (!delegate_ || !delegate_->AllowKeyboardLockForInnerContents(this))) {
    keyboard_lock_widget_->GotResponseToKeyboardLockRequest(false);
    return false;
  }
  keyboard_lock_widget_->GotResponseToKeyboardLockRequest(allowed);
  return true;
}

bool WebContentsImpl::HasOpener() {
  return GetOpener() != nullptr;
}

RenderFrameHostImpl* WebContentsImpl::GetOpener() {
  FrameTreeNode* opener_ftn = primary_frame_tree_.root()->opener();
  return opener_ftn ? opener_ftn->current_frame_host() : nullptr;
}

bool WebContentsImpl::HasLiveOriginalOpenerChain() {
  return GetFirstWebContentsInLiveOriginalOpenerChain() != nullptr;
}

WebContents* WebContentsImpl::GetFirstWebContentsInLiveOriginalOpenerChain() {
  FrameTreeNode* opener_ftn =
      primary_frame_tree_.root()
          ->first_live_main_frame_in_original_opener_chain();
  return opener_ftn ? WebContents::FromRenderFrameHost(
                          opener_ftn->current_frame_host())
                    : nullptr;
}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
void WebContentsImpl::DidChooseColorInColorChooser(SkColor color) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::DidChooseColorInColorChooser",
                        "color", color);
  if (color_chooser_holder_) {
    color_chooser_holder_->DidChooseColorInColorChooser(color);
  }
}

void WebContentsImpl::DidEndColorChooser() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DidEndColorChooser");
  color_chooser_holder_.reset();
}
#endif

int WebContentsImpl::DownloadImageFromAxNode(const ui::AXTreeID tree_id,
                                             const ui::AXNodeID node_id,
                                             const gfx::Size& preferred_size,
                                             uint32_t max_bitmap_size,
                                             bool bypass_cache,
                                             ImageDownloadCallback callback) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DownloadImage",
                        "tree_id,node_id",
                        tree_id.ToString() + "," + base::ToString(node_id));
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  const int download_id = GetNextDownloadId();

  RenderFrameHostImpl* target_frame =
      RenderFrameHostImpl::FromAXTreeID(tree_id);
  if (!target_frame || !target_frame->IsRenderFrameLive()) {
    // If the renderer process is dead (i.e. crash, or memory pressure on
    // Android), the downloader service will be invalid. Pre-Mojo, this would
    // hang the callback indefinitely since the IPC would be dropped. Now,
    // respond with a 400 HTTP error code to indicate that something went wrong.
    // Additionally for A11y requests, send an empty URL back since it won't be
    // used anyways.
    GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
                       weak_factory_.GetWeakPtr(),
                       target_frame ? target_frame->GetWeakPtr() : nullptr,
                       std::move(callback), download_id, GURL(), 400,
                       std::vector<SkBitmap>(), std::vector<gfx::Size>()));
    return download_id;
  }
  CHECK_EQ(target_frame->GetAXTreeID(), tree_id);
  target_frame->GetMojoImageDownloader()->DownloadImageFromAxNode(
      node_id, preferred_size, max_bitmap_size, bypass_cache,
      base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
                     weak_factory_.GetWeakPtr(), target_frame->GetWeakPtr(),
                     std::move(callback), download_id, GURL()));
  return download_id;
}

int WebContentsImpl::DownloadImage(
    const GURL& url,
    bool is_favicon,
    const gfx::Size& preferred_size,
    uint32_t max_bitmap_size,
    bool bypass_cache,
    WebContents::ImageDownloadCallback callback) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DownloadImage", "url",
                        url);
  return DownloadImageInFrame(GlobalRenderFrameHostId(), url, is_favicon,
                              preferred_size, max_bitmap_size, bypass_cache,
                              std::move(callback));
}

int WebContentsImpl::DownloadImageInFrame(
    const GlobalRenderFrameHostId& initiator_frame_routing_id,
    const GURL& url,
    bool is_favicon,
    const gfx::Size& preferred_size,
    uint32_t max_bitmap_size,
    bool bypass_cache,
    WebContents::ImageDownloadCallback callback) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DownloadImageInFrame");
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  const int download_id = GetNextDownloadId();

  RenderFrameHostImpl* initiator_frame =
      initiator_frame_routing_id.child_id
          ? RenderFrameHostImpl::FromID(initiator_frame_routing_id)
          : GetPrimaryMainFrame();
  if (!initiator_frame->IsRenderFrameLive()) {
    // If the renderer process is dead (i.e. crash, or memory pressure on
    // Android), the downloader service will be invalid. Pre-Mojo, this would
    // hang the callback indefinitely since the IPC would be dropped. Now,
    // respond with a 400 HTTP error code to indicate that something went wrong.
    GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(
            &WebContentsImpl::OnDidDownloadImage, weak_factory_.GetWeakPtr(),
            initiator_frame->GetWeakPtr(), std::move(callback), download_id,
            url, 400, std::vector<SkBitmap>(), std::vector<gfx::Size>()));
    return download_id;
  }

  initiator_frame->GetMojoImageDownloader()->DownloadImage(
      url, is_favicon, preferred_size, max_bitmap_size, bypass_cache,
      base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
                     weak_factory_.GetWeakPtr(), initiator_frame->GetWeakPtr(),
                     std::move(callback), download_id, url));
  return download_id;
}

void WebContentsImpl::Find(int request_id,
                           const std::u16string& search_text,
                           blink::mojom::FindOptionsPtr options,
                           bool skip_delay) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Find");
  // Cowardly refuse to search for no text.
  if (search_text.empty()) {
    NOTREACHED();
  }

  GetOrCreateFindRequestManager()->Find(request_id, search_text,
                                        std::move(options), skip_delay);
}

void WebContentsImpl::StopFinding(StopFindAction action) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::StopFinding");
  if (FindRequestManager* manager = GetFindRequestManager()) {
    manager->StopFinding(action);
  }
}

bool WebContentsImpl::WasEverAudible() {
  return was_ever_audible_;
}

void WebContentsImpl::ExitFullscreen(bool will_cause_resize) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ExitFullscreen");
  // Clean up related state and initiate the fullscreen exit.
  GetRenderViewHost()->GetWidget()->RejectPointerLockOrUnlockIfNecessary(
      blink::mojom::PointerLockResult::kUserRejected);
  ExitFullscreenMode(will_cause_resize);
}

std::optional<base::ScopedClosureRunner>
WebContentsImpl::ForSecurityDropFullscreen(int64_t display_id) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ForSecurityDropFullscreen",
                        "display_id", display_id);
  base::WeakPtr<WebContentsImpl> weak_this = weak_factory_.GetWeakPtr();

  // Make WebContentses "related" to this instance exit HTML element fullscreen,
  // ignoring browser fullscreen and fullscreen-within-tab modes. This needs to
  // be done with two passes, because it is simple to walk _up_ the chain of
  // openers and outer contents, but it not simple to walk _down_ the chain.
  auto is_fullscreen = [](WebContentsImpl* tab, int64_t display_id) {
    if (!tab || !tab->GetDelegate()) {
      return false;
    }
    const FullscreenState state = tab->GetDelegate()->GetFullscreenState(tab);
    return state.target_mode == FullscreenMode::kContent &&
           (display_id == display::kInvalidDisplayId ||
            state.target_display_id == display::kInvalidDisplayId ||
            state.target_display_id == display_id);
  };

  // First, determine if any fullscreen WebContents has this WebContents as an
  // upstream contents. Drop that WebContents out of fullscreen if it does. This
  // is theoretically quadratic-ish (fullscreen contentses x each one's opener
  // length) but neither of those is expected to ever be a large number.
  std::vector<base::WeakPtr<WebContentsImpl>> fullscreen_contents_list;
  for (WebContentsImpl* fullscreen_contents :
       *FullscreenContentsSet(GetBrowserContext())) {
    fullscreen_contents_list.push_back(
        fullscreen_contents->weak_factory_.GetWeakPtr());
  }

  for (auto& fullscreen_contents : fullscreen_contents_list) {
    if (!weak_this) {
      return std::nullopt;
    }
    if (!fullscreen_contents) {
      continue;
    }
    if (is_fullscreen(fullscreen_contents.get(), display_id)) {
      auto opener_contentses =
          GetAllOpeningWebContents(fullscreen_contents.get());
      if (opener_contentses.count(this)) {
        fullscreen_contents->ExitFullscreen(true);
      }
    }
  }

  if (!weak_this) {
    return std::nullopt;
  }

  // Second, walk upstream from this WebContents, and drop the fullscreen of
  // all WebContentses that are in fullscreen. Block all the WebContentses in
  // the chain from entering fullscreen while the returned closure runner is
  // alive. It's OK that this set doesn't contain downstream WebContentses, as
  // any request to enter fullscreen will have the upstream of the WebContents
  // checked. (See CanEnterFullscreenMode().)

  std::vector<base::WeakPtr<WebContentsImpl>> blocked_contents_list;
  auto cleanup_blockers = [](std::vector<base::WeakPtr<WebContentsImpl>> list) {
    for (auto& wc : list) {
      if (wc) {
        DCHECK_GT(wc->fullscreen_blocker_count_, 0);
        --wc->fullscreen_blocker_count_;
      }
    }
  };

  std::vector<base::WeakPtr<WebContentsImpl>> openers;
  for (WebContentsImpl* opener : GetAllOpeningWebContents(this)) {
    openers.push_back(opener->weak_factory_.GetWeakPtr());
  }

  for (auto& opener : openers) {
    if (!weak_this) {
      cleanup_blockers(std::move(blocked_contents_list));
      return std::nullopt;
    }
    if (!opener) {
      continue;
    }

    if (is_fullscreen(opener.get(), display_id)) {
      opener->ExitFullscreen(true);
    }

    if (!opener) {
      continue;
    }

    // ...block the WebContents from entering fullscreen until further notice.
    ++opener->fullscreen_blocker_count_;
    blocked_contents_list.push_back(opener);
  }

  if (!weak_this) {
    cleanup_blockers(std::move(blocked_contents_list));
    return std::nullopt;
  }

  return base::ScopedClosureRunner(
      base::BindOnce(cleanup_blockers, std::move(blocked_contents_list)));
}

void WebContentsImpl::ResumeLoadingCreatedWebContents() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ResumeLoadingCreatedWebContents");
  if (delayed_load_url_params_.get()) {
    DCHECK(!delayed_open_url_params_);
    base::WeakPtr<NavigationHandle> navigation =
        GetController().LoadURLWithParams(*delayed_load_url_params_.get());
    if (delayed_navigation_handle_callback_ && navigation) {
      std::move(delayed_navigation_handle_callback_).Run(*navigation);
    }
    delayed_navigation_handle_callback_.Reset();
    delayed_load_url_params_.reset(nullptr);
    return;
  }

  CHECK(!delayed_navigation_handle_callback_);

  if (delayed_open_url_params_.get()) {
    OpenURL(*delayed_open_url_params_.get(),
            std::move(delayed_navigation_handle_callback_));
    delayed_open_url_params_.reset(nullptr);
    return;
  }

  // Renderer-created main frames wait for the renderer to request for them to
  // perform navigations and to be shown. We signal that this condition is
  // satisfied by calling Init().
  if (is_resume_pending_) {
    is_resume_pending_ = false;
    GetPrimaryMainFrame()->Init();
  }
}

bool WebContentsImpl::FocusLocationBarByDefault() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::FocusLocationBarByDefault");
  if (should_focus_location_bar_by_default_) {
    return true;
  }

  return delegate_ && delegate_->ShouldFocusLocationBarByDefault(this);
}

void WebContentsImpl::DidStartNavigation(NavigationHandle* navigation_handle) {
  TRACE_EVENT1("navigation", "WebContentsImpl::DidStartNavigation",
               "navigation_handle", navigation_handle);
  const bool is_in_main_frame = navigation_handle->IsInMainFrame();
  const bool is_in_primary_main_frame =
      navigation_handle->IsInPrimaryMainFrame();
  const bool is_renderer_initiated = navigation_handle->IsRendererInitiated();
  const GURL url = navigation_handle->GetURL();

  base::ElapsedTimer duration;
  observers_.NotifyObservers(&WebContentsObserver::DidStartNavigation,
                             navigation_handle);
  // WARNING: DO NOT use |navigation_handle| after this point. An observer may
  // have destroyed the NavigationRequest, rendering the pointer invalid. See
  // crbug.com/504574017. We set `navigation_handle` to nullptr here to prevent
  // accidental usage in that case.
  navigation_handle = nullptr;
  base::TimeDelta elapsed = duration.Elapsed();
  base::UmaHistogramTimes("WebContentsObserver.DidStartNavigation", elapsed);

  base::UmaHistogramTimes(
      base::StrCat({"WebContentsObserver.DidStartNavigation.",
                    is_in_main_frame ? "MainFrame" : "Subframe"}),
      elapsed);

  if (is_in_primary_main_frame) {
    // `notify_disconnection_` may be reset during discard operations, ensure
    // this is restored when the when contents is re-navigated.
    notify_disconnection_ = true;

    // When the browser is started with about:blank as the startup URL, focus
    // the location bar (which will also select its contents) so people can
    // simply begin typing to navigate elsewhere.
    //
    // We need to be careful not to trigger this for anything other than the
    // startup navigation. In particular, if we allow an attacker to open a
    // popup to about:blank, then navigate, focusing the Omnibox will cause the
    // end of the new URL to be scrolled into view instead of the start,
    // allowing the attacker to spoof other URLs. The conditions checked here
    // are all aimed at ensuring no such attacker-controlled navigation can
    // trigger this.
    should_focus_location_bar_by_default_ =
        GetController().IsInitialNavigation() && !is_renderer_initiated &&
        url == url::kAboutBlankURL;
  }
}

void WebContentsImpl::DidRedirectNavigation(
    NavigationHandle* navigation_handle) {
  TRACE_EVENT1("navigation", "WebContentsImpl::DidRedirectNavigation",
               "navigation_handle", navigation_handle);
  const ReloadType reload_type = navigation_handle->GetReloadType();
  base::WeakPtr<RenderFrameHostImpl> rfh_weak;
  if (reload_type != ReloadType::NONE) {
    rfh_weak = NavigationRequest::From(navigation_handle)
                   ->frame_tree_node()
                   ->current_frame_host()
                   ->GetWeakPtr();
  }

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DidRedirectNavigation");
    observers_.NotifyObservers(&WebContentsObserver::DidRedirectNavigation,
                               navigation_handle);
  }
  // WARNING: DO NOT use |navigation_handle| after this point. An observer may
  // have destroyed the NavigationRequest, rendering the pointer invalid. See
  // crbug.com/504574017. We set `navigation_handle` to nullptr here to prevent
  // accidental usage in that case.
  navigation_handle = nullptr;

  // Notify accessibility if this is a reload. This has to called on the
  // BrowserAccessibilityManager associated with the old RFHI.
  if (reload_type != ReloadType::NONE && rfh_weak) {
    ui::BrowserAccessibilityManager* manager =
        rfh_weak->browser_accessibility_manager();
    if (manager) {
      manager->UserIsReloading();
    }
  }
}

void WebContentsImpl::ReadyToCommitNavigation(
    NavigationHandle* navigation_handle) {
  TRACE_EVENT1("navigation", "WebContentsImpl::ReadyToCommitNavigation",
               "navigation_handle", navigation_handle);
  CHECK(!navigation_handle->IsSameDocument());

  const bool is_in_main_frame = navigation_handle->IsInMainFrame();
  const bool is_renderer_initiated = navigation_handle->IsRendererInitiated();
  const GURL url = navigation_handle->GetURL();
  const net::Error net_error_code = navigation_handle->GetNetErrorCode();
  const std::optional<net::SSLInfo> ssl_info = navigation_handle->GetSSLInfo();

  // Notify the OS that the workload is about to increase for main frame
  // navigations only. This a trade off between latency and power - we don't
  // want to do it for every navigation.
  if (is_in_main_frame) {
    auto* gpu_process_host =
        GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED, /*force_create=*/false);
    if (gpu_process_host) {
      auto* host = gpu_process_host->gpu_host();
      if (host) {
        host->NotifyWorkloadIncrease();
      }
    }
  }

  observers_.NotifyObservers(&WebContentsObserver::ReadyToCommitNavigation,
                             navigation_handle);
  // WARNING: DO NOT use |navigation_handle| after this point. An observer may
  // have destroyed the NavigationRequest, rendering the pointer invalid. See
  // crbug.com/504574017. We set `navigation_handle` to nullptr here to prevent
  // accidental usage in that case.
  navigation_handle = nullptr;

  // If any domains are blocked from accessing 3D APIs because they may
  // have caused the GPU to reset recently, unblock them here if the user
  // initiated this navigation. This implies that the user was involved in
  // the decision to navigate, so there's no concern about
  // denial-of-service issues. Want to do this as early as
  // possible to avoid race conditions with pages attempting to access
  // WebGL early on.
  //
  // TODO(crbug.com/41257523): currently navigations initiated by the browser
  // (reload button, reload menu option, pressing return in the Omnibox)
  // return false from HasUserGesture(). If or when that is addressed,
  // remove the check for IsRendererInitiated() below.
  //
  // TODO(crbug.com/40571460): HasUserGesture comes from the renderer
  // process and isn't validated. Until it is, don't trust it.
  if (!is_renderer_initiated) {
    GpuDataManagerImpl::GetInstance()->UnblockDomainFrom3DAPIs(url);
  }

  // SSLInfo is not needed on subframe navigations since the main-frame
  // certificate is the only one that can be inspected (using the info
  // bubble) without refreshing the page with DevTools open.
  // We don't call DidStartResourceResponse on net errors, since that results on
  // existing cert exceptions being revoked, which leads to weird behavior with
  // committed interstitials or while offline. We only need the error check for
  // the main frame case.
  if (is_in_main_frame && net_error_code == net::OK) {
    GetController().ssl_manager()->DidStartResourceResponse(
        url::SchemeHostPort(url),
        ssl_info.has_value() ? net::IsCertStatusError(ssl_info->cert_status)
                             : false);
  }
}

void WebContentsImpl::OnStartDragging(
    DropData* drop_data,
    const GlobalRenderFrameHostToken& source_rfh_token) {
  CHECK(drop_data);
  auto* source_frame = RenderFrameHost::FromFrameToken(source_rfh_token);
  if (!source_frame) {
    return;
  }

  // Do not contaminate pure local file drags (e.g., from ChromeOS Files app)
  // with drag tracking tokens.
  bool is_pure_file_drag =
      !drop_data->filenames.empty() && !drop_data->text.has_value() &&
      !drop_data->html.has_value() && drop_data->file_contents.empty();

  if (is_pure_file_drag) {
    return;
  }

  DragId drag_id(base::UnguessableToken::Create());

  DragSourceDocumentTracker::GetOrCreateForCurrentDocument(source_frame)
      ->AddDragId(drag_id);

  active_drag_id_ = drag_id;
  SetDragSource(drag_id, source_rfh_token);
  drop_data->custom_data[u"chromium/x-drag-id"] =
      base::ASCIIToUTF16(drag_id.value().ToString());
}

void WebContentsImpl::OnDragSourceEnded() {
  if (active_drag_id_) {
    // We intentionally do not remove the drag source from DragSourceMap here.
    // On macOS, the drag can end synchronously from the OS perspective before
    // the async drop handling finishes, causing a race condition if we clear
    // the mapping. The mapping will be cleaned up by the
    // DragSourceDocumentTracker when the source document is destroyed.
    // However, to prevent memory leaks on long-lived pages if a drag is
    // cancelled or unconsumed, we post a deferred task to clean it up after 10
    // seconds.
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(
            [](base::WeakPtr<WebContentsImpl> web_contents, DragId drag_id) {
              if (!web_contents) {
                return;
              }
              DragSourceMap::Get(web_contents->GetBrowserContext())
                  ->RemoveDragSource(drag_id);
            },
            weak_factory_.GetWeakPtr(), *active_drag_id_),
        base::Seconds(10));
    active_drag_id_.reset();
  }
}

void WebContentsImpl::DidFinishNavigation(NavigationHandle* navigation_handle) {
  TRACE_EVENT1("navigation", "WebContentsImpl::DidFinishNavigation",
               "navigation_handle", navigation_handle);

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DidFinishNavigation");
    observers_.NotifyObservers(&WebContentsObserver::DidFinishNavigation,
                               navigation_handle);
  }
  if (safe_area_insets_host_) {
    safe_area_insets_host_->DidFinishNavigation(navigation_handle);
  }

  if (navigation_handle->HasCommitted()) {
    // TODO(domfarolino, dmazzoni): Do this using WebContentsObserver. See
    // https://crbug.com/981271.
    ui::BrowserAccessibilityManager* manager =
        static_cast<RenderFrameHostImpl*>(
            navigation_handle->GetRenderFrameHost())
            ->browser_accessibility_manager();
    if (manager) {
      if (navigation_handle->IsErrorPage()) {
        manager->NavigationFailed();
      } else {
        manager->NavigationSucceeded();
      }
    }

    // TODO(crbug.com/40774464) : Move this tracking to PageImpl.
    if (navigation_handle->IsInPrimaryMainFrame() &&
        !navigation_handle->IsSameDocument()) {
      was_ever_audible_ = false;
    }

    if (navigation_handle->IsInPrimaryMainFrame()) {
      OnCaptureHandleConfigUpdate(GetPrimaryPage());
    }

    if (!navigation_handle->IsSameDocument()) {
      last_screen_orientation_change_time_ = base::TimeTicks();
    }
  }

  // If we didn't end up on about:blank after setting this in DidStartNavigation
  // then don't focus the location bar.
  if (should_focus_location_bar_by_default_ &&
      navigation_handle->GetURL() != url::kAboutBlankURL) {
    should_focus_location_bar_by_default_ = false;
  }

  if (navigation_handle->IsInPrimaryMainFrame() &&
      first_primary_navigation_completed_) {
    RecordMaxFrameCountUMA(max_loaded_frame_count_);
  }

  // If navigation has successfully finished in the main frame, set
  // |first_primary_navigation_completed_| to true so that we will record
  // |max_loaded_frame_count_| above when future main frame navigations finish.
  if (navigation_handle->IsInPrimaryMainFrame() &&
      !navigation_handle->IsErrorPage()) {
    first_primary_navigation_completed_ = true;

    // Navigation has completed in main frame. Reset |max_loaded_frame_count_|.
    // |max_loaded_frame_count_| is not necessarily 1 if the navigation was
    // served from BackForwardCache.
    max_loaded_frame_count_ = GetFrameTreeSize(&primary_frame_tree_);

    BrowserAccessibilityStateImpl::GetInstance()->OnPageNavigationComplete(
        this);
  }

  // TODO(crbug.com/40202416): MPArch GuestView: We might need to look up the
  // preferences for the navigation here and adjust the guest, but for now
  // SetWebPreferences does not adjust the guest frame tree so we look for the
  // preferences matching the primary main frame.
  if (web_preferences_) {
    // Update the WebPreferences for this WebContents that depends on changes
    // that might occur during navigation. This will only update the preferences
    // that needs to be updated (and won't cause an update/overwrite preferences
    // that needs to stay the same after navigations).
    bool value_changed_due_to_override =
        GetContentClient()->browser()->OverrideWebPreferencesAfterNavigation(
            this, *GetPrimaryMainFrame()->GetSiteInstance(),
            web_preferences_.get());
    // We need to update the WebPreferences value on the renderer if the value
    // is changed due to the override above, or if the navigation is served from
    // the back-forward cache, because the WebPreferences value stored in the
    // renderer might be stale (because we don't send WebPreferences updates to
    // bfcached renderers). Same for prerendering.
    // TODO(rakina): Maybe handle the back-forward cache case in
    // ReadyToCommitNavigation instead?
    // TODO(crbug.com/40758687): Maybe sync RendererPreferences as well?
    if (value_changed_due_to_override ||
        NavigationRequest::From(navigation_handle)->IsPageActivation()) {
      SetWebPreferences(*web_preferences_.get());
    }
  }

  if (navigation_handle->HasCommitted() &&
      navigation_handle->IsPrerenderedPageActivation()) {
    // We defer favicon and manifest URL updates while prerendering. Upon
    // activation, we must inform interested parties about our candidate favicon
    // URLs and the manifest URL.
    DCHECK(navigation_handle->IsInPrimaryMainFrame());
    auto* rfhi = static_cast<RenderFrameHostImpl*>(
        navigation_handle->GetRenderFrameHost());
    UpdateFaviconURL(rfhi, rfhi->FaviconURLs(),
                     blink::mojom::FaviconUpdateReason::kPageLoad);
    OnManifestUrlChanged(rfhi->GetPage());

    // The page might have set its title while prerendering, and if it was, we
    // skipped notifying observers then, and we need to notify them now after
    // the page is activated.
    DCHECK(navigation_handle->IsInPrimaryMainFrame());
    NavigationEntryImpl* entry = GetController().GetLastCommittedEntry();
    DCHECK(entry);
    if (!entry->GetTitle().empty()) {
      NotifyTitleUpdateForEntry(entry);
    }
  }
}

void WebContentsImpl::DidCancelNavigationBeforeStart(
    NavigationHandle* navigation_handle) {
#if BUILDFLAG(IS_ANDROID)
  if (auto* animation_manager =
          static_cast<BackForwardTransitionAnimationManagerAndroid*>(
              GetBackForwardTransitionAnimationManager())) {
    animation_manager->OnNavigationCancelledBeforeStart(navigation_handle);
  }
#endif
}

void WebContentsImpl::DidFailLoadWithError(
    RenderFrameHostImpl* render_frame_host,
    const GURL& url,
    int error_code) {
  TRACE_EVENT2("content,navigation", "WebContentsImpl::DidFailLoadWithError",
               "render_frame_host", render_frame_host, "url", url);
  observers_.NotifyObservers(&WebContentsObserver::DidFailLoad,
                             render_frame_host, url, error_code);
}

void WebContentsImpl::DraggableRegionsChanged(
    const std::vector<blink::mojom::DraggableRegionPtr>& regions) {
  if (!GetDelegate()) {
    return;
  }
  GetDelegate()->DraggableRegionsChanged(regions, this);
}

void WebContentsImpl::OnFirstContentfulPaintInPrimaryMainFrame(
    base::TimeTicks presentation_time) {
  observers_.NotifyObservers(
      &WebContentsObserver::OnFirstContentfulPaintInPrimaryMainFrame,
      presentation_time);
}

void WebContentsImpl::OnLargestContentfulPaintInPrimaryMainFrame(
    base::TimeTicks presentation_time) {
  observers_.NotifyObservers(
      &WebContentsObserver::OnLargestContentfulPaintInPrimaryMainFrame,
      presentation_time);
}

gfx::NativeWindow WebContentsImpl::GetOwnerNativeWindow() {
  return GetTopLevelNativeWindow();
}

media::PictureInPictureEventsInfo::AutoPipInfo WebContentsImpl::GetAutoPipInfo()
    const {
  return GetContentClient()->browser()->GetAutoPipInfo(*this);
}

void WebContentsImpl::OnKeepAliveRequestCreated(
    const network::ResourceRequest& resource_request,
    RenderFrameHostImpl* initiator_rfh) {
  observers_.NotifyObservers(&WebContentsObserver::OnKeepAliveRequestCreated,
                             resource_request, initiator_rfh);
}

void WebContentsImpl::NotifyChangedNavigationState(
    InvalidateTypes changed_flags) {
  NotifyNavigationStateChanged(changed_flags);
}

bool WebContentsImpl::ShouldAllowRendererInitiatedCrossProcessNavigation(
    RenderFrameHostImpl* render_frame_host,
    bool is_outermost_main_frame_navigation) {
  OPTIONAL_TRACE_EVENT1(
      "content",
      "WebContentsImpl::ShouldAllowRendererInitiatedCrossProcessNavigation",
      "is_outermost_main_frame_navigation", is_outermost_main_frame_navigation);
  if (render_frame_host->frame_tree()->is_guest()) {
    GuestPageHolderImpl* guest =
        GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host);
    if (guest && guest->delegate()) {
      return guest->delegate()
          ->GuestShouldAllowRendererInitiatedCrossProcessNavigation(
              is_outermost_main_frame_navigation);
    }
    return true;
  }
  if (!delegate_) {
    return true;
  }
  return delegate_->ShouldAllowRendererInitiatedCrossProcessNavigation(
      is_outermost_main_frame_navigation);
}

bool WebContentsImpl::ShouldPreserveAbortedURLs() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ShouldPreserveAbortedURLs");
  if (!delegate_) {
    return false;
  }
  return delegate_->ShouldPreserveAbortedURLs(this);
}

BackForwardCacheImpl& WebContentsImpl::GetBackForwardCache() {
  CHECK(back_forward_cache_);
  return *back_forward_cache_;
}

void WebContentsImpl::NotifyNavigationStateChangedFromController(
    InvalidateTypes changed_flags) {
  NotifyNavigationStateChanged(changed_flags);
}

#if BUILDFLAG(IS_ANDROID)

scoped_refptr<viz::RasterContextProvider>
WebContentsImpl::GetRasterContextProvider() {
  auto window = GetTopLevelNativeWindow();
  if (!window) {
    return nullptr;
  }

  auto* compositor = static_cast<CompositorImpl*>(window->GetCompositor());
  if (!compositor) {
    return nullptr;
  }
  return compositor->GetRasterContextProvider();
}

gfx::ColorSpace WebContentsImpl::GetOutputColorSpace(
    gfx::ContentColorUsage color_usage,
    bool needs_alpha) {
  auto window = GetTopLevelNativeWindow();
  if (!window) {
    return gfx::ColorSpace();
  }
  return window->GetDisplayWithWindowColorSpace()
      .GetColorSpaces()
      .GetOutputColorSpace(color_usage, needs_alpha);
}

#endif  // BUILDFLAG(IS_ANDROID)

input::TouchEmulator* WebContentsImpl::GetTouchEmulator(
    bool create_if_necessary) {
  CHECK(rwh_input_event_router_);

  if (!touch_emulator_ && create_if_necessary) {
    touch_emulator_ = std::make_unique<TouchEmulatorImpl>(
        rwh_input_event_router_.get(),
        rwh_input_event_router_->last_device_scale_factor());
  }

  return touch_emulator_.get();
}

void WebContentsImpl::CancelAutoscroll(input::RenderWidgetHostViewInput* view) {
  auto* view_base = static_cast<RenderWidgetHostViewBase*>(view);
  auto* rwhi = RenderWidgetHostImpl::From(view_base->GetRenderWidgetHost());
  if (rwhi) {
    rwhi->AutoscrollEnd();
  }
}

void WebContentsImpl::DidNavigateMainFramePreCommit(
    NavigationHandle* navigation_handle,
    bool navigation_is_within_page) {
  auto* request = static_cast<NavigationRequest*>(navigation_handle);
  FrameTreeNode* frame_tree_node = request->frame_tree_node();

  // The `frame_tree_node` is always a main frame.
  DCHECK(frame_tree_node->IsMainFrame());
  TRACE_EVENT1("content,navigation",
               "WebContentsImpl::DidNavigateMainFramePreCommit",
               "navigation_is_within_page", navigation_is_within_page);
  const bool is_primary_mainframe =
      frame_tree_node->GetFrameType() == FrameType::kPrimaryMainFrame;
  // If running for a non-primary main frame, early out.
  if (!is_primary_mainframe) {
    return;
  }

#if BUILDFLAG(IS_ANDROID)
  auto* animation_manager =
      static_cast<BackForwardTransitionAnimationManagerAndroid*>(
          GetBackForwardTransitionAnimationManager());
  if (animation_manager) {
    animation_manager->OnDidNavigatePrimaryMainFramePreCommit(
        request, frame_tree_node->render_manager()->current_frame_host(),
        request->GetRenderFrameHost());
  }
#endif

  if (navigation_is_within_page) {
    return;
  }

  auto* rwhvb = static_cast<RenderWidgetHostViewBase*>(
      frame_tree_node->current_frame_host()->GetView());
  if (rwhvb) {
    rwhvb->OnOldViewDidNavigatePreCommit();
  }

  CancelKeyboardLock(keyboard_lock_widget_);
}

void WebContentsImpl::DidNavigateAnyFramePreCommit(
    NavigationHandle* navigation_handle,
    bool navigation_is_within_page) {
  // Ensure fullscreen mode is exited before committing the navigation to a
  // different page.  The next page will not start out assuming it is in
  // fullscreen mode.
  if (navigation_is_within_page || !IsFullscreen()) {
    return;
  }

  bool should_exit_fullscreen = false;
  if (navigation_handle->IsInPrimaryMainFrame()) {
    should_exit_fullscreen = true;
  } else {
    // For iframe navigation, exit if the fullscreen was requested by the
    // iframe or one of its descendants.
    const FrameTreeNodeId navigating_id =
        navigation_handle->GetFrameTreeNodeId();
    should_exit_fullscreen =
        std::any_of(fullscreen_frames_.begin(), fullscreen_frames_.end(),
                    [navigating_id](RenderFrameHostImpl* rfh) {
                      for (RenderFrameHostImpl* current = rfh; current;
                           current = current->GetParentOrOuterDocument()) {
                        if (current->frame_tree_node()->frame_tree_node_id() ==
                            navigating_id) {
                          return true;
                        }
                      }
                      return false;
                    });
  }

  if (should_exit_fullscreen) {
    base::WeakPtr<WebContentsImpl> weak_this = weak_factory_.GetWeakPtr();
    ExitFullscreen(false);

    // If `this` gets destructed due to ExitFullscreen(), we need to exit early.
    if (!weak_this) {
      return;
    }
    CancelKeyboardLock(keyboard_lock_widget_);
  }
}

void WebContentsImpl::DidNavigateMainFramePostCommit(
    RenderFrameHostImpl* render_frame_host,
    const LoadCommittedDetails& details) {
  // The render_frame_host is always a main frame.
  DCHECK(render_frame_host->is_main_frame());
  OPTIONAL_TRACE_EVENT1("content,navigation",
                        "WebContentsImpl::DidNavigateMainFramePostCommit",
                        "render_frame_host", render_frame_host);
  const bool is_primary_main_frame = render_frame_host->IsInPrimaryMainFrame();

  auto* rwhvb = static_cast<RenderWidgetHostViewBase*>(
      render_frame_host->GetMainFrame()->GetView());

  if (details.is_navigation_to_different_page()) {
    if (is_primary_main_frame) {
      // Clear the status bubble. This is a workaround for a bug where WebKit
      // doesn't let us know that the cursor left an element during a
      // transition (this is also why the mouse cursor remains as a hand after
      // clicking on a link); see bugs 1184641 and 980803. We don't want to
      // clear the bubble when a user navigates to a named anchor in the same
      // page.
      ClearTargetURL();

      // Run the post-commit tasks on the new View.
      if (rwhvb) {
        rwhvb->OnNewViewDidNavigatePostCommit();
      }
    }
  }

  PageImpl& page = render_frame_host->GetPage();
  if (page.IsPrimary()) {
    // The following events will not fire again if the this is a back-forward
    // cache restore or prerendering activation. Fire them ourselves if needed.
    if (details.is_navigation_to_different_page() &&
        page.did_first_visually_non_empty_paint()) {
      OnFirstVisuallyNonEmptyPaint(page);
    }
    OnThemeColorChanged(page);
    OnBackgroundColorChanged(page);
    DidInferColorScheme(page);
    AXTreeIDForMainFrameHasChanged();
  }

  if (is_primary_main_frame) {
    TRACE_EVENT_END("content", tracing_track_->track());
    EmitTracingSlice(base::UTF16ToUTF8(GetTitle()));
  }
}

void WebContentsImpl::DidNavigateAnyFramePostCommit(
    RenderFrameHostImpl* render_frame_host,
    const LoadCommittedDetails& details) {
  OPTIONAL_TRACE_EVENT1("content,navigation",
                        "WebContentsImpl::DidNavigateAnyFramePostCommit",
                        "render_frame_host", render_frame_host);

  // This function can be called by prerendered frames or other inactive frames,
  // we want to guard the dialog cancellations below.
  const bool is_active = render_frame_host->IsActive();

  // If we navigate off the active non-fenced frame page, close all JavaScript
  // dialogs. We do not cancel the dialogs for fenced frames because it can be
  // used as a communication channel. Please see also:
  // RenderFrameHostManager::UnloadOldFrame in
  // content/browser/renderer_host/render_frame_host_manager.cc
  //
  // TODO(crbug.com/40215909): Note that fenced frames cannot open modal dialogs
  // so this only affects dialogs outside the fenced frame tree. If this is ever
  // changed then the navigation should be deferred till the dialog from within
  // the fenced frame is open.
  if (is_active && !render_frame_host->IsNestedWithinFencedFrame() &&
      !details.is_same_document) {
    CancelActiveAndPendingDialogs();
  }

  // If this is a user-initiated navigation, start allowing JavaScript dialogs
  // again.
  //
  // TODO(crbug.com/40249773): Consider using the actual value of
  // "whether a navigation started with user activation or not" instead of
  // has_user_gesture, which might get filtered out when navigating from
  // proxies. If so, we can remove tracking of
  // `last_committed_common_params_has_user_gesture` entirely.
  if (render_frame_host->last_committed_common_params_has_user_gesture()) {
    DCHECK(is_active);
    CancelDialogManagerDialogs(/*reset_state=*/true);
  }
}

void WebContentsImpl::DidUpdateNavigationHandleTiming(
    NavigationHandle* navigation_handle) {
  SCOPED_UMA_HISTOGRAM_TIMER(
      "WebContentsObserver.DidUpdateNavigationHandleTiming");
  observers_.NotifyObservers(
      &WebContentsObserver::DidUpdateNavigationHandleTiming, navigation_handle);
}

bool WebContentsImpl::CanOverscrollContent() const {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::CanOverscrollContent");
  // Disable overscroll when touch emulation is on. See crbug.com/369938.
  if (force_disable_overscroll_content_) {
    return false;
  }
  return delegate_ && delegate_->CanOverscrollContent();
}

void WebContentsImpl::OnThemeColorChanged(PageImpl& page) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnThemeColorChanged",
                        "page", page);
  if (!page.IsPrimary()) {
    return;
  }

  if (page.did_first_visually_non_empty_paint() &&
      last_sent_theme_color_ != page.theme_color()) {
    observers_.NotifyObservers(&WebContentsObserver::DidChangeThemeColor);
    last_sent_theme_color_ = page.theme_color();
  }
}

void WebContentsImpl::OnBackgroundColorChanged(PageImpl& page) {
  if (!page.IsPrimary()) {
    return;
  }

  if (page.did_first_visually_non_empty_paint() &&
      last_sent_background_color_ != page.background_color()) {
    observers_.NotifyObservers(&WebContentsObserver::OnBackgroundColorChanged);
    last_sent_background_color_ = page.background_color();
    return;
  }

  if (page.background_color().has_value()) {
    if (auto* view = GetRenderWidgetHostView()) {
      static_cast<RenderWidgetHostViewBase*>(view)->SetContentBackgroundColor(
          page.background_color().value());
    }
  }
}

void WebContentsImpl::DidInferColorScheme(PageImpl& page) {
  // If the page is primary, notify embedders that the current inferred color
  // scheme for this WebContents has changed.
  if (page.IsPrimary()) {
    observers_.NotifyObservers(&WebContentsObserver::InferredColorSchemeUpdated,
                               page.inferred_color_scheme());
  }
}

void WebContentsImpl::OnVirtualKeyboardModeChanged(PageImpl& page) {
  if (!page.IsPrimary()) {
    return;
  }

  observers_.NotifyObservers(&WebContentsObserver::VirtualKeyboardModeChanged,
                             page.virtual_keyboard_mode());
}

void WebContentsImpl::DidLoadResourceFromMemoryCache(
    RenderFrameHostImpl* source,
    const GURL& url,
    const std::string& http_method,
    const std::string& mime_type,
    network::mojom::RequestDestination request_destination,
    bool include_credentials) {
  OPTIONAL_TRACE_EVENT2("content",
                        "WebContentsImpl::DidLoadResourceFromMemoryCache",
                        "render_frame_host", source, "url", url);
  observers_.NotifyObservers(
      &WebContentsObserver::DidLoadResourceFromMemoryCache, source, url,
      mime_type, request_destination);

  if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) {
    return;
  }

  StoragePartition* partition = source->GetProcess()->GetStoragePartition();

  // This method should only be called for resource loads (not navigations), so
  // CHECK that here using `request_destination`. Note that
  // `network::mojom::RequestDestination::kObject` and
  // `network::mojom::RequestDestination::kEmbed` can correspond to navigations
  // (see `blink::IsRequestDestinationFrame()`) but can also correspond to
  // resource loads, so exclude those from the CHECK.
  CHECK(request_destination != network::mojom::RequestDestination::kDocument);
  CHECK(!network::IsRequestDestinationEmbeddedFrame(request_destination));

  partition->GetNetworkContext()->NotifyExternalCacheHit(
      url, http_method, source->GetNetworkIsolationKey(),
      /*include_credentials=*/include_credentials);
}

void WebContentsImpl::PrimaryMainDocumentElementAvailable() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::PrimaryMainDocumentElementAvailable");
  SCOPED_UMA_HISTOGRAM_TIMER(
      "WebContentsObserver.PrimaryMainDocumentElementAvailable");
  observers_.NotifyObservers(
      &WebContentsObserver::PrimaryMainDocumentElementAvailable);
}

void WebContentsImpl::PassiveInsecureContentFound(const GURL& resource_url) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::PassiveInsecureContentFound",
                        "resource_url", resource_url);
  if (delegate_) {
    delegate_->PassiveInsecureContentFound(resource_url);
  }
}

bool WebContentsImpl::ShouldAllowRunningInsecureContent(
    bool allowed_per_prefs,
    const url::Origin& origin,
    const GURL& resource_url) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ShouldAllowRunningInsecureContent");
  if (delegate_) {
    return delegate_->ShouldAllowRunningInsecureContent(this, allowed_per_prefs,
                                                        origin, resource_url);
  }

  return allowed_per_prefs;
}

void WebContentsImpl::ViewSource(RenderFrameHostImpl* frame) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ViewSource",
                        "render_frame_host", frame);
  DCHECK_EQ(this, WebContents::FromRenderFrameHost(frame));

  // Don't do anything if there is no |delegate_| that could accept and show the
  // new WebContents containing the view-source.
  if (!delegate_) {
    return;
  }

  // Use the last committed entry, since the pending entry hasn't loaded yet and
  // won't be copied into the cloned tab.
  NavigationEntryImpl* last_committed_entry =
      frame->frame_tree()->controller().GetLastCommittedEntry();
  if (!last_committed_entry) {
    return;
  }

  FrameNavigationEntry* frame_entry =
      last_committed_entry->GetFrameEntry(frame->frame_tree_node());
  if (!frame_entry) {
    return;
  }

  // Any new WebContents opened while this WebContents is in fullscreen can be
  // used to confuse the user, so drop fullscreen.
  if (!ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId)) {
    return;
  }

  // We intentionally don't share the SiteInstance with the original frame so
  // that view source has a consistent process model and always ends up in a new
  // process (https://crbug.com/699493).
  scoped_refptr<SiteInstanceImpl> site_instance_for_view_source;
  // Referrer and initiator are not important, because view-source should not
  // hit the network, but should be served from the cache instead.
  Referrer referrer_for_view_source;
  std::optional<url::Origin> initiator_for_view_source = std::nullopt;
  std::optional<GURL> initiator_base_url_for_view_source = std::nullopt;
  // Do not restore title, derive it from the url.
  std::u16string title_for_view_source;
  auto navigation_entry = std::make_unique<NavigationEntryImpl>(
      site_instance_for_view_source, frame_entry->url(),
      referrer_for_view_source, initiator_for_view_source,
      initiator_base_url_for_view_source, title_for_view_source,
      ui::PAGE_TRANSITION_LINK,
      /* is_renderer_initiated = */ false,
      /* blob_url_loader_factory = */ nullptr, /* is_initial_entry = */ false);
  const GURL url(content::kViewSourceScheme + std::string(":") +
                 frame_entry->url().spec());
  navigation_entry->SetVirtualURL(url);

  // View source opens the URL in a new tab as a top-level navigation. A
  // top-level navigation may have a different IsolationInfo than the source
  // iframe, so preserve the IsolationInfo from the origin frame, to use the
  // same network shard and increase chances of a cache hit.
  navigation_entry->set_isolation_info(
      frame->ComputeIsolationInfoForNavigation(navigation_entry->GetURL()));

  // Do not restore scroller position.
  // TODO(creis, lukasza, arthursonzogni): Do not reuse the original PageState,
  // but start from a new one and only copy the needed data.
  const blink::PageState& new_page_state =
      frame_entry->page_state().RemoveScrollOffset();

  scoped_refptr<FrameNavigationEntry> new_frame_entry =
      navigation_entry->root_node()->frame_entry;
  new_frame_entry->set_method(frame_entry->method());
  new_frame_entry->SetPageState(new_page_state);

  // Create a new WebContents, which is used to display the source code.
  std::unique_ptr<WebContents> view_source_contents =
      Create(CreateParams(GetBrowserContext()));

  // Restore the previously created NavigationEntry.
  std::vector<std::unique_ptr<NavigationEntry>> navigation_entries;
  navigation_entries.push_back(std::move(navigation_entry));
  view_source_contents->GetController().Restore(0, RestoreType::kRestored,
                                                &navigation_entries);

  // Add |view_source_contents| as a new tab.
  constexpr bool kUserGesture = true;
  bool ignored_was_blocked;
  delegate_->AddNewContents(this, std::move(view_source_contents), url,
                            WindowOpenDisposition::NEW_FOREGROUND_TAB,
                            blink::mojom::WindowFeatures(), kUserGesture,
                            &ignored_was_blocked);
  // Note that the |delegate_| could have deleted |view_source_contents| during
  // AddNewContents method call.
}

void WebContentsImpl::ResourceLoadComplete(
    RenderFrameHostImpl* render_frame_host,
    const GlobalRequestID& request_id,
    const GURL& original_url,
    blink::mojom::ResourceLoadInfoPtr resource_load_info) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::ResourceLoadComplete",
                        "render_frame_host", render_frame_host, "request_id",
                        request_id);
  const blink::mojom::ResourceLoadInfo& resource_load_info_ref =
      *resource_load_info;
  observers_.NotifyObservers(&WebContentsObserver::ResourceLoadComplete,
                             render_frame_host, request_id, original_url,
                             resource_load_info_ref);
}

const blink::web_pref::WebPreferences&
WebContentsImpl::GetOrCreateWebPreferences() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::GetOrCreateWebPreferences");
  // Compute WebPreferences based on the current state if it's null.
  if (!web_preferences_) {
    OnWebPreferencesChanged();
  }

  CHECK(web_preferences_)
      << "WebPreferences is not created because GetOrCreateWebPreferences() "
      << "is called before OnWebPreferencesChanged() returns.";
  return *web_preferences_.get();
}

void WebContentsImpl::SetWebPreferences(
    const blink::web_pref::WebPreferences& prefs) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetWebPreferences");
  web_preferences_ = std::make_unique<blink::web_pref::WebPreferences>(prefs);
  // Get all the RenderViewHosts (except the ones for currently back-forward
  // cached pages), and make them send the current WebPreferences
  // to the renderer. WebPreferences updates for back-forward cached pages will
  // be sent when we restore those pages from the back-forward cache.
  primary_frame_tree_.ForEachRenderViewHost(
      [](RenderViewHostImpl* rvh) { rvh->SendWebPreferencesToRenderer(); });
}

std::optional<SkColor> WebContentsImpl::GetBaseBackgroundColor() {
  return page_base_background_color_;
}

blink::ColorProviderColorMaps WebContentsImpl::GetColorProviderColorMaps()
    const {
  const auto* color_mode_source = GetColorProviderSource();

  // Unlike preferred color scheme, ForcedColors should always use the
  // default color provider source, which reflects the NativeTheme web instance.
  // This is because the Page colors feature only modifies the Forced colors
  // mode for web without affecting the UI.
  const auto* forced_colors_source = DefaultColorProviderSource::GetInstance();
  ui::ColorProviderKey::ForcedColors forced_colors =
      forced_colors_source->GetForcedColors();
  if (forced_colors == ui::ColorProviderKey::ForcedColors::kNone) {
    forced_colors = ui::ColorProviderKey::ForcedColors::kSystem;
  }

  return blink::ColorProviderColorMaps{
      color_mode_source->GetRendererColorMap(
          ui::ColorProviderKey::ColorMode::kLight,
          ui::ColorProviderKey::ForcedColors::kNone),
      color_mode_source->GetRendererColorMap(
          ui::ColorProviderKey::ColorMode::kDark,
          ui::ColorProviderKey::ForcedColors::kNone),
      forced_colors_source->GetRendererColorMap(
          forced_colors_source->GetColorMode(), forced_colors)};
}

void WebContentsImpl::PrintCrossProcessSubframe(
    const gfx::Rect& rect,
    int document_cookie,
    RenderFrameHostImpl* subframe_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::PrintCrossProcessSubframe",
                        "subframe", subframe_host);
  auto* outer_contents = GetOuterWebContents();
  if (outer_contents) {
    // When an extension or app page is printed, the content should be
    // composited with outer content, so the outer contents should handle the
    // print request.
    outer_contents->PrintCrossProcessSubframe(rect, document_cookie,
                                              subframe_host);
    return;
  }

  // If there is no delegate such as in tests or during deletion, do nothing.
  if (!delegate_) {
    return;
  }

  delegate_->PrintCrossProcessSubframe(this, rect, document_cookie,
                                       subframe_host);
}

void WebContentsImpl::CapturePaintPreviewOfCrossProcessSubframe(
    const gfx::Rect& rect,
    const base::UnguessableToken& guid,
    RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::CapturePaintPreviewOfCrossProcessSubframe",
      "render_frame_host", render_frame_host);
  if (!delegate_) {
    return;
  }
  delegate_->CapturePaintPreviewOfSubframe(this, rect, guid, render_frame_host);
}

#if BUILDFLAG(IS_ANDROID)
base::android::ScopedJavaLocalRef<jobject>
WebContentsImpl::GetJavaRenderFrameHostDelegate() {
  return GetJavaWebContents();
}
#endif

void WebContentsImpl::DOMContentLoaded(RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DOMContentLoaded",
                        "render_frame_host", render_frame_host);
  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DOMContentLoaded");
  observers_.NotifyObservers(&WebContentsObserver::DOMContentLoaded,
                             render_frame_host);
}

void WebContentsImpl::OnDidFinishLoad(RenderFrameHostImpl* render_frame_host,
                                      const GURL& url) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnDidFinishLoad",
                        "render_frame_host", render_frame_host, "url", url);
  GURL validated_url(url);
  render_frame_host->GetProcess()->FilterURL(false, &validated_url);

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DidFinishLoad");
    observers_.NotifyObservers(&WebContentsObserver::DidFinishLoad,
                               render_frame_host, validated_url);
  }
  size_t tree_size = GetFrameTreeSize(&primary_frame_tree_);
  if (max_loaded_frame_count_ < tree_size) {
    max_loaded_frame_count_ = tree_size;
  }

  if (!render_frame_host->GetParentOrOuterDocument()) {
    UMA_HISTOGRAM_COUNTS_1000("Navigation.MainFrame.FrameCount", tree_size);
  }
}

bool WebContentsImpl::IsAllowedToGoToEntryAtOffset(int32_t offset) {
  // TODO(crbug.com/40165695): This should probably be renamed to
  // WebContentsDelegate::IsAllowedToGoToEntryAtOffset or
  // ShouldGoToEntryAtOffset
  return !delegate_ || delegate_->OnGoToEntryOffset(offset);
}

void WebContentsImpl::OnPageScaleFactorChanged(PageImpl& source) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnPageScaleFactorChanged",
                        "source", source);

  if (source.IsPrimary()) {
    observers_.NotifyObservers(&WebContentsObserver::OnPageScaleFactorChanged,
                               source.GetPageScaleFactor());
  }
}

void WebContentsImpl::EnumerateDirectory(
    base::WeakPtr<FileChooserImpl> file_chooser,
    RenderFrameHost* render_frame_host,
    scoped_refptr<FileChooserImpl::FileSelectListenerImpl> listener,
    const base::FilePath& directory_path) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::EnumerateDirectory",
                        "render_frame_host", render_frame_host,
                        "directory_path", directory_path);
  absl::Cleanup cancel_chooser = [&listener] {
    listener->FileSelectionCanceled();
  };
  if (visibility_ == Visibility::HIDDEN) {
    // Do not allow background tab to open file chooser.
    return;
  }
  if (!delegate_->IsContentsActive(this)) {
    // Do not allow inactive tabs to open file chooser.
    return;
  }
  if (active_file_chooser_) {
    // Only allow one active file chooser at one time.
    return;
  }

  // Any explicit focusing of another window while this WebContents is in
  // fullscreen can be used to confuse the user, so drop fullscreen.
  auto blocker =
      ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId);
  if (!blocker) {
    return;
  }
  listener->SetFullscreenBlock(std::move(*blocker));

  if (delegate_) {
    active_file_chooser_ = std::move(file_chooser);
    delegate_->EnumerateDirectory(this, std::move(listener), directory_path);
    std::move(cancel_chooser).Cancel();
  }
}

void WebContentsImpl::RegisterProtocolHandler(RenderFrameHostImpl* source,
                                              const std::string& protocol,
                                              const GURL& url,
                                              bool user_gesture) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::RegisterProtocolHandler",
                        "render_frame_host", source, "protocol", protocol);
  // TODO(nick): Do we need to apply FilterURL to |url|?
  if (!delegate_) {
    return;
  }

  blink::ProtocolHandlerSecurityLevel security_level =
      delegate_->GetProtocolHandlerSecurityLevel(source);

  // Run the protocol handler arguments normalization process defined in the
  // spec.
  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
  if (!AreValidRegisterProtocolHandlerArguments(
          protocol, url, source->GetLastCommittedOrigin(), security_level)) {
    ReceivedBadMessage(source->GetProcess(),
                       bad_message::REGISTER_PROTOCOL_HANDLER_INVALID_URL);
    return;
  }

  delegate_->RegisterProtocolHandler(source, protocol, url, user_gesture);
}

void WebContentsImpl::UnregisterProtocolHandler(RenderFrameHostImpl* source,
                                                const std::string& protocol,
                                                const GURL& url,
                                                bool user_gesture) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::UnregisterProtocolHandler",
                        "render_frame_host", source, "protocol", protocol);
  // TODO(nick): Do we need to apply FilterURL to |url|?
  if (!delegate_) {
    return;
  }

  blink::ProtocolHandlerSecurityLevel security_level =
      delegate_->GetProtocolHandlerSecurityLevel(source);

  if (!AreValidRegisterProtocolHandlerArguments(
          protocol, url, source->GetLastCommittedOrigin(), security_level)) {
    ReceivedBadMessage(source->GetProcess(),
                       bad_message::REGISTER_PROTOCOL_HANDLER_INVALID_URL);
    return;
  }

  delegate_->UnregisterProtocolHandler(source, protocol, url, user_gesture);
}

base::ScopedClosureRunner WebContentsImpl::MarkAudible() {
  auto audible_client = audio_stream_monitor_.RegisterAudibleClient(
      GetPrimaryMainFrame()->GetGlobalId());
  return base::ScopedClosureRunner(
      base::DoNothingWithBoundArgs(std::move(audible_client)));
}

void WebContentsImpl::DomOperationResponse(RenderFrameHost* render_frame_host,
                                           const std::string& json_string) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::DomOperationResponse",
                        "render_frame_host", render_frame_host, "json_string",
                        json_string);

  observers_.NotifyObservers(&WebContentsObserver::DomOperationResponse,
                             render_frame_host, json_string);
}

void WebContentsImpl::SavableResourceLinksResponse(
    RenderFrameHostImpl* source,
    const std::vector<GURL>& resources_list,
    blink::mojom::ReferrerPtr referrer,
    const std::vector<blink::mojom::SavableSubframePtr>& subframes) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SavableResourceLinksResponse",
                        "render_frame_host", source);
  if (save_package_) {
    save_package_->SavableResourceLinksResponse(source, resources_list,
                                                std::move(referrer), subframes);
  }
}

void WebContentsImpl::SavableResourceLinksError(RenderFrameHostImpl* source) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SavableResourceLinksError",
                        "render_frame_host", source);
  if (save_package_) {
    save_package_->SavableResourceLinksError(source);
  }
}

void WebContentsImpl::OnServiceWorkerAccessed(
    RenderFrameHost* render_frame_host,
    const GURL& scope,
    AllowServiceWorkerResult allowed) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnServiceWorkerAccessed",
                        "render_frame_host", render_frame_host, "scope", scope);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(RenderFrameHost*, const GURL&,
                                    AllowServiceWorkerResult) =
      &WebContentsObserver::OnServiceWorkerAccessed;
  observers_.NotifyObservers(func, render_frame_host, scope, allowed);
}

void WebContentsImpl::OnServiceWorkerAccessed(
    NavigationHandle* navigation,
    const GURL& scope,
    AllowServiceWorkerResult allowed) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnServiceWorkerAccessed",
                        "navigation_handle", navigation, "scope", scope);
  // Use a variable to select between overloads.
  void (WebContentsObserver::*func)(NavigationHandle*, const GURL&,
                                    AllowServiceWorkerResult) =
      &WebContentsObserver::OnServiceWorkerAccessed;
  observers_.NotifyObservers(func, navigation, scope, allowed);
}

void WebContentsImpl::OnColorChooserFactoryReceiver(
    mojo::PendingReceiver<blink::mojom::ColorChooserFactory> receiver) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::OnColorChooserFactoryReceiver");
  color_chooser_factory_receivers_.Add(this, std::move(receiver));
}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
void WebContentsImpl::OpenColorChooser(
    mojo::PendingReceiver<blink::mojom::ColorChooser> chooser_receiver,
    mojo::PendingRemote<blink::mojom::ColorChooserClient> client,
    SkColor color,
    std::vector<blink::mojom::ColorSuggestionPtr> suggestions) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OpenColorChooser");
  // Create `color_chooser_holder_` before calling OpenColorChooser since
  // OpenColorChooser may callback with results.
  color_chooser_holder_.reset();
  color_chooser_holder_ = std::make_unique<ColorChooserHolder>(
      std::move(chooser_receiver), std::move(client));

  auto new_color_chooser =
      delegate_ ? delegate_->OpenColorChooser(this, color, suggestions)
                : nullptr;
  if (color_chooser_holder_ && new_color_chooser) {
    color_chooser_holder_->SetChooser(std::move(new_color_chooser));
  } else if (new_color_chooser) {
    // OpenColorChooser synchronously called back to DidEndColorChooser.
    DCHECK(!color_chooser_holder_);
    new_color_chooser->End();
  } else if (color_chooser_holder_) {
    DCHECK(!new_color_chooser);
    color_chooser_holder_.reset();
  }
}
#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)

void WebContentsImpl::UpdateFaviconURL(
    RenderFrameHostImpl* source,
    const std::vector<blink::mojom::FaviconURLPtr>& candidates,
    blink::mojom::FaviconUpdateReason reason) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::UpdateFaviconURL",
                        "render_frame_host", source);
  // We get updated favicon URLs after the page stops loading. If a cross-site
  // navigation occurs while a page is still loading, the initial page
  // may stop loading and send us updated favicon URLs after the navigation
  // for the new page has committed.
  if (!source->IsInPrimaryMainFrame()) {
    return;
  }

  observers_.NotifyObservers(&WebContentsObserver::DidUpdateFaviconURL, source,
                             candidates, reason);
}

void WebContentsImpl::SetIsOverlayContent(bool is_overlay_content) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetIsOverlayContent",
                        "is_overlay_content", is_overlay_content);
  is_overlay_content_ = is_overlay_content;
}

void WebContentsImpl::OnFirstVisuallyNonEmptyPaint(PageImpl& page) {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::OnFirstVisuallyNonEmptyPaint", "page", page);
  if (!page.IsPrimary()) {
    return;
  }

  {
    SCOPED_UMA_HISTOGRAM_TIMER(
        "WebContentsObserver.DidFirstVisuallyNonEmptyPaint");
    observers_.NotifyObservers(
        &WebContentsObserver::DidFirstVisuallyNonEmptyPaint);
  }
  if (page.theme_color() != last_sent_theme_color_) {
    // Theme color should have updated by now if there was one.
    observers_.NotifyObservers(&WebContentsObserver::DidChangeThemeColor);
    last_sent_theme_color_ = GetPrimaryPage().theme_color();
  }

  if (page.background_color() != last_sent_background_color_) {
    // Background color should have updated by now if there was one.
    observers_.NotifyObservers(&WebContentsObserver::OnBackgroundColorChanged);
    last_sent_background_color_ = page.background_color();
  }
#if BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(
          features::kAndroidWarmUpSpareRendererWithTimeout) &&
      features::kAndroidSpareRendererCreationTiming.Get() ==
          features::kAndroidSpareRendererCreationAfterFirstPaint) {
    WarmUpAndroidSpareRenderer();
  }
#endif
}

bool WebContentsImpl::IsGuest() {
  return !!browser_plugin_guest_;
}

void WebContentsImpl::NotifyBeforeFormRepostWarningShow() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyBeforeFormRepostWarningShow");
  observers_.NotifyObservers(&WebContentsObserver::BeforeFormRepostWarningShow);
}

void WebContentsImpl::ActivateAndShowRepostFormWarningDialog() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::ActivateAndShowRepostFormWarningDialog");
  Activate();
  if (delegate_) {
    delegate_->ShowRepostFormWarningDialog(this);
  }
}

bool WebContentsImpl::HasAccessedInitialDocument() {
  return GetPrimaryFrameTree().has_accessed_initial_main_document();
}

void WebContentsImpl::UpdateTitleForEntry(NavigationEntry* entry,
                                          const std::u16string& title) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::UpdateTitleForEntry",
                        "title", title);
  NavigationEntryImpl* entry_impl =
      NavigationEntryImpl::FromNavigationEntry(entry);
  bool title_changed = UpdateTitleForEntryImpl(entry_impl, title);
  if (title_changed) {
    NotifyTitleUpdateForEntry(entry_impl);
  }
}

bool WebContentsImpl::UpdateTitleForEntryImpl(NavigationEntryImpl* entry,
                                              const std::u16string& title) {
  DCHECK(entry);
  std::u16string final_title;
  base::TrimWhitespace(title, base::TRIM_ALL, &final_title);

  if (final_title == entry->GetTitle()) {
    return false;  // Nothing changed, don't bother.
  }

  entry->SetTitle(std::move(final_title));
  return true;
}

void WebContentsImpl::NotifyTitleUpdateForEntry(NavigationEntryImpl* entry) {
  // |entry| must belong to the primary frame tree's NavigationController.
  DCHECK(GetController().GetEntryWithUniqueIDIncludingPending(
      entry->GetUniqueID()));
  std::u16string final_title = entry->GetTitleForDisplay();
  bool did_web_contents_title_change = entry == GetNavigationEntryForTitle();
  if (did_web_contents_title_change) {
    view_->SetPageTitle(final_title);
  }

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.TitleWasSet");
    observers_.NotifyObservers(&WebContentsObserver::TitleWasSet, entry);
  }

  if (did_web_contents_title_change) {
    NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
  }
}

NavigationEntry* WebContentsImpl::GetNavigationEntryForTitle() {
  // We use the title for the last committed entry rather than a pending
  // navigation entry. For example, when the user types in a URL, we want to
  // keep the old page's title until the new load has committed and we get a new
  // title.
  NavigationEntry* entry = GetController().GetLastCommittedEntry();

  // We make an exception for initial navigations. We only want to use the title
  // from the visible entry if:
  // 1. The pending entry has been explicitly assigned a title to display.
  // 2. The user is doing a history navigation in a new tab (e.g., Ctrl+Back),
  //    which case there is a pending entry index other than -1.
  //
  // Otherwise, we want to stick with the last committed entry's title during
  // new navigations, which have pending entries at index -1 with no title.
  if (GetController().IsInitialNavigation() &&
      ((GetController().GetVisibleEntry() &&
        !GetController().GetVisibleEntry()->GetTitle().empty()) ||
       GetController().GetPendingEntryIndex() != -1)) {
    entry = GetController().GetVisibleEntry();
  }

  return entry;
}

void WebContentsImpl::SendChangeLoadProgress() {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SendChangeLoadProgress",
                        "load_progress", GetLoadProgress());
  loading_last_progress_update_ = base::TimeTicks::Now();

  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.LoadProgressChanged");
  observers_.NotifyObservers(&WebContentsObserver::LoadProgressChanged,
                             GetLoadProgress());
}

void WebContentsImpl::ResetLoadProgressState() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ResetLoadProgressState");
  GetPrimaryPage().set_load_progress(0.0);
  loading_weak_factory_.InvalidateWeakPtrs();
  loading_last_progress_update_ = base::TimeTicks();
}

// Notifies the RenderWidgetHost instance about the fact that the page is
// loading, or done loading.
void WebContentsImpl::LoadingStateChanged(LoadingState new_state) {
  if (IsBeingDestroyed()) {
    return;
  }

  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::LoadingStateChanged",
                        "loading_state", new_state);

  if (new_state == LoadingState::NONE) {
    load_state_ =
        net::LoadStateWithParam(net::LOAD_STATE_IDLE, std::u16string());
    load_state_host_.clear();
    upload_size_ = 0;
    upload_position_ = 0;
  }

  if (delegate_) {
    delegate_->LoadingStateChanged(
        this, new_state == LoadingState::LOADING_UI_REQUESTED);
  }
  NotifyNavigationStateChanged(INVALIDATE_TYPE_LOAD);
}

void WebContentsImpl::NotifyViewSwapped(RenderViewHost* old_view,
                                        RenderViewHost* new_view) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::NotifyViewSwapped",
                        "old_view", old_view, "new_view", new_view);
  DCHECK_NE(old_view, new_view);
  // After sending out a swap notification, we need to send a disconnect
  // notification so that clients that pick up a pointer to |this| can NULL the
  // pointer.  See Bug 1230284.
  notify_disconnection_ = true;
  observers_.NotifyObservers(&WebContentsObserver::RenderViewHostChanged,
                             old_view, new_view);
  view_->RenderViewHostChanged(old_view, new_view);

  // If this is an inner WebContents that has swapped views, we need to reattach
  // it to its outer WebContents.
  if (node_.outer_web_contents()) {
    ReattachToOuterWebContentsFrame();
  }

  // Ensure that the associated embedder gets cleared after a RenderViewHost
  // gets swapped, so we don't reuse the same embedder next time a
  // RenderViewHost is attached to this WebContents.
  RemoveBrowserPluginEmbedder();
}

void WebContentsImpl::NotifyFrameSwapped(RenderFrameHostImpl* old_frame,
                                         RenderFrameHostImpl* new_frame) {
  TRACE_EVENT2("content", "WebContentsImpl::NotifyFrameSwapped", "old_frame",
               old_frame, "new_frame", new_frame);
#if BUILDFLAG(IS_ANDROID)
  // Copy importance from |old_frame| if |new_frame| is a main frame.
  if (old_frame && !new_frame->GetParent()) {
    RenderWidgetHostImpl* old_widget = old_frame->GetRenderWidgetHost();
    RenderWidgetHostImpl* new_widget = new_frame->GetRenderWidgetHost();
    new_widget->SetImportance(old_widget->importance());
  }
#endif
  observers_.NotifyObservers(&WebContentsObserver::RenderFrameHostChanged,
                             old_frame, new_frame);
}

void WebContentsImpl::NotifyNavigationEntryCommitted(
    const LoadCommittedDetails& load_details) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyNavigationEntryCommitted");
  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.NavigationEntryCommitted");
  observers_.NotifyObservers(&WebContentsObserver::NavigationEntryCommitted,
                             load_details);
}

void WebContentsImpl::NotifyNavigationEntryChanged(
    const EntryChangedDetails& change_details) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyNavigationEntryChanged");
  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.NavigationEntryChanged");
  observers_.NotifyObservers(&WebContentsObserver::NavigationEntryChanged,
                             change_details);
}

void WebContentsImpl::NotifyNavigationListPruned(
    const PrunedDetails& pruned_details) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyNavigationListPruned");
  observers_.NotifyObservers(&WebContentsObserver::NavigationListPruned,
                             pruned_details);
}

void WebContentsImpl::NotifyNavigationEntriesDeleted() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyNavigationEntriesDeleted");
  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.NavigationEntryDeleted");
  observers_.NotifyObservers(&WebContentsObserver::NavigationEntriesDeleted);
}

void WebContentsImpl::OnDidBlockNavigation(
    const GURL& blocked_url,
    const GURL& initiator_url,
    const url::Origin& initiator_origin,
    blink::mojom::NavigationBlockedReason reason) {
  OPTIONAL_TRACE_EVENT("content", "WebContentsImpl::OnDidBlockNavigation",
                       "blocked_url", blocked_url, "initiator_url",
                       initiator_url, "reason", reason);
  if (delegate_) {
    delegate_->OnDidBlockNavigation(this, blocked_url, initiator_url,
                                    initiator_origin, reason);
  }
}

void WebContentsImpl::RenderFrameCreated(
    RenderFrameHostImpl* render_frame_host) {
  TRACE_EVENT1("content", "WebContentsImpl::RenderFrameCreated",
               "render_frame_host", render_frame_host);

  if (render_frame_host->IsInPrimaryMainFrame()) {
    NotifyPrimaryMainFrameProcessIsAlive();
  }

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.RenderFrameCreated");
    observers_.NotifyObservers(&WebContentsObserver::RenderFrameCreated,
                               render_frame_host);
  }
  render_frame_host->UpdateAccessibilityMode();

  if (safe_area_insets_host_) {
    safe_area_insets_host_->RenderFrameCreated(render_frame_host);
  }

#if BUILDFLAG(IS_ANDROID)
  if (render_frame_host->GetParent() &&
      render_frame_host->frame_tree()->is_primary()) {
    if (auto* rwh = render_frame_host->GetLocalRenderWidgetHost()) {
      rwh->SetImportance(primary_subframe_importance_);
    }
  }
#endif
}

void WebContentsImpl::RenderFrameDeleted(
    RenderFrameHostImpl* render_frame_host) {
  TRACE_EVENT1("content", "WebContentsImpl::RenderFrameDeleted",
               "render_frame_host", render_frame_host);
  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.RenderFrameDeleted");
    observers_.NotifyObservers(&WebContentsObserver::RenderFrameDeleted,
                               render_frame_host);
  }

  if (safe_area_insets_host_) {
    safe_area_insets_host_->RenderFrameDeleted(render_frame_host);
  }

  // Remove any fullscreen state that the frame has stored.
  FullscreenStateChanged(render_frame_host, false /* is_fullscreen */,
                         blink::mojom::FullscreenOptionsPtr());
}

void WebContentsImpl::ShowContextMenu(
    RenderFrameHostImpl& render_frame_host,
    mojo::PendingAssociatedRemote<blink::mojom::ContextMenuClient>
        context_menu_client,
    const ContextMenuParams& params) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ShowContextMenu",
                        "render_frame_host", render_frame_host);
  // If a renderer fires off a second command to show a context menu before the
  // first context menu is closed, just ignore it. https://crbug.com/707534
  if (showing_context_menu_) {
    return;
  }

  if (context_menu_client) {
    context_menu_client_.reset();
    context_menu_client_.Bind(std::move(context_menu_client));
  }

  ContextMenuParams context_menu_params(params);

  // Give guest a first crack at the context menu.
  if (GuestPageHolderImpl* guest_holder =
          GuestPageHolderImpl::FromRenderFrameHost(render_frame_host)) {
    if (auto* guest_delegate = guest_holder->delegate()) {
      if (guest_delegate->GuestHandleContextMenu(render_frame_host,
                                                 context_menu_params)) {
        return;
      }
    }
  }

  // Allow WebContentsDelegates to handle the context menu operation first.
  if (delegate_ &&
      delegate_->HandleContextMenu(render_frame_host, context_menu_params)) {
    return;
  }

  render_view_host_delegate_view_->ShowContextMenu(render_frame_host,
                                                   context_menu_params);
}

namespace {
// Normalizes the line endings: \r\n -> \n, lone \r -> \n.
std::u16string NormalizeLineBreaks(const std::u16string& source) {
  static const base::NoDestructor<std::u16string> kReturnNewline(u"\r\n");
  static const base::NoDestructor<std::u16string> kReturn(u"\r");
  static const base::NoDestructor<std::u16string> kNewline(u"\n");

  std::vector<std::u16string_view> pieces;

  for (const auto& rn_line : base::SplitStringPieceUsingSubstr(
           source, *kReturnNewline, base::KEEP_WHITESPACE,
           base::SPLIT_WANT_ALL)) {
    auto r_lines = base::SplitStringPieceUsingSubstr(
        rn_line, *kReturn, base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
    std::move(std::begin(r_lines), std::end(r_lines),
              std::back_inserter(pieces));
  }

  return base::JoinString(pieces, *kNewline);
}
}  // namespace

void WebContentsImpl::RunJavaScriptDialog(
    RenderFrameHostImpl* render_frame_host,
    const std::u16string& message,
    const std::u16string& default_prompt,
    JavaScriptDialogType dialog_type,
    bool disable_third_party_subframe_suppresion,
    JavaScriptDialogCallback response_callback) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RunJavaScriptDialog",
                        "render_frame_host", render_frame_host);
  DCHECK(render_frame_host->IsActive());

  // Ensure that if showing a dialog is the first thing that a page does, that
  // the contents of the previous page aren't shown behind it. This is required
  // because showing a dialog freezes the renderer, so no frames will be coming
  // from it. https://crbug.com/823353
  auto* render_widget_host_impl = render_frame_host->GetRenderWidgetHost();
  if (render_widget_host_impl) {
    render_widget_host_impl->ForceFirstFrameAfterNavigationTimeout();
  }

  // Running a dialog causes an exit to webpage-initiated fullscreen.
  // http://crbug.com/728276
  auto blocker =
      ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId);
  if (!blocker) {
    return;
  }

  auto callback = base::BindOnce(
      &WebContentsImpl::OnDialogClosed, weak_factory_.GetWeakPtr(),
      render_frame_host->GetProcess()->GetDeprecatedID(),
      render_frame_host->GetRoutingID(), std::move(response_callback),
      std::move(*blocker));

  std::vector<protocol::PageHandler*> page_handlers =
      protocol::PageHandler::EnabledForWebContents(this);

  bool should_suppress = false;
  JavaScriptDialogManager* dialog_manager = nullptr;
  // Give guest a first crack at the dialog.
  if (GuestPageHolderImpl* guest_holder =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    if (auto* guest_delegate = guest_holder->delegate()) {
      dialog_manager = guest_delegate->GuestGetJavascriptDialogManager();
    }
  } else if (delegate_) {
    dialog_manager = delegate_->GetJavaScriptDialogManager(this);
    should_suppress = delegate_->ShouldSuppressDialogs(this);
  }

  // While a JS message dialog is showing, defer commits in this WebContents.
  javascript_dialog_dismiss_notifier_ =
      std::make_unique<JavaScriptDialogDismissNotifier>();

  // Suppress JavaScript dialogs when requested.
  bool has_non_devtools_handlers = delegate_ && dialog_manager;
  bool has_handlers = page_handlers.size() || has_non_devtools_handlers;
  bool suppress_this_message = should_suppress || !has_handlers;

  if (!disable_third_party_subframe_suppresion &&
      GetContentClient()->browser()->SuppressDifferentOriginSubframeJSDialogs(
          GetBrowserContext())) {
    // We can't check for opaque origin cases, default to allowing them to
    // trigger dialogs.
    // TODO(carlosil): The main use case for opaque use cases are tests,
    // investigate if there are uses in the wild, otherwise adapt tests that
    // require dialogs so they commit an origin first, and remove this
    // conditional.
    if (!render_frame_host->GetLastCommittedOrigin().opaque()) {
      bool is_different_origin_subframe =
          render_frame_host->GetLastCommittedOrigin() !=
          render_frame_host->GetOutermostMainFrame()->GetLastCommittedOrigin();
      suppress_this_message |= is_different_origin_subframe;
      if (is_different_origin_subframe) {
        GetPrimaryMainFrame()->AddMessageToConsole(
            blink::mojom::ConsoleMessageLevel::kWarning,
            base::StringPrintf(
                "A different origin subframe tried to create a JavaScript "
                "dialog. This is no longer allowed and was blocked. See "
                "https://www.chromestatus.com/feature/5148698084376576 for "
                "more details."));
      }
    }
  }

  if (suppress_this_message) {
    std::move(callback).Run(true, false, std::u16string());
    return;
  }

  scoped_refptr<CloseDialogCallbackWrapper> wrapper =
      new CloseDialogCallbackWrapper(std::move(callback));

  is_showing_javascript_dialog_ = true;

  std::u16string normalized_message = NormalizeLineBreaks(message);

  for (auto* handler : page_handlers) {
    handler->DidRunJavaScriptDialog(
        render_frame_host->GetLastCommittedURL(),
        render_frame_host->devtools_frame_token(), normalized_message,
        default_prompt, dialog_type, has_non_devtools_handlers,
        base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false));
  }

  if (dialog_manager) {
    created_dialog_since_last_cancel_ = true;
    dialog_manager->RunJavaScriptDialog(
        this, render_frame_host, dialog_type, normalized_message,
        default_prompt,
        base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false),
        &suppress_this_message);
  }

  if (suppress_this_message) {
    // If we are suppressing messages, just reply as if the user immediately
    // pressed "Cancel", passing true to |dialog_was_suppressed|.
    wrapper->Run(true, false, std::u16string());
  }
}

void WebContentsImpl::NotifyOnJavaScriptDialogDismiss(
    base::OnceClosure callback) {
  DCHECK(javascript_dialog_dismiss_notifier_);
  javascript_dialog_dismiss_notifier_->NotifyOnDismiss(std::move(callback));
}

void WebContentsImpl::Close(RenderFrameHostImpl* render_frame_host) {
  if (GuestPageHolderImpl* guest_holder =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    if (auto* guest_delegate = guest_holder->delegate()) {
      guest_delegate->GuestClose();
    }
    return;
  }
  Close();
}

void WebContentsImpl::RunBeforeUnloadConfirm(
    RenderFrameHostImpl* render_frame_host,
    bool is_reload,
    JavaScriptDialogCallback response_callback) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::RunBeforeUnloadConfirm",
                        "render_frame_host", render_frame_host, "is_reload",
                        is_reload);
  DCHECK(render_frame_host->IsActive());

  // Ensure that if showing a dialog is the first thing that a page does, that
  // the contents of the previous page aren't shown behind it. This is required
  // because showing a dialog freezes the renderer, so no frames will be coming
  // from it. https://crbug.com/823353
  auto* render_widget_host_impl = render_frame_host->GetRenderWidgetHost();
  if (render_widget_host_impl) {
    render_widget_host_impl->ForceFirstFrameAfterNavigationTimeout();
  }

  // Running a dialog causes an exit to webpage-initiated fullscreen.
  // http://crbug.com/728276
  auto blocker =
      ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId);
  if (!blocker) {
    return;
  }

  auto callback = base::BindOnce(
      &WebContentsImpl::OnDialogClosed, weak_factory_.GetWeakPtr(),
      render_frame_host->GetProcess()->GetDeprecatedID(),
      render_frame_host->GetRoutingID(), std::move(response_callback),
      std::move(*blocker));

  std::vector<protocol::PageHandler*> page_handlers =
      protocol::PageHandler::EnabledForWebContents(this);

  JavaScriptDialogManager* dialog_manager = nullptr;
  bool should_suppress = false;
  // Give guest a first crack at the dialog.
  if (GuestPageHolderImpl* guest_holder =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    if (auto* guest_delegate = guest_holder->delegate()) {
      dialog_manager = guest_delegate->GuestGetJavascriptDialogManager();
    }
  } else if (delegate_) {
    dialog_manager = delegate_->GetJavaScriptDialogManager(this);
    should_suppress = delegate_->ShouldSuppressDialogs(this);
  }

  // While a JS beforeunload dialog is showing, defer commits in this
  // WebContents.
  javascript_dialog_dismiss_notifier_ =
      std::make_unique<JavaScriptDialogDismissNotifier>();

  bool has_non_devtools_handlers = delegate_ && dialog_manager;
  bool has_handlers = page_handlers.size() || has_non_devtools_handlers;
  if (should_suppress || !has_handlers) {
    std::move(callback).Run(false, true, std::u16string());
    return;
  }

  is_showing_before_unload_dialog_ = true;

  scoped_refptr<CloseDialogCallbackWrapper> wrapper =
      new CloseDialogCallbackWrapper(std::move(callback));

  GURL frame_url = render_frame_host->GetLastCommittedURL();
  for (auto* handler : page_handlers) {
    handler->DidRunBeforeUnloadConfirm(
        frame_url, render_frame_host->devtools_frame_token(),
        has_non_devtools_handlers,
        base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false));
  }

  if (dialog_manager) {
    created_dialog_since_last_cancel_ = true;
    dialog_manager->RunBeforeUnloadDialog(
        this, render_frame_host, is_reload,
        base::BindOnce(&CloseDialogCallbackWrapper::Run, wrapper, false));
#if BUILDFLAG(IS_ANDROID)
    // If A embeds a subframe B and B has a BeforeUnload handler whereas A
    // doesn't, the main frame navigation from A will show a dialog. In that
    // case `render_frame_host` is B and `initiator` is A. The navigation
    // request is on A.
    RenderFrameHostImpl* initiator =
        render_frame_host->GetBeforeUnloadInitiator();
    NavigationRequest* request =
        initiator ? initiator->frame_tree_node()->navigation_request()
                  : nullptr;
    auto* animation_manager =
        static_cast<BackForwardTransitionAnimationManagerAndroid*>(
            GetBackForwardTransitionAnimationManager());
    // We might not always have a NavigationRequest at this point (i.e. a
    // renderer-initiated navigation). However if this is a browser-initiated
    // history navigation, the request can't be null.
    if (animation_manager && request) {
      animation_manager->OnBeforeUnloadDialogShown(request->GetNavigationId());
    }
#endif
  }
}

void WebContentsImpl::RunFileChooser(
    base::WeakPtr<FileChooserImpl> file_chooser,
    RenderFrameHost* render_frame_host,
    scoped_refptr<FileChooserImpl::FileSelectListenerImpl> listener,
    const blink::mojom::FileChooserParams& params) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RunFileChooser",
                        "render_frame_host", render_frame_host);

  absl::Cleanup cancel_chooser = [&listener] {
    listener->FileSelectionCanceled();
  };
  if (visibility_ == Visibility::HIDDEN) {
    // Do not allow background tab to open file chooser.
    return;
  }
  if (delegate_ && !delegate_->IsContentsActive(this)) {
    // Do not allow inactive tabs to open file chooser.
    return;
  }
  if (active_file_chooser_) {
    // Only allow one active file chooser at one time.
    return;
  }

  // Any explicit focusing of another window while this WebContents is in
  // fullscreen can be used to confuse the user, so drop fullscreen.
  auto blocker =
      ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId);
  if (!blocker) {
    return;
  }
  listener->SetFullscreenBlock(std::move(*blocker));

  if (delegate_) {
    active_file_chooser_ = std::move(file_chooser);
    delegate_->RunFileChooser(render_frame_host, std::move(listener), params);
    std::move(cancel_chooser).Cancel();
  }
}

double WebContentsImpl::GetPendingZoomLevel(RenderWidgetHostImpl* rwh) {
  RenderFrameHostImpl* rfh = nullptr;
  // Find the RenderFrameHost for `rwh`.
  ForEachRenderFrameHostImplWithAction(
      [rwh, &rfh](RenderFrameHostImpl* render_frame_host) {
        if (render_frame_host->GetRenderWidgetHost() == rwh) {
          rfh = render_frame_host;
          return RenderFrameHost::FrameIterationAction::kStop;
        }
        return RenderFrameHost::FrameIterationAction::kContinue;
      });

  if (rfh) {
    // Find nearest ancestor that has independent zoom.
    // Use GetParentOrOuterDocument() instead of GetParent() in order
    // to "step out of" any fenced frames that are encountered.
    while (!HostZoomMapForRenderFrameHost(rfh)->IsIndependentZoomFrameTreeNode(
               rfh->GetFrameTreeNodeId()) &&
           rfh->GetParentOrOuterDocument()) {
      rfh = rfh->GetParentOrOuterDocument();
    }
  } else {
    // Not finding a `rfh` suggests that `rwh` is still in the process of being
    // set up, or is for a newly created RenderWidgetHost without a
    // RenderFrameHost (e.g. for a <select> tag popup>), and that this function
    // will be called again when there is a findable `rfh` for `rwh`. For now,
    // just return the zoom level of this WebContents, as that will most often
    // be right.
    return HostZoomMap::GetZoomLevel(this);
  }

  GURL url;
  if (rfh->IsInBackForwardCache()) {
    // If the `rfh`s page is in the bf cache, then the use of the pending (or
    // last committed) entry would have the lookup be based on an unrelated URL.
    // In that case use the RFH's last committed URL. While using the
    // NavigationEntry is preferred in order to avoid a virtual URL, that
    // shouldn't be a problem if the URL from the `rfh` is used. Note that
    // subframes get their zoom updates asynchronously from the main frame, so
    // it's possible to get here in a race condition for a page that is no
    // longer visible. For example, it's possible to get here on Android in the
    // tests FencedFrameParameterizedBrowserTest.NavigateUnfencedTopAndGoBack
    // and All/FencedFrameAutomaticBeaconBrowserTest.MessageExceedsLengthLimit/
    // fencedframe.
    url = rfh->GetLastCommittedURL();
  } else if (!rfh->GetParent()) {
    // Only use the pending entry if `rfh` is a main frame, otherwise the
    // resulting url is from outside the independently zoomed subtree, and
    // may result in the wrong zoom level.
    NavigationEntry* pending_entry = rfh->GetController().GetPendingEntry();
    if (!pending_entry) {
      return HostZoomMap::GetZoomLevel(this, rfh->GetGlobalId());
    }
    url = pending_entry->GetURL();
  } else {
    // In this case `rfh` is for an independently-zoomed subframe. Call
    // GetZoomLevel(WebContents*, RenderFrameHost*) in case `rfh` uses temporary
    // zoom.
    return HostZoomMap::GetZoomLevel(this, rfh->GetGlobalId());
  }
#if BUILDFLAG(IS_ANDROID)
  return HostZoomMapForRenderFrameHost(rfh)
      ->GetZoomLevelForHostAndSchemeAndroid(url.GetScheme(),
                                            net::GetHostOrSpecFromURL(url));
#else
  return HostZoomMapForRenderFrameHost(rfh)->GetZoomLevelForHostAndScheme(
      url.GetScheme(), net::GetHostOrSpecFromURL(url));
#endif
}

bool WebContentsImpl::IsPictureInPictureAllowedForFullscreenVideo() const {
  return media_web_contents_observer_
      ->IsPictureInPictureAllowedForFullscreenVideo();
}

bool WebContentsImpl::IsFocusedElementEditable() {
  RenderFrameHostImpl* frame = GetFocusedFrame();
  return frame && frame->has_focused_editable_element();
}

bool WebContentsImpl::IsShowingContextMenu() {
  return showing_context_menu_;
}

void WebContentsImpl::SetShowingContextMenu(bool showing) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::SetShowingContextMenu",
                        "showing", showing);

  DCHECK_NE(showing_context_menu_, showing);
  showing_context_menu_ = showing;

  if (auto* view = GetRenderWidgetHostView()) {
    // Notify the main frame's RWHV to run the platform-specific code, if any.
    static_cast<RenderWidgetHostViewBase*>(view)->SetShowingContextMenu(
        showing);
  }
}

void WebContentsImpl::ClearFocusedElement() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ClearFocusedElement");
  if (auto* frame = GetFocusedFrame()) {
    frame->ClearFocusedElement();
  }
}

bool WebContentsImpl::IsNeverComposited() {
  return is_never_composited_;
}

RenderViewHostDelegateView* WebContentsImpl::GetDelegateView() {
  return render_view_host_delegate_view_;
}

const blink::RendererPreferences& WebContentsImpl::GetRendererPrefs(
    RenderViewHostImpl* render_view_host) {
  if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
          *render_view_host->frame_tree()->GetMainFrame())) {
    return guest->GetRendererPrefs();
  }
  RenderViewHostImpl::GetPlatformSpecificPrefs(&renderer_preferences_);
  return renderer_preferences_;
}

const blink::web_pref::WebPreferences&
WebContentsImpl::GetOrCreateWebPreferences(
    RenderViewHostImpl* render_view_host) {
  if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
          *render_view_host->frame_tree()->GetMainFrame())) {
    return guest->GetWebPreferences();
  }
  return GetOrCreateWebPreferences();
}

RenderFrameHostImpl* WebContentsImpl::GetOuterWebContentsFrame() {
  if (GetOuterDelegateFrameTreeNodeId().is_null()) {
    return nullptr;
  }

  FrameTreeNode* outer_node =
      FrameTreeNode::GloballyFindByID(GetOuterDelegateFrameTreeNodeId());
  // The outer node should be in the outer WebContents.
  DCHECK_EQ(&outer_node->frame_tree(),
            &GetOuterWebContents()->GetPrimaryFrameTree());
  return outer_node->parent();
}

WebContentsImpl* WebContentsImpl::GetOuterWebContents() {
  return node_.outer_web_contents();
}

WebContentsDelegate* WebContentsImpl::GetFirstWebContentsDelegate() {
  // `delegate_` can be nullptr outside of tests, e.g. during WebContents
  // creation, teardown, or when transferring ownership (e.g. prerender
  // activation).
  if (delegate_) {
    return delegate_;
  }
  if (auto* connector = GetSurfaceEmbedConnector()) {
    return static_cast<SurfaceEmbedConnectorImpl*>(connector)
        ->GetFirstWebContentsDelegate();
  }
  if (WebContentsImpl* outer = GetOuterWebContents()) {
    return outer->GetFirstWebContentsDelegate();
  }
  return nullptr;
}

bool WebContentsImpl::HasPointerLockWidgetInParentChain() {
  if (pointer_lock_widget_) {
    return true;
  }
  if (auto* connector = GetSurfaceEmbedConnector()) {
    return static_cast<SurfaceEmbedConnectorImpl*>(connector)
        ->HasPointerLockWidgetInParentChain();
  }
  if (WebContentsImpl* outer = GetOuterWebContents()) {
    return outer->HasPointerLockWidgetInParentChain();
  }
  return false;
}

void WebContentsImpl::SetPointerLockWidgetInParentChain(
    RenderWidgetHostImpl* widget) {
  pointer_lock_widget_ = widget;
  if (auto* connector = GetSurfaceEmbedConnector()) {
    static_cast<SurfaceEmbedConnectorImpl*>(connector)
        ->SetPointerLockWidgetInParentChain(widget);
  } else if (WebContentsImpl* outer = GetOuterWebContents()) {
    outer->SetPointerLockWidgetInParentChain(widget);
  }
}

std::vector<WebContents*> WebContentsImpl::GetInnerWebContents() {
  std::vector<WebContents*> all_inner_contents;
  const auto& inner_contents = node_.GetInnerWebContents();
  all_inner_contents.insert(all_inner_contents.end(), inner_contents.begin(),
                            inner_contents.end());
  return all_inner_contents;
}

WebContentsImpl* WebContentsImpl::GetResponsibleWebContents() {
  if (delegate_) {
    WebContentsImpl* responsible_from_delegate = static_cast<WebContentsImpl*>(
        delegate_->GetResponsibleWebContents(this));
    if (responsible_from_delegate) {
      return responsible_from_delegate;
    }
  }

  return FromRenderFrameHostImpl(
      GetPrimaryMainFrame()->GetOutermostMainFrameOrEmbedder());
}

WebContentsImpl* WebContentsImpl::GetFocusedWebContents() {
  if (auto* focused_frame_tree = GetFocusedFrameTree()) {
    return WebContentsImpl::FromFrameTreeNode(focused_frame_tree->root());
  }
  return nullptr;
}

FrameTree* WebContentsImpl::GetFocusedFrameTree() {
  if (surface_embed_connector_) {
    return surface_embed_connector_->GetFocusFrameTreeIfContainsFocus();
  }
  if (GetOuterWebContents()) {
    return GetOuterWebContents()->GetFocusedFrameTree();
  }
  return node_.focused_frame_tree();
}

void WebContentsImpl::SetFocusToLocationBar() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetFocusToLocationBar");
  if (delegate_) {
    delegate_->SetFocusToLocationBar();
  }
}

bool WebContentsImpl::ContainsOrIsFocusedWebContents() {
  // Delegate to SurfaceEmbedConnectorImpl which knows how to find the parent
  // WebContents for both SurfaceEmbed-based embedding and traditional outer
  // WebContents embedding, allowing it to traverse the full parent chain to
  // the root WebContents where the focused frame tree is tracked.
  return SurfaceEmbedConnectorImpl::ContainsOrIsFocusedWebContents(this);
}

void WebContentsImpl::RemoveBrowserPluginEmbedder() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::RemoveBrowserPluginEmbedder");
  browser_plugin_embedder_.reset();
}

WebContentsImpl* WebContentsImpl::GetOutermostWebContents() {
  WebContentsImpl* root = this;
  while (root->GetOuterWebContents()) {
    root = root->GetOuterWebContents();
  }
  return root;
}

void WebContentsImpl::InnerWebContentsCreated(WebContents* inner_web_contents) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::InnerWebContentsCreated");
  observers_.NotifyObservers(&WebContentsObserver::InnerWebContentsCreated,
                             inner_web_contents);
}

void WebContentsImpl::InnerWebContentsAttached(
    WebContents* inner_web_contents) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::InnerWebContentsAttached");
  if (inner_web_contents->IsCurrentlyAudible()) {
    OnAudioStateChanged();
  }
}

void WebContentsImpl::SurfaceEmbedChildWebContentsAttached(
    WebContents* inner_web_contents,
    RenderFrameHost* embedder_render_frame_host) {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::SurfaceEmbedChildWebContentsAttached");
  surface_embed_children_.push_back(inner_web_contents->GetWeakPtr());
  observers_.NotifyObservers(
      &WebContentsObserver::SurfaceEmbedChildWebContentsAttached,
      inner_web_contents, embedder_render_frame_host);
}

void WebContentsImpl::SurfaceEmbedChildWebContentsDetached(
    WebContents* inner_web_contents) {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::SurfaceEmbedChildWebContentsDetached");
  std::erase_if(surface_embed_children_, [inner_web_contents](const auto& ptr) {
    return ptr.get() == inner_web_contents;
  });
  observers_.NotifyObservers(
      &WebContentsObserver::SurfaceEmbedChildWebContentsDetached,
      inner_web_contents);
}

void WebContentsImpl::InnerWebContentsDetached(
    WebContents* inner_web_contents) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::InnerWebContentsDetached");
  if (!IsBeingDestroyed()) {
    OnAudioStateChanged();
  }
}

void WebContentsImpl::RenderViewReady(RenderViewHost* rvh) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RenderViewReady",
                        "render_view_host", rvh);
  if (rvh != GetRenderViewHost()) {
    // Don't notify the world, since this came from a renderer in the
    // background.
    return;
  }

  RenderWidgetHostViewBase* rwhv =
      static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView());
  if (rwhv) {
    rwhv->SetMainFrameAXTreeID(GetPrimaryMainFrame()->GetAXTreeID());
  }

  notify_disconnection_ = true;

  {
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.RenderViewReady");
    observers_.NotifyObservers(&WebContentsObserver::RenderViewReady);
  }
  view_->RenderViewReady();
}

void WebContentsImpl::RenderViewTerminated(RenderViewHost* rvh,
                                           base::TerminationStatus status,
                                           int error_code) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::RenderViewTerminated",
                        "render_view_host", rvh, "status",
                        static_cast<int>(status));

  // It is possible to get here while the WebContentsImpl is being destroyed,
  // in particular when the destruction of the main frame's RenderFrameHost and
  // RenderViewHost triggers cleanup of the main frame's process, which in turn
  // dispatches RenderProcessExited observers, one of which calls in here.  In
  // this state, we cannot check GetRenderViewHost() below, since
  // current_frame_host() for the root FrameTreeNode has already been cleared.
  // Since the WebContents is going away, none of the work here is needed, so
  // just return early.
  auto* rvh_impl = static_cast<RenderViewHostImpl*>(rvh);
  if (IsBeingDestroyed() || rvh_impl->frame_tree()->IsBeingDestroyed()) {
    return;
  }

  // If we are a guest frame tree and the RenderViewHost is the main frame's RVH
  // then notify that the guest's main frame process went away.
  if (rvh_impl->frame_tree()->is_guest() &&
      rvh_impl->frame_tree()->GetMainFrame()->render_view_host() == rvh_impl) {
    if (auto* guest = GuestPageHolderImpl::FromRenderFrameHost(
            *rvh_impl->frame_tree()->GetMainFrame())) {
      guest->delegate()->GuestMainFrameProcessGone(status);
      return;
    }
  }

  if (rvh != GetRenderViewHost()) {
    // The pending page's RenderViewHost is gone.
    return;
  }

  DCHECK(rvh_impl->GetMainRenderFrameHost()->IsInPrimaryMainFrame())
      << "GetRenderViewHost() must belong to the primary frame tree";

  // Ensure fullscreen mode is exited in the |delegate_| since a crashed
  // renderer may not have made a clean exit.
  if (IsFullscreen()) {
    ExitFullscreenMode(false);
  }

  // Ensure any video or document in Picture-in-Picture is exited in the
  // |delegate_| since a crashed renderer may not have made a clean exit.
  if (HasPictureInPictureVideo() || HasPictureInPictureDocument()) {
    ExitPictureInPicture();
  }

  // Cancel any visible dialogs so they are not left dangling over the sad tab.
  CancelActiveAndPendingDialogs();

  audio_stream_monitor_.RenderProcessGone(rvh_impl->GetProcess()->GetID());

  // Reset the loading progress. TODO(avi): What does it mean to have a
  // "renderer crash" when there is more than one renderer process serving a
  // webpage? Once this function is called at a more granular frame level, we
  // probably will need to more granularly reset the state here.
  ResetLoadProgressState();

  SetPrimaryMainFrameProcessStatus(status, error_code);

  TRACE_EVENT0(
      "content",
      "Dispatching WebContentsObserver::PrimaryMainFrameRenderProcessGone");
  // Some observers might destroy WebContents in
  // PrimaryMainFrameRenderProcessGone.
  base::WeakPtr<WebContentsImpl> weak_ptr = weak_factory_.GetWeakPtr();
  for (auto& observer : observers_.observer_list()) {
    observer.PrimaryMainFrameRenderProcessGone(status);
    if (!weak_ptr) {
      return;
    }
  }

  // |this| might have been deleted. Do not add code here.
}

void WebContentsImpl::RenderViewDeleted(RenderViewHost* rvh) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RenderViewDeleted",
                        "render_view_host", rvh);
  observers_.NotifyObservers(&WebContentsObserver::RenderViewDeleted, rvh);
}

void WebContentsImpl::ClearTargetURL() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ClearTargetURL");
  frame_that_set_last_target_url_ = nullptr;
  if (delegate_) {
    delegate_->UpdateTargetURL(this, GURL());
  }
}

void WebContentsImpl::Close() {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::Close",
                        "render_frame_host", GetPrimaryMainFrame());
#if BUILDFLAG(IS_MAC)
  // The UI may be in an event-tracking loop, such as between the
  // mouse-down and mouse-up in text selection or a button click.
  // Defer the close until after tracking is complete, so that we
  // don't free objects out from under the UI.
  // TODO(shess): This could get more fine-grained.  For instance,
  // closing a tab in another window while selecting text in the
  // current window's Omnibox should be just fine.
  if (view_->CloseTabAfterEventTrackingIfNeeded()) {
    return;
  }
#endif

  if (delegate_) {
    delegate_->CloseContents(this);
  }
}

void WebContentsImpl::SetWindowRect(const gfx::Rect& new_bounds) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetWindowRect");
  if (!delegate_) {
    return;
  }

  // Members of |new_bounds| may be 0 to indicate uninitialized values for newly
  // opened windows, even if the |GetContainerBounds()| inner rect is correct.
  // TODO(crbug.com/40092782): Plumb values as specified; fallback on outer
  // rect.
  auto bounds = new_bounds;
  if (bounds.IsEmpty()) {
    bounds.set_size(GetContainerBounds().size());
  }

  // Only requests from the main frame, not subframes, should reach this code.
  int64_t display_id = AdjustWindowRect(&bounds, GetPrimaryMainFrame());

  // Drop fullscreen when placing a WebContents to prohibit deceptive behavior.
  // Only drop fullscreen on the specific destination display, which is known.
  // This supports sites using cross-screen window management capabilities to
  // retain fullscreen and place a window on another screen.
  if (!ForSecurityDropFullscreen(display_id)) {
    return;
  }

  delegate_->SetContentsBounds(this, bounds);
}

void WebContentsImpl::MoveWindowTo(const gfx::Point& origin) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MoveWindowTo");
  if (!delegate_) {
    return;
  }
  auto* view = GetPrimaryMainFrame()->GetView();
  if (!view) {
    return;
  }
  gfx::Rect bounds(origin, view->GetBoundsInScreen().size());
  int64_t display_id = AdjustWindowRect(&bounds, GetPrimaryMainFrame());
  if (!ForSecurityDropFullscreen(display_id)) {
    return;
  }
  delegate_->SetContentsBounds(this, bounds);
}

void WebContentsImpl::ResizeWindowTo(const gfx::Size& size) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ResizeWindowTo");
  if (!delegate_) {
    return;
  }
  auto* view = GetPrimaryMainFrame()->GetView();
  if (!view) {
    return;
  }
  gfx::Rect bounds(view->GetBoundsInScreen().origin(), size);
  int64_t display_id = AdjustWindowRect(&bounds, GetPrimaryMainFrame());
  if (!ForSecurityDropFullscreen(display_id)) {
    return;
  }
  delegate_->SetContentsBounds(this, bounds);
}

void WebContentsImpl::UpdateWindowPreferredSize(
    RenderFrameHostImpl* render_frame_host,
    const gfx::Size& pref_size) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::UpdatePreferredSize");
  if (auto* guest =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    guest->delegate()->GuestUpdateWindowPreferredSize(pref_size);
    return;
  }

  CHECK(render_frame_host->IsInPrimaryMainFrame());

  const gfx::Size old_size = GetPreferredSize();
  preferred_size_ = pref_size;
  OnPreferredSizeChanged(old_size);
}

std::vector<RenderFrameHostImpl*>
WebContentsImpl::GetActiveTopLevelDocumentsInBrowsingContextGroup(
    RenderFrameHostImpl* render_frame_host) {
  std::vector<RenderFrameHostImpl*> out;
  for (WebContentsImpl* web_contents : GetAllWebContents()) {
    RenderFrameHostImpl* other_render_frame_host =
        web_contents->GetPrimaryMainFrame();

    // Filters out inactive documents.
    if (other_render_frame_host->lifecycle_state() !=
        RenderFrameHostImpl::LifecycleStateImpl::kActive) {
      continue;
    }

    // Filter frames in different browsing context groups.
    if (!render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
            other_render_frame_host->GetSiteInstance())) {
      continue;
    }

    out.push_back(other_render_frame_host);
  }
  return out;
}

PrerenderHostRegistry* WebContentsImpl::GetPrerenderHostRegistry() {
  DCHECK(prerender_host_registry_);
  return prerender_host_registry_.get();
}

void WebContentsImpl::DidStartLoading(FrameTreeNode* frame_tree_node) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DidStartLoading",
                        "frame_tree_node", frame_tree_node);

  auto loading_track =
      perfetto::NamedTrack("Loading", 0, tracing_track_->track());
  TRACE_EVENT_BEGIN("browser,navigation", "WebContentsImpl Loading",
                    loading_track, "URL", "NULL",
                    "Primary Main FrameTreeNode id",
                    GetPrimaryFrameTree().root()->frame_tree_node_id());
  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DidStartLoading");
  observers_.NotifyObservers(&WebContentsObserver::DidStartLoading);

  // Reset the focus state from DidStartNavigation to false if a new load starts
  // afterward, in case loading logic triggers a FocusLocationBarByDefault call.
  should_focus_location_bar_by_default_ = false;

  // Notify accessibility that the user is navigating away from the
  // current document.
  // TODO(domfarolino, dmazzoni): Do this using WebContentsObserver. See
  // https://crbug.com/981271.
  ui::BrowserAccessibilityManager* manager =
      frame_tree_node->current_frame_host()->browser_accessibility_manager();
  if (manager) {
    manager->UserIsNavigatingAway();
  }
}

void WebContentsImpl::DidStopLoading() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DidStopLoading");
  if (IsBeingDestroyed()) {
    return;
  }

  // Use the last committed entry rather than the active one, in case a
  // pending entry has been created.
  // An entry may not exist for a stop when loading an initial blank page or
  // if an iframe injected by script into a blank page finishes loading.
  NavigationEntry* entry = GetController().GetLastCommittedEntry();
  std::string url =
      (entry ? entry->GetVirtualURL().possibly_invalid_spec() : "NULL");

  SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.DidStopLoading");
  observers_.NotifyObservers(&WebContentsObserver::DidStopLoading);

  // WebContentsImpl Loading
  auto loading_track =
      perfetto::NamedTrack("Loading", 0, tracing_track_->track());
  TRACE_EVENT_END("browser,navigation", loading_track, "URL", url);

  GetPrimaryMainFrame()->ForEachRenderFrameHostImpl(
      [](RenderFrameHostImpl* render_frame_host) {
        ui::BrowserAccessibilityManager* manager =
            render_frame_host->browser_accessibility_manager();
        if (manager) {
          manager->DidStopLoading();
        }
      });

#if BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(
          features::kAndroidWarmUpSpareRendererWithTimeout) &&
      features::kAndroidSpareRendererCreationTiming.Get() ==
          features::kAndroidSpareRendererCreationAfterLoading) {
    WarmUpAndroidSpareRenderer();
  }
#endif
}

ui::AXTreeUpdate WebContentsImpl::RequestAXTreeSnapshotWithinBrowserProcess() {
  ui::BrowserAccessibilityManager* root_manager =
      GetRootBrowserAccessibilityManager();
  // A page can be fully loaded before a user turns on accessibility services,
  // and we can be in a transient state waiting for managers.
  if (!root_manager || !root_manager->ax_tree()) {
    return ui::AXTreeUpdate();
  }

  ui::AXTreeUpdate combined_update;
  ui::AXTreeCombiner combiner;

  GetPrimaryMainFrame()->ForEachRenderFrameHostImplWithAction(
      [this, &combiner](RenderFrameHostImpl* rfhi) {
        ui::BrowserAccessibilityManager* manager =
            rfhi->browser_accessibility_manager();
        if (!manager) {
          return RenderFrameHost::FrameIterationAction::kContinue;
        }

        ui::AXTree* tree = manager->ax_tree();
        if (!tree || !tree->root()) {
          return RenderFrameHost::FrameIterationAction::kContinue;
        }

        ui::AXTreeUpdate update_for_frame;
        update_for_frame.root_id = tree->root()->id();
        update_for_frame.tree_data = tree->data();

        std::vector<ui::AXNodeData> nodes;
        RecursivelyConstructAXTree(tree->root(), nodes);
        update_for_frame.nodes = nodes;

        combiner.AddTree(update_for_frame, rfhi->AccessibilityIsRootFrame());

        return RenderFrameHost::FrameIterationAction::kContinue;
      });

  // Combine all the individual frame tree updates into one.
  combiner.Combine();
  CHECK(combiner.combined());
  combined_update = std::move(combiner.combined().value());
  return combined_update;
}

void WebContentsImpl::RecursivelyConstructAXTree(
    ui::AXNode* node,
    std::vector<ui::AXNodeData>& nodes) {
  nodes.push_back(node->data());

  for (ui::AXNode* child : node->GetAllChildren()) {
    RecursivelyConstructAXTree(child, nodes);
  }
}

void WebContentsImpl::ApplyAXTreeFixingResult(ui::AXTreeID tree_id,
                                              ui::AXNodeID node_id,
                                              ax::mojom::Role role) {
// The AXTreeFixing feature is not currently available on Android.
#if !BUILDFLAG(IS_ANDROID)
  CHECK(features::IsAXTreeFixingEnabled());

  GetPrimaryMainFrame()->ForEachRenderFrameHostImplWithAction(
      [tree_id, node_id, role](RenderFrameHostImpl* rfhi) {
        ui::BrowserAccessibilityManager* manager =
            rfhi->browser_accessibility_manager();
        if (!manager) {
          return RenderFrameHost::FrameIterationAction::kContinue;
        }

        ui::AXTree* tree = manager->ax_tree();
        if (!tree || !tree->root()) {
          return RenderFrameHost::FrameIterationAction::kContinue;
        }

        ui::AXNode* node = manager->ax_tree()->GetFromId(node_id);
        if (!node) {
          return RenderFrameHost::FrameIterationAction::kContinue;
        }

        // Update the node's role and stop iterating.
        ui::AXNodeData new_data = node->data();
        new_data.role = role;
        node->SetData(new_data);
        return RenderFrameHost::FrameIterationAction::kStop;
      });
#endif
}

void WebContentsImpl::DidChangeLoadProgressForMainFrame(
    RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::DidChangeLoadProgressForMainFrame");
  if (IsBeingDestroyed() ||
      render_frame_host->frame_tree()->IsBeingDestroyed()) {
    return;
  }

  DCHECK(render_frame_host->is_main_frame());
  if (!render_frame_host->IsOutermostMainFrame()) {
    // If this main frame isn't an outermost main frame but an embedded
    // main frame then return. ie. a fenced frame inside a guest.
    return;
  }

  if (auto* guest =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    guest->delegate()->GuestDidChangeLoadProgress(
        render_frame_host->frame_tree()->GetLoadProgress());
  } else if (render_frame_host->IsInPrimaryMainFrame()) {
    double load_progress = GetLoadProgress();

    // The delegate is notified immediately for the first and last updates.
    // Also, since the message loop may be pretty busy when a page is loaded, it
    // might not execute a posted task in a timely manner so the progress report
    // is sent immediately if enough time has passed.
    base::TimeDelta min_delay = minimum_delay_between_loading_updates_ms_;
    bool delay_elapsed =
        loading_last_progress_update_.is_null() ||
        base::TimeTicks::Now() - loading_last_progress_update_ > min_delay;

    if (load_progress == 0.0 || load_progress == 1.0 || delay_elapsed) {
      // If there is a pending task to send progress, it is now obsolete.
      loading_weak_factory_.InvalidateWeakPtrs();

      // Notify the load progress change.
      SendChangeLoadProgress();

      // Clean-up the states if needed.
      if (load_progress == 1.0) {
        ResetLoadProgressState();
      }
      return;
    }

    if (loading_weak_factory_.HasWeakPtrs()) {
      return;
    }

    if (min_delay == base::Milliseconds(0)) {
      SendChangeLoadProgress();
    } else {
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
          FROM_HERE,
          base::BindOnce(&WebContentsImpl::SendChangeLoadProgress,
                         loading_weak_factory_.GetWeakPtr()),
          min_delay);
    }
  }
}

bool WebContentsImpl::IsHidden() {
  return GetPageVisibilityState() == PageVisibilityState::kHidden;
}

void WebContentsImpl::CreateThrottlesForNavigation(
    NavigationThrottleRegistry& registry) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::CreateThrottlesForNavigation",
                        "navigation", registry.GetNavigationHandle());
  GetContentClient()->browser()->CreateThrottlesForNavigation(registry);
}

void WebContentsImpl::CreateThrottlesForCommitWithoutUrlLoader(
    NavigationThrottleRegistry& registry) {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::CreateThrottlesForCommitWithoutUrlLoader",
      "navigation", registry.GetNavigationHandle());
  GetContentClient()->browser()->CreateThrottlesForCommitWithoutUrlLoader(
      registry);
}

std::vector<std::unique_ptr<CommitDeferringCondition>>
WebContentsImpl::CreateDeferringConditionsForNavigationCommit(
    NavigationHandle& navigation_handle,
    CommitDeferringCondition::NavigationType type) {
  OPTIONAL_TRACE_EVENT2(
      "content", "WebContentsImpl::CreateDeferringConditionsForNavigation",
      "navigation", navigation_handle, "NavigationType", type);
  std::vector<std::unique_ptr<CommitDeferringCondition>> conditions =
      GetContentClient()
          ->browser()
          ->CreateCommitDeferringConditionsForNavigation(&navigation_handle,
                                                         type);

  if (auto condition = JavaScriptDialogCommitDeferringCondition::MaybeCreate(
          static_cast<NavigationRequest&>(navigation_handle))) {
    conditions.push_back(std::move(condition));
  }
  return conditions;
}

std::unique_ptr<NavigationUIData> WebContentsImpl::GetNavigationUIData(
    NavigationHandle* navigation_handle) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::GetNavigationUIData");
  return GetContentClient()->browser()->GetNavigationUIData(navigation_handle);
}

void WebContentsImpl::RegisterExistingOriginAsHavingDefaultIsolation(
    const url::Origin& origin,
    NavigationRequest* navigation_request_to_exclude) {
  OPTIONAL_TRACE_EVENT2(
      "content",
      "WebContentsImpl::RegisterExistingOriginAsHavingDefaultIsolation",
      "origin", origin, "navigation_request_to_exclude",
      navigation_request_to_exclude);
  // Note: This function can be made static if we ever need call it without
  // a WebContentsImpl instance, in which case we can use a wrapper to
  // implement the override from NavigatorDelegate.
  for (WebContentsImpl* web_contents : GetAllWebContents()) {
    // We only need to search entries in the same BrowserContext as us.
    if (web_contents->GetBrowserContext() != GetBrowserContext()) {
      continue;
    }

    // Walk the frame trees to notify each one's NavigationController about the
    // opting-in or opting-out `origin`, and also pick up any frames without
    // FrameNavigationEntries.
    // * Some frames won't have FrameNavigationEntries (Issues 524208, 608402).
    // * Some pending navigations won't have NavigationEntries.
    web_contents->ForEachFrameTree(
        [origin, navigation_request_to_exclude](FrameTree& frame_tree) {
          frame_tree.RegisterExistingOriginAsHavingDefaultIsolation(
              origin, navigation_request_to_exclude);
        });
  }
}

bool WebContentsImpl::MaybeCopyContentAreaAsBitmap(
    base::OnceCallback<void(const SkBitmap&)> callback) {
  if (!GetDelegate()) {
    return false;
  }
  return GetDelegate()->MaybeCopyContentAreaAsBitmap(std::move(callback));
}

#if BUILDFLAG(IS_ANDROID)
bool WebContentsImpl::MaybeCopyContentAreaAsHardwareBuffer(
    HardwareBufferResultCallback callback) {
  if (!GetDelegate()) {
    return false;
  }
  return GetDelegate()->MaybeCopyContentAreaAsHardwareBuffer(
      std::move(callback));
}
#endif  // BUILDFLAG(IS_ANDROID)

bool WebContentsImpl::SupportsForwardTransitionAnimation() {
#if BUILDFLAG(IS_ANDROID)
  return supports_forward_transition_animation_;
#else
  return true;
#endif  // !BUILDFLAG(IS_ANDROID)
}

#if BUILDFLAG(IS_ANDROID)
void WebContentsImpl::SetSupportsForwardTransitionAnimation(bool supports) {
  supports_forward_transition_animation_ = supports;
}
#endif  // !BUILDFLAG(IS_ANDROID)

void WebContentsImpl::DidChangeName(RenderFrameHostImpl* render_frame_host,
                                    const std::string& name) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::DidChangeName",
                        "render_frame_host", render_frame_host, "name", name);
  observers_.NotifyObservers(&WebContentsObserver::FrameNameChanged,
                             render_frame_host, name);
}

void WebContentsImpl::DidReceiveUserActivation(
    RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DidReceiveUserActivation",
                        "render_frame_host", render_frame_host);
  observers_.NotifyObservers(&WebContentsObserver::FrameReceivedUserActivation,
                             render_frame_host);
}

void WebContentsImpl::WebAuthnAssertionRequestSucceeded(
    RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::WebAuthnAssertionRequestSucceeded",
                        "render_frame_host", render_frame_host);
  observers_.NotifyObservers(
      &WebContentsObserver::WebAuthnAssertionRequestSucceeded,
      render_frame_host);
}

void WebContentsImpl::BindDisplayCutoutHost(
    RenderFrameHostImpl* render_frame_host,
    mojo::PendingAssociatedReceiver<blink::mojom::DisplayCutoutHost> receiver) {
  if (safe_area_insets_host_) {
    safe_area_insets_host_->BindReceiver(std::move(receiver),
                                         render_frame_host);
  }
}

void WebContentsImpl::DidChangeDisplayState(
    RenderFrameHostImpl* render_frame_host,
    bool is_display_none) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::DidChangeDisplayState",
                        "render_frame_host", render_frame_host,
                        "is_display_none", is_display_none);
  observers_.NotifyObservers(&WebContentsObserver::FrameDisplayStateChanged,
                             render_frame_host, is_display_none);
}

void WebContentsImpl::FrameSizeChanged(RenderFrameHostImpl* render_frame_host,
                                       const gfx::Size& frame_size) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::FrameSizeChanged",
                        "render_frame_host", render_frame_host);
  observers_.NotifyObservers(&WebContentsObserver::FrameSizeChanged,
                             render_frame_host, frame_size);
}

void WebContentsImpl::DocumentOnLoadCompleted(
    RenderFrameHostImpl* render_frame_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DocumentOnLoadCompleted",
                        "render_frame_host", render_frame_host);
  DCHECK(render_frame_host->is_main_frame());
  if (!render_frame_host->IsOutermostMainFrame()) {
    // If this main frame isn't an outermost main frame but an embedded
    // main frame then return. )ie. a fenced frame inside a guest).
    return;
  }

  if (auto* guest =
          GuestPageHolderImpl::FromRenderFrameHost(*render_frame_host)) {
    ShowInsecureLocalhostWarningIfNeeded(render_frame_host->GetPage());
    guest->delegate()->GuestDocumentOnLoadCompleted();
  } else if (render_frame_host->IsInPrimaryMainFrame()) {
    // Don't dispatch DocumentOnLoadCompletedInPrimaryMainFrame for non-primary
    // main frames. As most of the observers are interested only in the onload
    // completion of the current document in the primary main frame. Since the
    // WebContents could be hosting more than one main frame (e.g., fenced
    // frames, prerender pages or pending delete RFHs), return early for other
    // cases. In case of prerendering, we dispatch
    // DocumentOnLoadCompletedInPrimaryMainFrame on activation.
    ShowInsecureLocalhostWarningIfNeeded(render_frame_host->GetPage());
    observers_.NotifyObservers(
        &WebContentsObserver::DocumentOnLoadCompletedInPrimaryMainFrame);
  }
}

void WebContentsImpl::UpdateTitle(RenderFrameHostImpl* render_frame_host,
                                  const std::u16string& title) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::UpdateTitle",
                        "render_frame_host", render_frame_host, "title", title);
  // Try to find the navigation entry, which might not be the current one.
  // For example, it might be from a pending deletion RFH, or it might be from a
  // non-primary frame tree.
  NavigationEntryImpl* entry =
      render_frame_host->frame_tree()->controller().GetEntryWithUniqueID(
          render_frame_host->nav_entry_id());

  if (!entry) {
    // We can handle title updates with no matching NavigationEntry, but only if
    // the update is for the initial NavigationEntry. In that case the initial
    // empty document RFH wouldn't have a `nav_entry_id` set because it hasn't
    // committed any navigation. Note that if the title update came from the
    // initial empty document but the WebContents is doing a session restore,
    // we will ignore the title update (because GetLastCommittedEntry() would
    // return the non-initial restored entry), which avoids accidental
    // overwriting of the restored entry's title.
    if (render_frame_host->GetParent() || !render_frame_host->frame_tree()
                                               ->controller()
                                               .GetLastCommittedEntry()
                                               ->IsInitialEntry()) {
      return;
    }
    entry =
        render_frame_host->frame_tree()->controller().GetLastCommittedEntry();
  }

  bool title_changed = UpdateTitleForEntryImpl(entry, title);
  if (title_changed) {
    if (render_frame_host == GetPrimaryMainFrame()) {
      NotifyTitleUpdateForEntry(entry);
      TRACE_EVENT_END("content", tracing_track_->track());
      EmitTracingSlice(base::UTF16ToUTF8(GetTitle()));
    }
    SCOPED_UMA_HISTOGRAM_TIMER("WebContentsObserver.TitleWasSetForMainFrame");
    observers_.NotifyObservers(&WebContentsObserver::TitleWasSetForMainFrame,
                               render_frame_host);
  }
}

void WebContentsImpl::UpdateApplicationTitle(
    RenderFrameHostImpl* render_frame_host,
    const std::u16string& application_title) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::UpdateTitle",
                        "render_frame_host", render_frame_host,
                        "application_title", application_title);
  // Same logic as UpdateTitle() above.
  NavigationEntryImpl* entry =
      render_frame_host->frame_tree()->controller().GetEntryWithUniqueID(
          render_frame_host->nav_entry_id());
  if (!entry) {
    if (render_frame_host->GetParent() || !render_frame_host->frame_tree()
                                               ->controller()
                                               .GetLastCommittedEntry()
                                               ->IsInitialEntry()) {
      return;
    }
    entry =
        render_frame_host->frame_tree()->controller().GetLastCommittedEntry();
  }
  std::u16string final_application_title;
  base::TrimWhitespace(application_title, base::TRIM_ALL,
                       &final_application_title);

  if (final_application_title == entry->GetApplicationTitle()) {
    return;
  }

  entry->SetApplicationTitle(final_application_title);
  NotifyNavigationStateChanged(INVALIDATE_TYPE_TITLE);
}

void WebContentsImpl::UpdateTargetURL(RenderFrameHostImpl* render_frame_host,
                                      const GURL& url) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::UpdateTargetURL",
                        "render_frame_host", render_frame_host, "url", url);
  // In case of racey updates from multiple RenderViewHosts, the last URL should
  // be shown - see also some discussion in https://crbug.com/807776.
  if (!url.is_valid() && render_frame_host != frame_that_set_last_target_url_) {
    return;
  }
  frame_that_set_last_target_url_ =
      url.is_valid() ? render_frame_host : nullptr;

  if (delegate_) {
    delegate_->UpdateTargetURL(this, url);
  }
}

void WebContentsImpl::SetAsFocusedWebContentsIfNecessary() {
  SetFocusedFrameTree(&GetPrimaryFrameTree());
}

void WebContentsImpl::SetFocusedFrameTree(FrameTree* frame_tree_to_focus) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetFocusedFrameTree");
  DCHECK(frame_tree_to_focus);

  // Only change focus if we are not currently focused.
  FrameTree* old_focused_frame_tree = GetFocusedFrameTree();
  if (old_focused_frame_tree == frame_tree_to_focus) {
    return;
  }

  if (surface_embed_connector_) {
    surface_embed_connector_->SetFocusedFrameTree(frame_tree_to_focus);
    return;
  }

  if (GetOuterWebContents()) {
    GetOuterWebContents()->SetFocusedFrameTree(frame_tree_to_focus);
    return;
  }

  node_.SetFocusedFrameTree(frame_tree_to_focus);

  // Send a page level blur to the `old_focused_frame_tree` so that it displays
  // inactive UI and focus `frame_tree_to_focus` to activate it.
  if (old_focused_frame_tree) {
    old_focused_frame_tree->root()
        ->current_frame_host()
        ->GetRenderWidgetHost()
        ->SetPageFocus(false);
  }

  // Make sure the outer frame trees knows our frame is focused. Otherwise, the
  // outer renderer could have the element before or after the frame element
  // focused which would return early without actually advancing focus.
  RenderFrameHostImpl::UpdateAXFocusDeferScope focus_defer_scope(
      *frame_tree_to_focus->root()
           ->current_frame_host()
           ->GetOutermostMainFrameOrEmbedder());
  if (frame_tree_to_focus->GetFocusedFrame() &&
      frame_tree_to_focus->GetFocusedFrame()
          ->current_frame_host()
          ->inner_tree_main_frame_tree_node_id()) {
    // If an inner frame tree, in `frame_tree_to_focus`, had focus, the
    // placeholder RenderFrameHost needs to be unset as the focused frame in
    // `frame_tree_to_focus`.
    frame_tree_to_focus->SetFocusedFrame(frame_tree_to_focus->root(), nullptr);
  }
  frame_tree_to_focus->FocusOuterFrameTrees();

  frame_tree_to_focus->root()
      ->current_frame_host()
      ->GetRenderWidgetHost()
      ->SetPageFocus(true);
}

void WebContentsImpl::SetFocusedFrame(FrameTreeNode* node,
                                      SiteInstanceGroup* source) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetFocusedFrame",
                        "frame_tree_node", node, "source_site_instance_group",
                        source);

  if (GetFocusedFrameTree() && GetFocusedFrameTree()->GetFocusedFrame()) {
    RenderFrameHostImpl* focused_rfh =
        GetFocusedFrameTree()->GetFocusedFrame()->current_frame_host();
    // This is only enforced for focus changes that cross a fenced frame
    // boundary.
    if (!node->current_frame_host()->VerifyFencedFrameFocusChange(
            focused_rfh)) {
      return;
    }
  }

  // Focus will not be in a consistent state until the focused frame tree is
  // also updated (if necessary).
  RenderFrameHostImpl::UpdateAXFocusDeferScope focus_defer_scope(
      *node->current_frame_host()->GetOutermostMainFrameOrEmbedder());

  node->frame_tree().SetFocusedFrame(node, source);

  auto* inner_contents = node_.GetInnerWebContentsInFrame(node);

  // We currently do not need to descend into inner frame trees that
  // are not an inner WebContents for focus. If `inner_contents` is null,
  // then the only supported inner frame tree type would be fenced frames.
  // An embedding frame focusing a fenced frame is not allowed since that would
  // be an information leak.  If a renderer attempts to do that, that should be
  // blocked by `RenderFrameProxyHost::DidFocusFrame()`.
  //
  // TODO(https://crbug.com/376085320): MPArch based <webview> allows focus on
  // the inner frame tree node. For now, disable this DCHECK when the flag is
  // enabled.
  DCHECK(base::FeatureList::IsEnabled(features::kGuestViewMPArch) ||
         inner_contents ||
         node->current_frame_host()
             ->inner_tree_main_frame_tree_node_id()
             .is_null());

  if (inner_contents) {
    // An inner WebContents is not created from Fenced Frames so we
    // shouldn't end up in this branch.
    DCHECK(!node->IsInFencedFrameTree());

    // |this| is an outer WebContents and |node| represents an inner
    // WebContents. Transfer the focus to the inner contents if |this| is
    // focused.
    if (GetFocusedWebContents() == this) {
      inner_contents->SetAsFocusedWebContentsIfNecessary();
    }
  } else if (node_.OuterContentsFrameTreeNode() &&
             node_.OuterContentsFrameTreeNode()
                     ->current_frame_host()
                     ->GetSiteInstance()
                     ->group() == source) {
    // A GuestView embedding a fenced frame will have an
    // OuterContentsFrameTreeNode. However, it will not have the same site
    // instance because a FencedFrame creates a new BrowsingInstance.
    DCHECK(!node->IsInFencedFrameTree());

    // |this| is an inner WebContents, |node| is its main FrameTreeNode and
    // the outer WebContents FrameTreeNode is at |source|'s SiteInstance.
    // Transfer the focus to the inner WebContents if the outer WebContents is
    // focused. This branch is used when an inner WebContents is focused through
    // its RenderFrameProxyHost (via FrameFocused mojo call, used to
    // implement the window.focus() API).
    if (GetFocusedWebContents() == GetOuterWebContents()) {
      // OuterContentsFrameTreeNode() indicates that this WebContents is an
      // inner WebContents, so it must have an outer WebContents. Otherwise, we
      // have an inconsistent state.
      CHECK(GetOuterWebContents());
      SetFocusedFrameTree(&node->frame_tree());
    }
  } else if ((!GetOuterWebContents() && !surface_embed_connector_) ||
             GetFocusedWebContents() == this) {
    // This is an outermost WebContents or we are currently focused so allow
    // the requested node's frame tree to be focused. The
    // (GetFocusedWebContents() == this) is needed when there are multiple
    // frame trees within an inner WebContents (ie. a GuestView with fenced
    // frames).
    //
    // A SurfaceEmbed child is not an outermost WebContents and cannot take
    // focus through a renderer request alone.
    SetFocusedFrameTree(&node->frame_tree());
  }

  CloseListenerManager::DidChangeFocusedFrame(this);
}

FrameTree* WebContentsImpl::GetOwnedDocumentPictureInPictureFrameTree() {
  if (has_picture_in_picture_document_) {
    WebContents* picture_in_picture_web_contents =
        PictureInPictureWindowController::
            GetOrCreateDocumentPictureInPictureController(this)
                ->GetChildWebContents();
    if (picture_in_picture_web_contents) {
      return &(static_cast<WebContentsImpl*>(picture_in_picture_web_contents)
                   ->GetPrimaryFrameTree());
    }
  }

  return nullptr;
}

FrameTree* WebContentsImpl::GetDocumentPictureInPictureOpenerFrameTree() {
  if (picture_in_picture_opener_) {
    return &(static_cast<WebContentsImpl*>(picture_in_picture_opener_.get())
                 ->GetPrimaryFrameTree());
  }

  return nullptr;
}

std::optional<int64_t> WebContentsImpl::GetPrivilegedContentsFeatureId() {
  if (privileged_params_) {
    return privileged_params_->feature_id;
  }
  return std::nullopt;
}

bool WebContentsImpl::IsPrivileged() {
  return privileged_params_.has_value();
}

bool WebContentsImpl::DoesWebContentsDisallowServiceWorkerControl() {
  return privileged_params_ &&
         privileged_params_->disallow_service_worker_control;
}

WebContents* WebContentsImpl::GetDocumentPictureInPictureOpener() {
  return picture_in_picture_opener_.get();
}

void WebContentsImpl::DidCallFocus() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DidCallFocus");
  // Any explicit focusing of another window while this WebContents is in
  // fullscreen can be used to confuse the user, so drop fullscreen.
  if (!ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId)) {
    return;
  }
}

void WebContentsImpl::OnAdvanceFocus(RenderFrameHostImpl* source_rfh) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnAdvanceFocus",
                        "render_frame_host", source_rfh);
  // When a RenderFrame needs to advance focus to a `blink::RemoteFrame` (by
  // hitting TAB), the `blink::RemoteFrame` sends an IPC to
  // RenderFrameProxyHost. When this RenderFrameProxyHost represents an inner
  // WebContents, the outer WebContents needs to focus the inner WebContents.
  if (GetOuterWebContents() &&
      GetOuterWebContents() == WebContents::FromRenderFrameHost(source_rfh) &&
      GetFocusedWebContents() == GetOuterWebContents()) {
    SetAsFocusedWebContentsIfNecessary();
  }
}

void WebContentsImpl::OnFocusedElementChangedInFrame(
    RenderFrameHostImpl* frame,
    const gfx::Rect& bounds_in_root_view,
    blink::mojom::FocusType focus_type,
    blink::DOMNodeIdType editable_dom_node_id) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::OnFocusedElementChangedInFrame",
                        "render_frame_host", frame);
  // Only apply focus updates from the currently focused frame. We ignore
  // updates from unfocused frames (instead of treating them as bad messages)
  // because document-local focus changes are allowed, and focus transitions
  // can race asynchronously. Focus theft is already handled and blocked (with
  // bad messages) in `RenderFrameHostImpl::VerifyFencedFrameFocusChange`.
  if (frame != GetFocusedFrame()) {
    return;
  }
  RenderWidgetHostViewBase* root_view =
      static_cast<RenderWidgetHostViewBase*>(GetRenderWidgetHostView());
  if (!root_view || !frame->GetView()) {
    return;
  }
  // Convert to screen coordinates from window coordinates by adding the
  // window's origin.
  gfx::Point origin = bounds_in_root_view.origin();
  origin += root_view->GetViewBounds().OffsetFromOrigin();
  gfx::Rect bounds_in_screen(origin, bounds_in_root_view.size());

  root_view->FocusedNodeChanged(frame->has_focused_editable_element(),
                                bounds_in_screen);

  GlobalDOMNodeId global_dom_node_id{frame->GetWeakDocumentPtr(),
                                     editable_dom_node_id};
  FocusedNodeDetails details = {frame->focused_editable_level(),
                                bounds_in_screen, focus_type,
                                global_dom_node_id};
  BrowserAccessibilityStateImpl::GetInstance()->OnFocusChangedInPage(details);
  observers_.NotifyObservers(&WebContentsObserver::OnFocusChangedInPage,
                             details);
}

bool WebContentsImpl::DidAddMessageToConsole(
    RenderFrameHostImpl* source_frame,
    blink::mojom::ConsoleMessageLevel log_level,
    const std::u16string& message,
    int32_t line_no,
    const std::u16string& source_id,
    const std::optional<std::u16string>& untrusted_stack_trace) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DidAddMessageToConsole",
                        "message", message);

  observers_.NotifyObservers(&WebContentsObserver::OnDidAddMessageToConsole,
                             source_frame, log_level, message, line_no,
                             source_id, untrusted_stack_trace);

  if (!delegate_) {
    return false;
  }
  return delegate_->DidAddMessageToConsole(this, log_level, message, line_no,
                                           source_id);
}

void WebContentsImpl::DidReceiveInputEvent(
    RenderWidgetHostImpl* render_widget_host,
    const blink::WebInputEvent& event) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DidReceiveInputEvent",
                        "render_widget_host", render_widget_host);

  if (event.GetType() == blink::WebInputEvent::Type::kMouseDown &&
      static_cast<const blink::WebMouseEvent&>(event).button ==
          blink::WebPointerProperties::Button::kBack) {
    BackNavigationLikely(content_preloading_predictor::kMouseBackButton,
                         WindowOpenDisposition::CURRENT_TAB);
  }

  HandleUserInteractionForInputEvent(render_widget_host, event);
}

void WebContentsImpl::SimulateUserInteraction(
    RenderWidgetHostImpl* render_widget_host,
    const blink::WebInputEvent& event) {
  CHECK(IsUserInteractionInputType(event.GetType()));

  HandleUserInteractionForInputEvent(render_widget_host, event);
}

void WebContentsImpl::HandleUserInteractionForInputEvent(
    RenderWidgetHostImpl* render_widget_host,
    const blink::WebInputEvent& event) {
  if (!IsUserInteractionInputType(event.GetType())) {
    return;
  }

  // Ignore unless the widget is currently in the frame tree.
  if (!HasMatchingWidgetHost(&primary_frame_tree_, render_widget_host)) {
    return;
  }

  if (event.GetType() != blink::WebInputEvent::Type::kGestureScrollBegin) {
    last_interaction_time_ = ui::EventTimeForNow();
  }

  observers_.NotifyObservers(&WebContentsObserver::DidGetUserInteraction,
                             event);
}

bool WebContentsImpl::ShouldIgnoreWebInputEvents(
    const blink::WebInputEvent& event) {
  if (ignore_input_events_count_ > 0) {
    return true;
  }
  for (const auto& callback : web_input_event_audit_callbacks_) {
    if (!callback.second.Run(event)) {
      return true;
    }
  }
  WebContentsImpl* web_contents = GetOuterWebContents();
  if (!web_contents) {
    return false;
  }
  return web_contents->ShouldIgnoreWebInputEvents(event);
}

bool WebContentsImpl::ShouldIgnoreInputEvents() {
  if (ignore_input_events_count_ > 0 ||
      !web_input_event_audit_callbacks_.empty()) {
    return true;
  }
  WebContentsImpl* web_contents = GetOuterWebContents();
  if (!web_contents) {
    return false;
  }
  return web_contents->ShouldIgnoreInputEvents();
}

void WebContentsImpl::FocusOwningWebContents(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::FocusOwningWebContents",
                        "render_widget_host", render_widget_host);

  RenderWidgetHostImpl* main_frame_widget_host =
      GetPrimaryMainFrame()->GetRenderWidgetHost();
  RenderWidgetHostImpl* focused_widget =
      GetFocusedRenderWidgetHost(main_frame_widget_host);

  if (focused_widget != render_widget_host &&
      (!focused_widget ||
       focused_widget->delegate() != render_widget_host->delegate())) {
#if BUILDFLAG(IS_ANDROID)
    if (&GetPrimaryFrameTree() != GetFocusedFrameTree()) {
      UMA_HISTOGRAM_BOOLEAN("Android.FocusChanged.FocusOwningWebContents",
                            true);
    }
#endif
    SetAsFocusedWebContentsIfNecessary();
  } else {
#if BUILDFLAG(IS_ANDROID)
    UMA_HISTOGRAM_BOOLEAN("Android.FocusChanged.FocusOwningWebContents", false);
#endif
  }
}

void WebContentsImpl::OnIgnoredUIEvent() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnIgnoredUIEvent");
  // Notify observers.
  observers_.NotifyObservers(&WebContentsObserver::DidGetIgnoredUIEvent);
}

void WebContentsImpl::RendererUnresponsive(
    RenderWidgetHostImpl* render_widget_host,
    base::RepeatingClosure hang_monitor_restarter) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive",
                        "render_widget_host", render_widget_host);
  if (ShouldIgnoreUnresponsiveRenderer()) {
    return;
  }

  bool visible = GetVisibility() == Visibility::VISIBLE;
  RecordRendererUnresponsiveMetrics(visible, render_widget_host);

  // Do not report hangs (to task manager, to hang renderer dialog, etc.) for
  // invisible tabs (like extension background page, background tabs).  See
  // https://crbug.com/881812 for rationale and for choosing the visibility
  // (rather than process priority) as the signal here.
  if (!visible) {
    return;
  }

  if (!render_widget_host->renderer_initialized()) {
    return;
  }

  CrashRepHandlingOutcome outcome =
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kNoErrorDialogs)
          ? CrashRepHandlingOutcome::kDropped
          : CrashRepHandlingOutcome::kPotentiallyQueued;
  base::UmaHistogramEnumeration(
      "ReportingAndNEL.UnresponsiveRenderer.CrashReportOutcome", outcome);

  if (base::FeatureList::IsEnabled(
          blink::features::kDocumentPolicyIncludeJSCallStacksInCrashReports) &&
      this->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
    RenderProcessHost* rph = render_widget_host->GetProcess();
    if (rph) {
      RenderProcessHostImpl* process_host =
          static_cast<RenderProcessHostImpl*>(rph);
      process_host->InterruptJavaScriptIsolateAndCollectCallStack();
    }
  }

  observers_.NotifyObservers(&WebContentsObserver::OnRendererUnresponsive,
                             render_widget_host->GetProcess());
  if (delegate_) {
    delegate_->RendererUnresponsive(this, render_widget_host,
                                    std::move(hang_monitor_restarter));
  }
}

void WebContentsImpl::RendererResponsive(
    RenderWidgetHostImpl* render_widget_host) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RenderResponsive",
                        "render_widget_host", render_widget_host);
  observers_.NotifyObservers(&WebContentsObserver::OnRendererResponsive,
                             render_widget_host->GetProcess());

  if (delegate_) {
    delegate_->RendererResponsive(this, render_widget_host);
  }
}

void WebContentsImpl::BeforeUnloadFiredFromRenderManager(
    bool proceed,
    bool* proceed_to_fire_unload) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::BeforeUnloadFiredFromRenderManager");
  observers_.NotifyObservers(&WebContentsObserver::BeforeUnloadFired, proceed);
  if (delegate_) {
    delegate_->BeforeUnloadFired(this, proceed, proceed_to_fire_unload);
  }
  // Note: |this| might be deleted at this point.
}

void WebContentsImpl::CancelDialogManagerDialogs(bool reset_state) {
  if (!created_dialog_since_last_cancel_) {
    return;
  }
  // We only reset `created_dialog_since_last_cancel_` if reset_state
  // is true.
  if (reset_state) {
    created_dialog_since_last_cancel_ = false;
  }
  if (!delegate_) {
    return;
  }
  JavaScriptDialogManager* dialog_manager =
      delegate_->GetJavaScriptDialogManager(this);
  if (!dialog_manager) {
    return;
  }
  dialog_manager->CancelDialogs(this, reset_state);
}

void WebContentsImpl::CancelModalDialogsForRenderManager() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::CancelModalDialogsForRenderManager");
  // We need to cancel modal dialogs when doing a process swap, since the load
  // deferrer would prevent us from swapping out. We also clear the state
  // because this is a cross-process navigation, which means that it's a new
  // site that should not have to pay for the sins of its predecessor.
  //
  // Note that we don't bother telling |browser_plugin_embedder_| because the
  // cross-process navigation will either destroy the browser plugins or not
  // require their dialogs to close.
  CancelDialogManagerDialogs(/*reset_state=*/true);
}

void WebContentsImpl::NotifyPrimaryPageWillBeDeactivated(PageImpl& page) {
  observers_.NotifyObservers(&WebContentsObserver::PrimaryPageWillBeDeactivated,
                             page);
}

void WebContentsImpl::NotifySwappedFromRenderManager(
    RenderFrameHostImpl* old_frame,
    RenderFrameHostImpl* new_frame) {
  TRACE_EVENT2("content", "WebContentsImpl::NotifySwappedFromRenderManager",
               "old_render_frame_host", old_frame, "new_render_frame_host",
               new_frame);
  DCHECK_NE(new_frame->lifecycle_state(),
            RenderFrameHostImpl::LifecycleStateImpl::kSpeculative);

  // Only fire RenderViewHostChanged if it is related to our FrameTree, as
  // observers can not deal with events coming from non-primary FrameTree.
  // TODO(crbug.com/40165060): Update observers to deal with the events,
  // and fire events for all frame trees.
  if (new_frame->IsInPrimaryMainFrame()) {
    // The |new_frame| and its various compadres are already swapped into place
    // for the WebContentsImpl when this method is called.
    DCHECK_EQ(
        primary_frame_tree_.root()->render_manager()->GetRenderWidgetHostView(),
        new_frame->GetView());

    RenderViewHost* old_rvh =
        old_frame ? old_frame->GetRenderViewHost() : nullptr;
    RenderViewHost* new_rvh = new_frame->GetRenderViewHost();
    // |old_rvh| and |new_rvh| might be equal when navigating from a crashed
    // RenderFrameHost to a new same-site one. With RenderDocument, this will
    // happen for every same-site navigation.
    if (old_rvh != new_rvh) {
      NotifyViewSwapped(old_rvh, new_rvh);
    }

    auto* rwhv = static_cast<RenderWidgetHostViewBase*>(new_frame->GetView());
    if (rwhv) {
      rwhv->SetMainFrameAXTreeID(new_frame->GetAXTreeID());

      // The RenderWidgetHostView for the speculative RenderFrameHost is not
      // resized with the current RenderFrameHost while a navigation is
      // pending. So when we swap in the main frame, we need to update the
      // RenderWidgetHostView's size.
      //
      // Historically, this was done to fix b/1079768 for interstitials.
      rwhv->SetSize(GetSizeForMainFrame());
    }

    NotifyPrimaryMainFrameProcessIsAlive();
  }

  NotifyFrameSwapped(old_frame, new_frame);
}

void WebContentsImpl::PrimaryMainFrameCommitted(
    RenderFrameHostImpl* new_frame) {
  CHECK(new_frame->GetView() == GetRenderWidgetHostView());
  SetPrimaryMainFrameViewVisibility(GetVisibility());
}

void WebContentsImpl::NotifySwappedFromRenderManagerWithoutFallbackContent(
    RenderFrameHostImpl* new_frame) {
  auto* rwhv = static_cast<RenderWidgetHostViewBase*>(new_frame->GetView());
  if (rwhv) {
    rwhv->ClearFallbackSurfaceForCommitPending();
  }
}

void WebContentsImpl::NotifyMainFrameSwappedFromRenderManager(
    RenderFrameHostImpl* old_frame,
    RenderFrameHostImpl* new_frame) {
  // Only fire RenderViewHostChanged if it is
  // related to our FrameTree, as observers cannot deal with events coming
  // from non-primary FrameTree.
  // TODO(crbug.com/40165060): Update observers to deal with the events,
  // and fire events for all frame trees.
  if (!new_frame->IsInPrimaryMainFrame()) {
    return;
  }
  NotifyViewSwapped(old_frame ? old_frame->GetRenderViewHost() : nullptr,
                    new_frame->GetRenderViewHost());
}

void WebContentsImpl::CreateRenderWidgetHostViewForRenderManager(
    RenderViewHost* render_view_host) {
  OPTIONAL_TRACE_EVENT1(
      "content", "WebContentsImpl::CreateRenderWidgetHostViewForRenderManager",
      "render_view_host", render_view_host);

  bool is_inner_frame_tree = [&]() {
    FrameTree* frame_tree =
        static_cast<RenderViewHostImpl*>(render_view_host)->frame_tree();
    switch (frame_tree->type()) {
      case FrameTree::Type::kPrimary:
      case FrameTree::Type::kPrerender:
        return false;
      case FrameTree::Type::kFencedFrame:
      case FrameTree::Type::kGuest:
        return true;
    }
  }();

  if (is_inner_frame_tree) {
    WebContentsViewChildFrame::CreateRenderWidgetHostViewForInnerFrameTree(
        this, render_view_host->GetWidget());
  } else {
    RenderWidgetHostViewBase* rwh_view =
        view_->CreateViewForWidget(render_view_host->GetWidget());
    view_->SetOverscrollControllerEnabled(CanOverscrollContent());
    rwh_view->SetSize(GetSizeForMainFrame());
  }

  if (opt_out_frame_eviction_) {
    if (RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
            render_view_host->GetWidget()->GetView())) {
      view->OptOutFrameEviction();
    }
  }
}

void WebContentsImpl::ReattachOuterDelegateIfNeeded() {
  if (node_.outer_web_contents()) {
    ReattachToOuterWebContentsFrame();
  }
}

bool WebContentsImpl::CreateRenderViewForRenderManager(
    RenderViewHost* render_view_host,
    const std::optional<blink::FrameToken>& opener_frame_token,
    RenderFrameProxyHost* proxy_host,
    const std::optional<base::UnguessableToken>& navigation_metrics_token) {
  TRACE_EVENT1("browser,navigation",
               "WebContentsImpl::CreateRenderViewForRenderManager",
               "render_view_host", render_view_host);
  auto* rvh_impl = static_cast<RenderViewHostImpl*>(render_view_host);
  // Observers should not destroy the WebContents here or we will crash as the
  // stack unwinds. See crbug.com/1181043.
  base::AutoReset<bool> scope(&prevent_destruction_, true);

  if (!proxy_host) {
    CreateRenderWidgetHostViewForRenderManager(render_view_host);
  }

  const auto proxy_routing_id =
      proxy_host ? proxy_host->GetRoutingID() : IPC::mojom::kRoutingIdNone;
  // TODO(crbug.com/40166243): Given MPArch, should we pass
  // opened_by_another_window_ for non primary FrameTrees?
  if (!rvh_impl->CreateRenderView(opener_frame_token, proxy_routing_id,
                                  opened_by_another_window_,
                                  navigation_metrics_token)) {
    return false;
  }
  // `CreateRenderView` initializes visibility from `IsHidden`, which cannot
  // represent `kHiddenButPainting`. Set the full visibility state explicitly.
  if (!did_first_set_visible_ && initially_hidden_but_painting_) {
    rvh_impl->SetFrameTreeVisibility(PageVisibilityState::kHiddenButPainting);
  }

  // If `render_view_host` is for an inner WebContents, ensure that its
  // RenderWidgetHostView is properly reattached to the outer WebContents. Note
  // that this should only be done when `render_view_host` is already the
  // current RenderViewHost in this WebContents.  If it isn't, the reattachment
  // will happen later when `render_view_host` becomes current as part of
  // committing the speculative RenderFrameHost it's associated with.
  if (!proxy_host && render_view_host == GetRenderViewHost()) {
    ReattachOuterDelegateIfNeeded();
  }

#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
  // Force a ViewMsg_Resize to be sent, needed to make plugins show up on
  // linux. See crbug.com/83941.
  RenderWidgetHostView* rwh_view = render_view_host->GetWidget()->GetView();
  if (rwh_view) {
    if (RenderWidgetHost* render_widget_host =
            rwh_view->GetRenderWidgetHost()) {
      render_widget_host->SynchronizeVisualProperties();
    }
  }
#endif

  return true;
}

#if BUILDFLAG(IS_ANDROID)

base::android::ScopedJavaLocalRef<jobject>
WebContentsImpl::GetJavaWebContents() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  return GetWebContentsAndroid()->GetJavaObject();
}

base::android::ScopedJavaLocalRef<jthrowable>
WebContentsImpl::GetJavaCreatorLocation() {
  return base::android::ScopedJavaLocalRef<jthrowable>(java_creator_location_);
}

WebContentsAndroid* WebContentsImpl::GetWebContentsAndroid() {
  if (!web_contents_android_) {
    web_contents_android_ = std::make_unique<WebContentsAndroid>(this);
  }
  return web_contents_android_.get();
}

void WebContentsImpl::ClearWebContentsAndroid() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  web_contents_android_.reset();
}

void WebContentsImpl::ActivateNearestFindResult(float x, float y) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::ActivateNearestFindResult");
  GetOrCreateFindRequestManager()->ActivateNearestFindResult(x, y);
}

void WebContentsImpl::RequestFindMatchRects(int current_version) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::RequestFindMatchRects");
  GetOrCreateFindRequestManager()->RequestFindMatchRects(current_version);
}

service_manager::InterfaceProvider* WebContentsImpl::GetJavaInterfaces() {
  if (!java_interfaces_) {
    mojo::PendingRemote<service_manager::mojom::InterfaceProvider> provider;
    BindInterfaceRegistryForWebContents(
        provider.InitWithNewPipeAndPassReceiver(), this);
    java_interfaces_ = std::make_unique<service_manager::InterfaceProvider>(
        base::SingleThreadTaskRunner::GetCurrentDefault());
    java_interfaces_->Bind(std::move(provider));
  }
  return java_interfaces_.get();
}

void WebContentsImpl::SetSelectionPopupDelegate(
    std::unique_ptr<SelectionPopupDelegate> delegate) {
  SelectionPopupController::FromWebContents(*this)->SetDelegate(
      std::move(delegate));
}

#endif

bool WebContentsImpl::CompletedFirstVisuallyNonEmptyPaint() {
  return GetPrimaryPage().did_first_visually_non_empty_paint();
}

void WebContentsImpl::OnDidDownloadImage(
    base::WeakPtr<RenderFrameHostImpl> rfh,
    ImageDownloadCallback callback,
    int id,
    const GURL& image_url,
    int32_t http_status_code,
    const std::vector<SkBitmap>& images,
    const std::vector<gfx::Size>& original_image_sizes) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnDidDownloadImage",
                        "image_url", image_url);

  // Guard against buggy or compromised renderers that could violate the API
  // contract that |images| and |original_image_sizes| must have the same
  // length.
  if (images.size() != original_image_sizes.size()) {
    if (rfh) {
      ReceivedBadMessage(rfh->GetProcess(),
                         bad_message::WCI_INVALID_DOWNLOAD_IMAGE_RESULT);
    }
    // Respond with a 400 to indicate that something went wrong.
    std::move(callback).Run(id, 400, image_url, std::vector<SkBitmap>(),
                            std::vector<gfx::Size>());
    return;
  }

  std::move(callback).Run(id, http_status_code, image_url, images,
                          original_image_sizes);
}

int WebContentsImpl::GetNextDownloadId() {
  static int next_image_download_id = 0;
  return ++next_image_download_id;
}

void WebContentsImpl::OnDialogClosed(int render_process_id,
                                     int render_frame_id,
                                     JavaScriptDialogCallback response_callback,
                                     base::ScopedClosureRunner fullscreen_block,
                                     bool dialog_was_suppressed,
                                     bool success,
                                     const std::u16string& user_input) {
  RenderFrameHostImpl* rfh =
      RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
  // The user confirms and closes a dialog even after the page has navigated
  // away and got into BackForwardCache.
  DCHECK(!rfh || rfh->IsActive() || rfh->IsInBackForwardCache());
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnDialogClosed",
                        "render_frame_host", rfh);
  last_dialog_suppressed_ = dialog_was_suppressed;
  fullscreen_block.RunAndReset();

  javascript_dialog_dismiss_notifier_.reset();

  if (is_showing_before_unload_dialog_ && !success) {
    // It is possible for the current RenderFrameHost to have changed in the
    // meantime.  Do not reset the navigation state in that case.
    if (rfh && rfh == rfh->frame_tree_node()->current_frame_host()) {
      rfh->frame_tree_node()->BeforeUnloadCanceled();
      rfh->frame_tree()->controller().DiscardNonCommittedEntries();
    }

    // Update the URL display either way, to avoid showing a stale URL.
    NotifyNavigationStateChanged(INVALIDATE_TYPE_URL);

    observers_.NotifyObservers(
        &WebContentsObserver::BeforeUnloadDialogCancelled);
  }

  std::move(response_callback).Run(success, user_input);

  std::vector<protocol::PageHandler*> page_handlers =
      protocol::PageHandler::EnabledForWebContents(this);
  for (auto* handler : page_handlers) {
    handler->DidCloseJavaScriptDialog(rfh->devtools_frame_token(), success,
                                      user_input);
  }

  is_showing_javascript_dialog_ = false;
  is_showing_before_unload_dialog_ = false;
}

RenderFrameHostManager* WebContentsImpl::GetRenderManager() {
  return primary_frame_tree_.root()->render_manager();
}

BrowserPluginGuest* WebContentsImpl::GetBrowserPluginGuest() const {
  return browser_plugin_guest_.get();
}

void WebContentsImpl::SetBrowserPluginGuest(
    std::unique_ptr<BrowserPluginGuest> guest) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetBrowserPluginGuest");
  DCHECK(!browser_plugin_guest_);
  DCHECK(guest);
  browser_plugin_guest_ = std::move(guest);
}

base::UnguessableToken WebContentsImpl::GetAudioGroupId() {
  return GetAudioStreamFactory()->group_id();
}

const std::vector<blink::mojom::FaviconURLPtr>&
WebContentsImpl::GetFaviconURLs() {
  return GetPrimaryMainFrame()->FaviconURLs();
}

void WebContentsImpl::Resize(const gfx::Rect& new_bounds) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::Resize");
  // If we're embedded, the HTML element has control over the size (and
  // resizing the platform view will resize the embedder, not this).
  // TODO(crbug.com/505317114): Refactor this so this is in the View, which
  // already has the appropriate platform split.
  if (surface_embed_connector_) {
    return;
  }
  if (view_) {
    view_->Resize(new_bounds);
  }
}

gfx::Size WebContentsImpl::GetSize() {
  return view_ ? view_->GetSize() : gfx::Size();
}

gfx::Rect WebContentsImpl::GetWindowsControlsOverlayRect() const {
  return window_controls_overlay_rect_;
}

void WebContentsImpl::UpdateWindowControlsOverlay(
    const gfx::Rect& bounding_rect) {
  if (window_controls_overlay_rect_ == bounding_rect) {
    return;
  }

  window_controls_overlay_rect_ = bounding_rect;

  // Updates to the |window_controls_overlay_rect_| are sent via
  // the VisualProperties message.
  if (RenderWidgetHost* render_widget_host =
          GetPrimaryMainFrame()->GetRenderWidgetHost()) {
    render_widget_host->SynchronizeVisualProperties();
  }
}

BrowserPluginEmbedder* WebContentsImpl::GetBrowserPluginEmbedder() const {
  return browser_plugin_embedder_.get();
}

void WebContentsImpl::CreateBrowserPluginEmbedderIfNecessary() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::CreateBrowserPluginEmbedderIfNecessary");
  if (browser_plugin_embedder_) {
    return;
  }
  browser_plugin_embedder_.reset(BrowserPluginEmbedder::Create(this));
}

gfx::Size WebContentsImpl::GetSizeForMainFrame() {
  if (delegate_) {
    // The delegate has a chance to specify a size independent of the UI.
    gfx::Size delegate_size = delegate_->GetSizeForNewRenderView(this);
    if (!delegate_size.IsEmpty()) {
      return delegate_size;
    }
  }

  // Device emulation, when enabled, can specify a size independent of the UI.
  if (!device_emulation_size_.IsEmpty()) {
    return device_emulation_size_;
  }

  return GetContainerBounds().size();
}

void WebContentsImpl::OnFrameTreeNodeDestroyed(FrameTreeNode* node) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnFrameTreeNodeDestroyed",
                        "node", node);
  // If we are the focused frame tree and are destroying the root node
  // reassign the focused frame tree node.
  if (!node->parent() && GetFocusedFrameTree() == &node->frame_tree() &&
      &node->frame_tree() != &primary_frame_tree_) {
    FrameTreeNode* frame_in_embedder =
        node->render_manager()->GetOuterDelegateNode();
    SetFocusedFrameTree(frame_in_embedder ? &frame_in_embedder->frame_tree()
                                          : &primary_frame_tree_);
  }

  observers_.NotifyObservers(&WebContentsObserver::FrameDeleted,
                             node->frame_tree_node_id());
}

void WebContentsImpl::OnPreferredSizeChanged(const gfx::Size& old_size) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnPreferredSizeChanged");
  if (!delegate_) {
    return;
  }
  const gfx::Size new_size = GetPreferredSize();
  if (new_size != old_size) {
    delegate_->UpdatePreferredSize(this, new_size);
  }
}

FindRequestManager* WebContentsImpl::GetFindRequestManager() {
  for (auto* contents = this; contents;
       contents = contents->GetOuterWebContents()) {
    if (contents->find_request_manager_) {
      return contents->find_request_manager_.get();
    }
  }

  return nullptr;
}

FindRequestManager* WebContentsImpl::GetOrCreateFindRequestManager() {
  if (FindRequestManager* manager = GetFindRequestManager()) {
    return manager;
  }

  DCHECK(!browser_plugin_guest_ || GetOuterWebContents());

  // No existing FindRequestManager found, so one must be created.
  find_request_manager_ = std::make_unique<FindRequestManager>(this);

  // Concurrent find sessions must not overlap, so destroy any existing
  // FindRequestManagers in any inner WebContentses.
  for (WebContents* contents : GetWebContentsAndAllInner()) {
    auto* web_contents_impl = static_cast<WebContentsImpl*>(contents);
    if (web_contents_impl == this) {
      continue;
    }
    if (web_contents_impl->find_request_manager_) {
      web_contents_impl->find_request_manager_->StopFinding(
          STOP_FIND_ACTION_CLEAR_SELECTION);
      web_contents_impl->find_request_manager_.release();
    }
  }

  return find_request_manager_.get();
}

void WebContentsImpl::NotifyFindReply(int request_id,
                                      int number_of_matches,
                                      const gfx::Rect& selection_rect,
                                      int active_match_ordinal,
                                      bool final_update) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::NotifyFindReply");
  if (delegate_ && !IsBeingDestroyed() &&
      !GetPrimaryMainFrame()->GetProcess()->FastShutdownStarted()) {
    delegate_->FindReply(this, request_id, number_of_matches, selection_rect,
                         active_match_ordinal, final_update);
  }
}

void WebContentsImpl::IncrementBluetoothConnectedDeviceCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::IncrementBluetoothConnectedDeviceCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }
  // Notify for UI updates if the state changes.
  bluetooth_connected_device_count_++;
  if (bluetooth_connected_device_count_ == 1) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kBluetoothConnected,
                             /*used=*/true);
  }
}

void WebContentsImpl::DecrementBluetoothConnectedDeviceCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::DecrementBluetoothConnectedDeviceCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }
  // Notify for UI updates if the state changes.
  DCHECK_NE(bluetooth_connected_device_count_, 0u);
  bluetooth_connected_device_count_--;
  if (bluetooth_connected_device_count_ == 0) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kBluetoothConnected,
                             /*used=*/false);
  }
}

void WebContentsImpl::IncrementBluetoothScanningSessionsCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::IncrementBluetoothScanningSessionsCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the state changes.
  bluetooth_scanning_sessions_count_++;
  if (bluetooth_scanning_sessions_count_ == 1) {
    NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
  }
}

void WebContentsImpl::DecrementBluetoothScanningSessionsCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::DecrementBluetoothScanningSessionsCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  DCHECK_NE(0u, bluetooth_scanning_sessions_count_);
  bluetooth_scanning_sessions_count_--;
  if (bluetooth_scanning_sessions_count_ == 0) {
    NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
  }
}

void WebContentsImpl::IncrementSerialActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::IncrementSerialActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the state changes.
  serial_active_frame_count_++;
  if (serial_active_frame_count_ == 1) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kSerial, /*used=*/true);
  }
}

void WebContentsImpl::DecrementSerialActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::DecrementSerialActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the state changes.
  DCHECK_NE(0u, serial_active_frame_count_);
  serial_active_frame_count_--;
  if (serial_active_frame_count_ == 0) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kSerial,
                             /*used=*/false);
  }
}

void WebContentsImpl::IncrementHidActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::IncrementHidActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from zero to
  // non-zero.
  hid_active_frame_count_++;
  if (hid_active_frame_count_ == 1) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kHID, /*used=*/true);
  }
}

void WebContentsImpl::DecrementHidActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::DecrementHidActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from non-zero
  // to zero.
  DCHECK_NE(0u, hid_active_frame_count_);
  hid_active_frame_count_--;
  if (hid_active_frame_count_ == 0) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kHID, /*used=*/false);
  }
}

void WebContentsImpl::IncrementGeolocationActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::IncrementGeolocationActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from zero to
  // non-zero.
  geolocation_active_frame_count_++;
  if (geolocation_active_frame_count_ == 1) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kGeolocation,
                             /*used=*/true);
  }
}

void WebContentsImpl::DecrementGeolocationActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::DecrementGeolocationActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from non-zero
  // to zero.
  DCHECK_NE(0u, geolocation_active_frame_count_);
  geolocation_active_frame_count_--;
  if (geolocation_active_frame_count_ == 0) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kGeolocation,
                             /*used=*/false);
  }
}

void WebContentsImpl::OnCapabilityTypesChanged(
    WebContentsCapabilityType capability_type,
    bool used) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnCapabilityTypesChanged");
  NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
  observers_.NotifyObservers(&WebContentsObserver::OnCapabilityTypesChanged,
                             capability_type, used);
}

void WebContentsImpl::IncrementUsbActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::IncrementUsbActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from zero to
  // non-zero.
  usb_active_frame_count_++;
  if (usb_active_frame_count_ == 1) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kUSB, /*used=*/true);
  }
}

void WebContentsImpl::DecrementUsbActiveFrameCount() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::DecrementUsbActiveFrameCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the active frame count transitions from non-zero
  // to zero.
  DCHECK_NE(0u, usb_active_frame_count_);
  usb_active_frame_count_--;
  if (usb_active_frame_count_ == 0) {
    OnCapabilityTypesChanged(WebContentsCapabilityType::kUSB, /*used=*/false);
  }
}

void WebContentsImpl::IncrementFileSystemAccessHandleCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::IncrementFileSystemAccessHandleCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the state changes. Need both TYPE_TAB and TYPE_URL
  // to update both the tab-level usage indicator and the usage indicator in the
  // omnibox.
  file_system_access_handle_count_++;
  if (file_system_access_handle_count_ == 1) {
    NotifyNavigationStateChanged(static_cast<content::InvalidateTypes>(
        INVALIDATE_TYPE_TAB | INVALIDATE_TYPE_URL));
  }
}

void WebContentsImpl::DecrementFileSystemAccessHandleCount() {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::DecrementFileSystemAccessHandleCount");
  // Trying to invalidate the tab state while being destroyed could result in a
  // use after free.
  if (IsBeingDestroyed()) {
    return;
  }

  // Notify for UI updates if the state changes. Need both TYPE_TAB and TYPE_URL
  // to update both the tab-level usage indicator and the usage indicator in the
  // omnibox.
  DCHECK_NE(0u, file_system_access_handle_count_);
  file_system_access_handle_count_--;
  if (file_system_access_handle_count_ == 0) {
    NotifyNavigationStateChanged(static_cast<content::InvalidateTypes>(
        INVALIDATE_TYPE_TAB | INVALIDATE_TYPE_URL));
  }
}

void WebContentsImpl::SetHasPersistentVideo(bool has_persistent_video) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::SetHasPersistentVideo",
                        "has_persistent_video", has_persistent_video,
                        "had_persistent_value", has_persistent_video_);
  if (has_persistent_video_ == has_persistent_video) {
    return;
  }

  has_persistent_video_ = has_persistent_video;
  NotifyPreferencesChanged();
  media_web_contents_observer()->RequestPersistentVideo(has_persistent_video);

  // This is Picture-in-Picture on Android S+.
  if (auto* view = GetRenderWidgetHostView()) {
    static_cast<RenderWidgetHostViewBase*>(view)->SetHasPersistentVideo(
        has_persistent_video);
  }
}

void WebContentsImpl::SetSpatialNavigationDisabled(bool disabled) {
  OPTIONAL_TRACE_EVENT2(
      "content", "WebContentsImpl::SetSpatialNavigationDisabled", "disabled",
      disabled, "was_disabled", is_spatial_navigation_disabled_);
  if (is_spatial_navigation_disabled_ == disabled) {
    return;
  }

  is_spatial_navigation_disabled_ = disabled;
  NotifyPreferencesChanged();
}

#if BUILDFLAG(IS_ANDROID)
void WebContentsImpl::SetStylusHandwritingEnabled(bool enabled) {
  if (stylus_handwriting_enabled_ == enabled) {
    return;
  }
  stylus_handwriting_enabled_ = enabled;
  NotifyPreferencesChanged();
}
#endif  // BUILDFLAG(IS_ANDROID)

PictureInPictureResult WebContentsImpl::EnterPictureInPicture() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterPictureInPicture");
  return delegate_ ? delegate_->EnterPictureInPicture(this)
                   : PictureInPictureResult::kNotSupported;
}

void WebContentsImpl::ExitPictureInPicture() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ExitPictureInPicture");
  if (delegate_) {
    delegate_->ExitPictureInPicture();
  }
}

void WebContentsImpl::OnXrHasRenderTarget(
    const viz::FrameSinkId& frame_sink_id) {
  xr_render_target_ = frame_sink_id;
  observers_.NotifyObservers(&WebContentsObserver::CaptureTargetChanged);
}

WebContentsImpl::CaptureTarget WebContentsImpl::GetCaptureTarget() {
  // We don't provide a view for the Android AR capturer. This is not a problem
  // because it is currently only used by the MouseCursorOverlayController,
  // which isn't used on Android anyway.
  if (xr_render_target_.is_valid()) {
    return CaptureTarget{.sink_id = xr_render_target_};
  }

  RenderWidgetHostView* host_view = GetRenderWidgetHostView();
  if (!host_view) {
    return {};
  }

  RenderWidgetHostViewBase* base_view =
      static_cast<RenderWidgetHostViewBase*>(host_view);
  return CaptureTarget{.sink_id = base_view->GetFrameSinkId(),
                       .view = host_view->GetNativeView()};
}

#if BUILDFLAG(IS_ANDROID)
void WebContentsImpl::NotifyFindMatchRectsReply(
    int version,
    const std::vector<gfx::RectF>& rects,
    const gfx::RectF& active_rect) {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::NotifyFindMatchRectsReply");
  if (delegate_) {
    delegate_->FindMatchRectsReply(this, version, rects, active_rect);
  }
}
#endif

void WebContentsImpl::SetForceDisableOverscrollContent(bool force_disable) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SetForceDisableOverscrollContent",
                        "force_disable", force_disable);
  force_disable_overscroll_content_ = force_disable;
  if (view_) {
    view_->SetOverscrollControllerEnabled(CanOverscrollContent());
  }
}

bool WebContentsImpl::SetDeviceEmulationSize(const gfx::Size& new_size) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetDeviceEmulationSize");
  device_emulation_size_ = new_size;
  RenderWidgetHostView* rwhv = GetPrimaryMainFrame()->GetView();

  const gfx::Size current_size = rwhv->GetViewBounds().size();
  if (view_size_before_emulation_.IsEmpty()) {
    view_size_before_emulation_ = current_size;
  }

  if (current_size != new_size) {
    rwhv->SetSize(new_size);
  }

  return current_size != new_size;
}

void WebContentsImpl::ClearDeviceEmulationSize() {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::ClearDeviceEmulationSize");
  RenderWidgetHostView* rwhv = GetPrimaryMainFrame()->GetView();
  // WebContentsView could get resized during emulation, which also resizes
  // RWHV. If it happens, assume user would like to keep using the size after
  // emulation.
  // TODO(jzfeng): Prohibit resizing RWHV through any other means (at least when
  // WebContentsView size changes).
  if (!view_size_before_emulation_.IsEmpty() && rwhv &&
      rwhv->GetViewBounds().size() == device_emulation_size_) {
    rwhv->SetSize(view_size_before_emulation_);
  }
  device_emulation_size_ = gfx::Size();
  view_size_before_emulation_ = gfx::Size();
}

ForwardingAudioStreamFactory* WebContentsImpl::GetAudioStreamFactory() {
  if (!audio_stream_factory_) {
    audio_stream_factory_ = CreateAudioStreamFactory();
  }
  return audio_stream_factory_.get();
}

std::unique_ptr<ForwardingAudioStreamFactory>
WebContentsImpl::CreateAudioStreamFactory(
    base::PassKey<GuestPageHolderImpl> pass_key) {
  return CreateAudioStreamFactory();
}

std::unique_ptr<ForwardingAudioStreamFactory>
WebContentsImpl::CreateAudioStreamFactory() {
  std::unique_ptr<AudioStreamBrokerFactory> broker_factory;
  if (delegate_) {
    broker_factory = delegate_->CreateAudioStreamBrokerFactory(this);
  }
  if (!broker_factory) {
    broker_factory = AudioStreamBrokerFactory::CreateImpl();
  }
  return std::make_unique<ForwardingAudioStreamFactory>(
      this, std::move(broker_factory));
}

void WebContentsImpl::MediaStartedPlaying(
    const WebContentsObserver::MediaPlayerInfo& media_info,
    const MediaPlayerId& id) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MediaStartedPlaying");
  observers_.NotifyObservers(&WebContentsObserver::MediaStartedPlaying,
                             media_info, id);
}

void WebContentsImpl::MediaStoppedPlaying(
    const WebContentsObserver::MediaPlayerInfo& media_info,
    const MediaPlayerId& id,
    WebContentsObserver::MediaStoppedReason reason) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MediaStoppedPlaying");
  observers_.NotifyObservers(&WebContentsObserver::MediaStoppedPlaying,
                             media_info, id, reason);
}

void WebContentsImpl::MediaMetadataChanged(
    const WebContentsObserver::MediaPlayerInfo& media_info,
    const MediaPlayerId& id) {
  observers_.NotifyObservers(&WebContentsObserver::MediaMetadataChanged,
                             media_info, id);
}

void WebContentsImpl::MediaResized(const gfx::Size& size,
                                   const MediaPlayerId& id) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MediaResized");
  cached_video_sizes_[id] = size;

  observers_.NotifyObservers(&WebContentsObserver::MediaResized, size, id);
}

void WebContentsImpl::MediaEffectivelyFullscreenChanged(bool is_fullscreen) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::MediaEffectivelyFullscreenChanged",
                        "is_fullscreen", is_fullscreen);
  observers_.NotifyObservers(
      &WebContentsObserver::MediaEffectivelyFullscreenChanged, is_fullscreen);
}

void WebContentsImpl::MediaDestroyed(const MediaPlayerId& id) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::MediaDestroyed");
  observers_.NotifyObservers(&WebContentsObserver::MediaDestroyed, id);
}

void WebContentsImpl::MediaSessionCreated(MediaSession* media_session) {
  observers_.NotifyObservers(&WebContentsObserver::MediaSessionCreated,
                             media_session);
}

int WebContentsImpl::GetCurrentlyPlayingVideoCount() const {
  return media_web_contents_observer_->GetCurrentlyPlayingVideoCount();
}

std::optional<gfx::Size> WebContentsImpl::GetFullscreenVideoSize() {
  std::optional<MediaPlayerId> id =
      media_web_contents_observer_->GetFullscreenVideoMediaPlayerId();
  if (id) {
    if (auto it = cached_video_sizes_.find(id.value());
        it != cached_video_sizes_.end()) {
      return std::optional<gfx::Size>(it->second);
    }
  }
  return std::nullopt;
}

void WebContentsImpl::AudioContextPlaybackStarted(RenderFrameHostImpl* host,
                                                  int context_id) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::AudioContextPlaybackStarted",
                        "render_frame_host", host);
  WebContentsObserver::AudioContextId audio_context_id(host, context_id);
  observers_.NotifyObservers(&WebContentsObserver::AudioContextPlaybackStarted,
                             audio_context_id);
}

void WebContentsImpl::AudioContextPlaybackStopped(RenderFrameHostImpl* host,
                                                  int context_id) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::AudioContextPlaybackStopped",
                        "render_frame_host", host);
  WebContentsObserver::AudioContextId audio_context_id(host, context_id);
  observers_.NotifyObservers(&WebContentsObserver::AudioContextPlaybackStopped,
                             audio_context_id);
}

void WebContentsImpl::OnFrameAudioStateChanged(RenderFrameHostImpl* host,
                                               bool is_audible) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnFrameAudioStateChanged",
                        "render_frame_host", host, "is_audible", is_audible);
  observers_.NotifyObservers(&WebContentsObserver::OnFrameAudioStateChanged,
                             host, is_audible);
}

void WebContentsImpl::OnFrameVisibilityChanged(
    RenderFrameHostImpl* host,
    blink::mojom::FrameVisibility visibility) {
  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::OnFrameVisibilityChanged",
                        "render_frame_host", host, "visibility", visibility);
  observers_.NotifyObservers(&WebContentsObserver::OnFrameVisibilityChanged,
                             host, visibility);
}

void WebContentsImpl::OnRemoteSubframeViewportIntersectionStateChanged(
    RenderFrameHostImpl* host,
    const blink::mojom::ViewportIntersectionState&
        viewport_intersection_state) {
  OPTIONAL_TRACE_EVENT2(
      "content",
      "WebContentsImpl::OnRemoteSubframeViewportIntersectionStateChanged",
      "render_frame_host", host, "viewport_intersection_state",
      viewport_intersection_state);
  observers_.NotifyObservers(
      &WebContentsObserver::OnRemoteSubframeViewportIntersectionStateChanged,
      host, viewport_intersection_state);
}

void WebContentsImpl::OnFrameIsCapturingMediaStreamChanged(
    RenderFrameHostImpl* host,
    bool is_capturing_media_stream) {
  observers_.NotifyObservers(
      &WebContentsObserver::OnFrameIsCapturingMediaStreamChanged, host,
      is_capturing_media_stream);
}

// Cf. `GetProspectiveOuterDocument` which applies to the same situation, but is
// for ascending.
std::vector<FrameTreeNode*> WebContentsImpl::GetUnattachedOwnedNodes(
    RenderFrameHostImpl* owner) {
  if (owner->GetParent()) {
    return {};
  }
  BrowserPluginGuestManager* guest_manager =
      GetBrowserContext()->GetGuestManager();
  if (!guest_manager) {
    return {};
  }
  std::vector<FrameTreeNode*> unattached_owned_nodes;
  if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
    guest_manager->ForEachUnattachedGuestPage(
        owner->GetPage(), [&](GuestPageHolder& guest_page) {
          unattached_owned_nodes.push_back(
              static_cast<GuestPageHolderImpl&>(guest_page)
                  .frame_tree()
                  .root());
        });
  } else if (owner == GetPrimaryMainFrame()) {
    guest_manager->ForEachUnattachedGuestContents(
        this, [&](WebContents* guest_contents) {
          unattached_owned_nodes.push_back(
              static_cast<WebContentsImpl*>(guest_contents)
                  ->primary_frame_tree_.root());
        });
  }
  return unattached_owned_nodes;
}

void WebContentsImpl::IsClipboardPasteAllowedByPolicy(
    const ClipboardEndpoint& source,
    const ClipboardEndpoint& destination,
    const ui::ClipboardMetadata& metadata,
    ClipboardPasteData clipboard_paste_data,
    IsClipboardPasteAllowedCallback callback) {
  ++suppress_unresponsive_renderer_count_;
  GetContentClient()->browser()->IsClipboardPasteAllowedByPolicy(
      source, destination, metadata, std::move(clipboard_paste_data),
      base::BindOnce(&WebContentsImpl::IsClipboardPasteAllowedWrapperCallback,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void WebContentsImpl::OnTextCopiedToClipboard(
    RenderFrameHostImpl* render_frame_host,
    const std::u16string& copied_text) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnTextCopiedToClipboard",
                        "render_frame_host", render_frame_host);
  observers_.NotifyObservers(&WebContentsObserver::OnTextCopiedToClipboard,
                             render_frame_host, copied_text);
}

void WebContentsImpl::TextSelectionChanged(
    RenderFrameHostImpl* render_frame_host,
    std::u16string_view selected_text) {
  // Notify observers.
  observers_.NotifyObservers(&WebContentsObserver::OnTextSelectionChanged,
                             render_frame_host, selected_text);
}

void WebContentsImpl::IsClipboardPasteAllowedWrapperCallback(
    IsClipboardPasteAllowedCallback callback,
    std::optional<ClipboardPasteData> clipboard_paste_data) {
  std::move(callback).Run(std::move(clipboard_paste_data));
  --suppress_unresponsive_renderer_count_;
}

std::optional<std::vector<std::u16string>>
WebContentsImpl::GetClipboardTypesIfPolicyApplied(
    const ui::ClipboardSequenceNumberToken& seqno) {
  return GetContentClient()->browser()->GetClipboardTypesIfPolicyApplied(seqno);
}

void WebContentsImpl::BindScreenOrientation(
    RenderFrameHost* rfh,
    mojo::PendingAssociatedReceiver<device::mojom::ScreenOrientation>
        receiver) {
  screen_orientation_provider_->BindScreenOrientation(rfh, std::move(receiver));
}

bool WebContentsImpl::IsTransientActivationRequiredForHtmlFullscreen() {
  // Allow fullscreen if the screen orientation changed in the last 1 second.
  static constexpr base::TimeDelta kMaxInterval = base::Seconds(1);
  const base::TimeDelta delta =
      ui::EventTimeForNow() - last_screen_orientation_change_time_;
  if (delta <= kMaxInterval) {
    return false;
  }

  // Require transient activation shortly after a same-origin WebContents exit.
  RenderFrameHostImpl* host = GetPrimaryMainFrame();
  auto* last_exits = GetFullscreenUserData(GetBrowserContext())->last_exits();
  auto last_exit = last_exits->find(host->GetLastCommittedOrigin());
  constexpr base::TimeDelta kCooldown = base::Seconds(5);
  if (last_exit != last_exits->end() &&
      base::TimeTicks::Now() < last_exit->second + kCooldown) {
    return true;
  }

  // Waive transient activation requirements if Automatic Fullscreen is granted.
  if (IsAutomaticFullscreenGranted(host)) {
    return false;
  }

  return true;
}

bool WebContentsImpl::IsBackForwardCacheSupported() {
  if (!GetDelegate()) {
    return false;
  }
  return GetDelegate()->IsBackForwardCacheSupported(*this);
}

FrameTree* WebContentsImpl::LoadingTree() {
  return &GetPrimaryFrameTree();
}

void WebContentsImpl::DidChangeScreenOrientation() {
  last_screen_orientation_change_time_ = ui::EventTimeForNow();
}

void WebContentsImpl::UpdateWebContentsVisibility(Visibility visibility) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::UpdateWebContentsVisibility",
                        "visibility", visibility);

  // For opt-in WebUIs, the WebContents's visibility will be kept VISIBLE until
  // the first visually non-empty paint has occurred.
  // This is an optimization to prevent the occlusion calculation from blocking
  // the first visually non-empty paint.
  WebUI* web_ui = GetWebUI();
  WebUIConfig* webui_config = web_ui ? web_ui->GetWebUIConfig() : nullptr;
  if (webui_config &&
      webui_config->ShouldKeepVisibleUntilFirstVisuallyNonEmptyPaint() &&
      !CompletedFirstVisuallyNonEmptyPaint()) {
    visibility = Visibility::VISIBLE;
  }

  // Occlusion is disabled when
  // |switches::kDisableBackgroundingOccludedWindowsForTesting| is specified on
  // the command line (to avoid flakiness in browser tests).
  const bool occlusion_is_disabled =
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableBackgroundingOccludedWindowsForTesting);
  if (occlusion_is_disabled && visibility == Visibility::OCCLUDED) {
    visibility = Visibility::VISIBLE;
  }

  if (!did_first_set_visible_) {
    if (visibility == Visibility::VISIBLE) {
      // A WebContents created with CreateParams::initially_hidden = false
      // starts with GetVisibility() == Visibility::VISIBLE even though it is
      // not really visible. Call WasShown() when it becomes visible for real as
      // the page load mechanism and some WebContentsObserver rely on that.
      WasShown();
      did_first_set_visible_ = true;
    }

    // Trust the initial visibility of the WebContents and do not switch it to
    // HIDDEN or OCCLUDED before it becomes VISIBLE for real. Doing so would
    // result in destroying resources that would immediately be recreated (e.g.
    // UpdateWebContents(HIDDEN) can be called when a WebContents is added to a
    // hidden window that is about to be shown).

    return;
  }

  if (visibility == visibility_) {
    return;
  }

  UpdateVisibilityAndNotifyPageAndView(visibility);
}

void WebContentsImpl::UpdateOverridingUserAgent() {
  OPTIONAL_TRACE_EVENT0("content",
                        "WebContentsImpl::UpdateOverridingUserAgent");
  NotifyPreferencesChanged();
}

void WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded(PageImpl& page) {
  OPTIONAL_TRACE_EVENT0(
      "content", "WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded");

  bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kAllowInsecureLocalhost);
  if (!allow_localhost) {
    return;
  }

  RenderFrameHostImpl& frame = page.GetMainDocument();
  NavigationEntry* entry =
      frame.frame_tree()->controller().GetLastCommittedEntry();
  if (!entry || !net::IsLocalhost(entry->GetURL())) {
    return;
  }

  SSLStatus ssl_status = entry->GetSSL();
  if (!net::IsCertStatusError(ssl_status.cert_status)) {
    return;
  }

  frame.AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
                            "This site does not have a valid SSL "
                            "certificate! Without SSL, your site's and "
                            "visitors' data is vulnerable to theft and "
                            "tampering. Get a valid SSL certificate before "
                            " releasing your website to the public.");
}

bool WebContentsImpl::IsShowingContextMenuOnPage() const {
  return showing_context_menu_;
}

download::DownloadUrlParameters::RequestHeadersType
WebContentsImpl::ParseDownloadHeaders(const std::string& headers) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ParseDownloadHeaders",
                        "headers", headers);
  download::DownloadUrlParameters::RequestHeadersType request_headers;
  for (std::string_view key_value : base::SplitStringPiece(
           headers, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
    std::vector<std::string> pair = base::SplitString(
        key_value, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
    if (2ul == pair.size()) {
      request_headers.push_back(make_pair(pair[0], pair[1]));
    }
  }
  return request_headers;
}

void WebContentsImpl::SetOpenerForNewContents(FrameTreeNode* opener,
                                              bool opener_suppressed) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::SetOpenerForNewContents");
  if (opener) {
    FrameTreeNode* new_root = GetPrimaryFrameTree().root();

    // For the "original opener", track the opener's main frame instead, because
    // if the opener is a subframe, the opener tracking could be easily bypassed
    // by spawning from a subframe and deleting the subframe.
    // https://crbug.com/705316
    new_root->SetOriginalOpener(opener->frame_tree().root());
    new_root->SetOpenerDevtoolsFrameToken(
        opener->current_frame_host()->devtools_frame_token());
    opened_by_another_window_ = true;

    if (!opener_suppressed) {
      new_root->SetOpener(opener);
    }
  }
}

void WebContentsImpl::MediaMutedStatusChanged(const MediaPlayerId& id,
                                              bool muted) {
  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::MediaMutedStatusChanged",
                        "muted", muted);
  observers_.NotifyObservers(&WebContentsObserver::MediaMutedStatusChanged, id,
                             muted);
}

void WebContentsImpl::SetVisibilityForChildViews(bool visible) {
  OPTIONAL_TRACE_EVENT1("content",
                        "WebContentsImpl::SetVisibilityForChildViews",
                        "visible", visible);
  GetPrimaryMainFrame()->SetVisibilityForChildViews(visible);
}

void WebContentsImpl::ScheduleColorRelatedStateChanges() {
  if (color_related_state_change_scheduled_) {
    return;
  }
  color_related_state_change_scheduled_ = true;
  GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&WebContentsImpl::HandleColorRelatedStateChanges,
                     weak_factory_.GetWeakPtr()));
}

void WebContentsImpl::HandleColorRelatedStateChanges() {
  color_related_state_change_scheduled_ = false;

  // This can be reached re-entrantly during ~WebContentsImpl, after the
  // primary main frame has begun being destroyed. Bail out before
  // dereferencing it via GetPrimaryMainFrame() below.
  if (IsBeingDestroyed()) {
    return;
  }

  if (const auto* const theme = ui::NativeTheme::GetInstanceForWeb();
      prefers_reduced_transparency_ != theme->prefers_reduced_transparency() ||
      inverted_colors_ != theme->inverted_colors() ||
      GetContentClient()
          ->browser()
          ->WebPreferencesNeedUpdateForColorRelatedStateChanges(
              *this, *GetPrimaryMainFrame()->GetSiteInstance())) {
    NotifyPreferencesChanged();
  }

  // Update color providers after applying any pending WebPreferences changes.
  // This is done because WebPreferences changes (e.g. `in_forced_colors`) might
  // not trigger an invalidation in Blink, but color provider changes will
  // always trigger invalidation. Therefore, we want to update color providers
  // after we have the right states for the web preferences so we can use the
  // right color providers in Blink to paint. This also handles scenarios where
  // the forced colors state remains the same, but the `forced_colors_map` is
  // updated due to changes in the forced colors mode theme.
  if (blink::ColorProviderColorMaps color_maps = GetColorProviderColorMaps();
      color_maps_ != color_maps) {
    color_maps_.swap(color_maps);
    ExecutePageBroadcastMethodForAllPages([this](RenderViewHostImpl* rvh) {
      if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
        broadcast->UpdateColorProviders(color_maps_);
      }
    });
  }
}

void WebContentsImpl::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::OnNativeThemeUpdated");
  DCHECK_EQ(observed_theme, ui::NativeTheme::GetInstanceForWeb());

  if (base::FeatureList::IsEnabled(features::kThemeChangeOptimization)) {
    ScheduleColorRelatedStateChanges();
  } else {
    HandleColorRelatedStateChanges();
  }

  const auto caret_blink_interval = observed_theme->caret_blink_interval();
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
    BUILDFLAG(IS_WIN)
  const auto use_overlay_scrollbar = observed_theme->use_overlay_scrollbar();
#endif
  bool renderer_preference_changed = false;
  if (renderer_preferences_.caret_blink_interval != caret_blink_interval) {
    renderer_preferences_.caret_blink_interval = caret_blink_interval;
    renderer_preference_changed = true;
  }
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
    BUILDFLAG(IS_WIN)
  if (renderer_preferences_.use_overlay_scrollbar != use_overlay_scrollbar) {
    renderer_preferences_.use_overlay_scrollbar = use_overlay_scrollbar;
    renderer_preference_changed = true;
  }
#endif

  if (renderer_preference_changed) {
    SyncRendererPrefs();
  }
}

void WebContentsImpl::OnCaptionStyleUpdated() {
  NotifyPreferencesChanged();
}

void WebContentsImpl::OnColorProviderChanged() {
  // OnColorProviderChanged() might have been triggered as the result of the
  // observed source being destroyed during teardown, in this case, no-op.
  if (IsBeingDestroyed()) {
    return;
  }

  // OnColorProviderChanged() might have been triggered as the result of the
  // observed source being reset. If this is the case fallback to the default
  // source.
  if (!GetColorProviderSource()) {
    // This will synchronously call `OnColorProviderChanged()` again.
    SetColorProviderSource(DefaultColorProviderSource::GetInstance());
    return;
  }

  observers_.NotifyObservers(&WebContentsObserver::OnColorProviderChanged);

  if (base::FeatureList::IsEnabled(features::kThemeChangeOptimization)) {
    ScheduleColorRelatedStateChanges();
  } else {
    HandleColorRelatedStateChanges();
  }
}

const ui::ColorProvider& WebContentsImpl::GetColorProvider() const {
  auto* source = GetColorProviderSource();
  DCHECK(source);
  auto* color_provider = source->GetColorProvider();
  DCHECK(color_provider);
  return *color_provider;
}

void WebContentsImpl::OnSlowWebPreferenceChanged() {
  OnWebPreferencesChanged();
}

blink::mojom::FrameWidgetInputHandler*
WebContentsImpl::GetFocusedFrameWidgetInputHandler() {
  auto* focused_render_widget_host =
      GetFocusedRenderWidgetHost(GetPrimaryMainFrame()->GetRenderWidgetHost());
  if (!focused_render_widget_host) {
    return nullptr;
  }
  return focused_render_widget_host->GetFrameWidgetInputHandler();
}

ukm::SourceId WebContentsImpl::GetCurrentPageUkmSourceId() {
  return GetPrimaryMainFrame()->GetPageUkmSourceId();
}

void WebContentsImpl::ForEachRenderViewHost(
    ForEachRenderViewHostTypes view_mask,
    RenderViewHostIterationCallback on_render_view_host) {
  std::set<RenderViewHostImpl*> render_view_hosts;

  if ((view_mask & (ForEachRenderViewHostTypes::kPrerenderViews |
                    ForEachRenderViewHostTypes::kActiveViews)) != 0) {
    ForEachFrameTree([view_mask, &render_view_hosts](FrameTree& frame_tree) {
      // Check the view masks.
      if (frame_tree.is_prerendering()) {
        // We are in a prerendering page.
        if ((view_mask & ForEachRenderViewHostTypes::kPrerenderViews) == 0) {
          return;
        }
      } else {
        // We are in an active page.
        if ((view_mask & ForEachRenderViewHostTypes::kActiveViews) == 0) {
          return;
        }
      }
      frame_tree.ForEachRenderViewHost(
          [&render_view_hosts](RenderViewHostImpl* rvh) {
            render_view_hosts.insert(rvh);
          });
    });
  }

  if ((view_mask & ForEachRenderViewHostTypes::kBackForwardCacheViews) != 0) {
    // Add RenderViewHostImpls in BackForwardCache.
    const auto& entries = GetController().GetBackForwardCache().GetEntries();
    for (const auto& entry : entries) {
      for (const auto& render_view : entry->render_view_hosts()) {
        render_view_hosts.insert(&*render_view);
      }
    }
  }

  for (auto* render_view_host : render_view_hosts) {
    on_render_view_host.Run(render_view_host);
  }
}

void WebContentsImpl::NotifyPageBecamePrimary(PageImpl& page) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::PrimaryPageChanged");

  DCHECK_EQ(&page, &GetPrimaryPage());

#if BUILDFLAG(IS_ANDROID)
  // Apply the cached subframe importance if it is set. This is needed for
  // pages restored from back/forward cache. Note that we don't need to clear
  // importance for non-primary pages because the importance is ignored at
  // RenderWidgetHostImpl::GetPriority() and updated when it becomes inactive.
  ApplyPrimaryPageSubframeImportance();
#endif

  // Clear |save_package_| since the primary page changed.
  if (save_package_) {
    save_package_->ClearPage();
    save_package_.reset();
  }

  // Sync draggable regions.
  SetSupportsDraggableRegions(supports_draggable_regions_);

  observers_.NotifyObservers(&WebContentsObserver::PrimaryPageChanged, page);
}

FrameTreeNodeId WebContentsImpl::GetOuterDelegateFrameTreeNodeId() {
  return node_.outer_contents_frame_tree_node_id();
}

// Cf. `GetUnattachedOwnedNodes` which applies to the same situation, but is for
// descending.
RenderFrameHostImpl* WebContentsImpl::GetProspectiveOuterDocument() {
  // If the outer WebContents is already known, then there was no need to call
  // this method.
  DCHECK(!GetOuterWebContents());

  RenderFrameHostImpl* unattached_guest_owner =
      browser_plugin_guest_
          ? browser_plugin_guest_->GetProspectiveOuterDocument()
          : nullptr;
  if (unattached_guest_owner) {
    return unattached_guest_owner;
  }

  return nullptr;
}

void WebContentsImpl::RenderFrameHostStateChanged(
    RenderFrameHost* render_frame_host,
    LifecycleState old_state,
    LifecycleState new_state) {
  DCHECK_NE(old_state, new_state);
  OPTIONAL_TRACE_EVENT("content",
                       "WebContentsImpl::RenderFrameHostStateChanged",
                       "render_frame_host", render_frame_host, "old_state",
                       old_state, "new_state", new_state);

#if BUILDFLAG(IS_ANDROID)
  if (old_state == LifecycleState::kActive && !render_frame_host->GetParent()) {
    // TODO(sreejakshetty): Remove this reset when ColorChooserHolder becomes
    // per-frame.
    // Close the color chooser popup when RenderFrameHost changes state from
    // kActive.
    color_chooser_holder_.reset();
  }
#endif  // BUILDFLAG(IS_ANDROID)

  observers_.NotifyObservers(&WebContentsObserver::RenderFrameHostStateChanged,
                             render_frame_host, old_state, new_state);
}

void WebContentsImpl::DecrementCapturerCount(bool stay_hidden,
                                             bool stay_awake,
                                             bool is_activity) {
  OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DecrementCapturerCount");
  if (stay_hidden) {
    --hidden_capturer_count_;
  } else {
    --visible_capturer_count_;
  }
  if (stay_awake) {
    --stay_awake_capturer_count_;
  }
  DCHECK_GE(hidden_capturer_count_, 0);
  DCHECK_GE(visible_capturer_count_, 0);
  DCHECK_GE(stay_awake_capturer_count_, 0);

  if (IsBeingDestroyed()) {
    return;
  }

  OnCapturerCountChanged();

  const bool is_being_captured = IsBeingCaptured();
  if (!is_being_captured) {
    const gfx::Size old_size = preferred_size_for_capture_;
    preferred_size_for_capture_ = gfx::Size();
    OnPreferredSizeChanged(old_size);
  }

  if (capture_wake_lock_ &&
      (!is_being_captured || !stay_awake_capturer_count_)) {
    capture_wake_lock_->CancelWakeLock();
  }

  UpdateVisibilityAndNotifyPageAndView(GetVisibility(), is_activity);
}

void WebContentsImpl::NotifyPrimaryMainFrameProcessIsAlive() {
  // The WebContents tracks the process state for the primary main frame's
  // renderer.
  // Consider renderer as terminated when exited with any termination status.
  bool was_renderer_terminated = primary_main_frame_process_status_ !=
                                 base::TERMINATION_STATUS_STILL_RUNNING;
  SetPrimaryMainFrameProcessStatus(base::TERMINATION_STATUS_STILL_RUNNING, 0);
  // Restore the focus to the tab (otherwise the focus will be on the top
  // window).
  if (was_renderer_terminated && !FocusLocationBarByDefault()) {
    if (!delegate_ || delegate_->ShouldFocusPageAfterCrash(this)) {
      view_->Focus();
    }
  }
}

void WebContentsImpl::UpdateBrowserControlsState(
    cc::BrowserControlsState constraints,
    cc::BrowserControlsState current,
    bool animate,
    const std::optional<cc::BrowserControlsOffsetTagModifications>&
        offset_tag_modifications) {
  // Browser controls should be synchronised with the scroll state. Therefore,
  // they are controlled from the renderer by the main RenderFrame(Host).
  GetPrimaryPage().UpdateBrowserControlsState(constraints, current, animate,
                                              offset_tag_modifications);
}

void WebContentsImpl::SetSupportsDraggableRegions(
    bool supports_draggable_regions) {
  supports_draggable_regions_ = supports_draggable_regions;
  ExecutePageBroadcastMethod(
      [supports_draggable_regions](RenderViewHostImpl* rvh) {
        if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
          broadcast->SetSupportsDraggableRegions(supports_draggable_regions);
        }
      });
}

void WebContentsImpl::SetV8CompileHints(base::ReadOnlySharedMemoryRegion data) {
  GetPrimaryMainFrame()->GetAssociatedLocalMainFrame()->SetV8CompileHints(
      std::move(data));
}

void WebContentsImpl::SetTabSwitchStartTime(base::TimeTicks start_time,
                                            bool destination_is_loaded,
                                            bool had_saved_frame_at_start) {
  GetVisibleTimeRequestTrigger().UpdateRequest(blink::VisibleTimeEvent{
      .event_start_time = start_time,
      .reason = blink::VisibleTimeEvent::TabSwitchReason{
          .destination_is_loaded = destination_is_loaded,
          .had_saved_frame_at_start = had_saved_frame_at_start}});
}

VisibleTimeRequestTrigger& WebContentsImpl::GetVisibleTimeRequestTrigger() {
  return visible_time_request_trigger_;
}

gfx::mojom::DelegatedInkPointRenderer* WebContentsImpl::GetDelegatedInkRenderer(
    ui::Compositor* compositor) {
  if (!delegated_ink_point_renderer_.is_bound()) {
    // The remote can't be bound if the compositor is null, so bail if
    // that is the case so we don't crash by trying to use an unbound
    // remote.
    if (!compositor) {
      return nullptr;
    }

    TRACE_EVENT_INSTANT("delegated_ink_trails",
                        "Binding mojo interface for delegated ink points.");
    compositor->SetDelegatedInkPointRenderer(
        delegated_ink_point_renderer_.BindNewPipeAndPassReceiver());
    delegated_ink_point_renderer_.reset_on_disconnect();
  }
  return delegated_ink_point_renderer_.get();
}

#if BUILDFLAG(IS_ANDROID)
gfx::PointF WebContentsImpl::GetCurrentTouchSequenceOffset() {
  ui::ViewAndroid* view_android = GetNativeView();
  return view_android->event_forwarder()->GetCurrentTouchSequenceOffset();
}
#endif

std::unique_ptr<PrefetchHandle> WebContentsImpl::StartPrefetch(
    const GURL& prefetch_url,
    bool use_prefetch_proxy,
    const std::string& embedder_histogram_suffix,
    const blink::mojom::Referrer& referrer,
    const std::optional<url::Origin>& referring_origin,
    std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
    std::optional<PrefetchPriority> priority,
    scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
    base::WeakPtr<PreloadingAttempt> attempt,
    PreloadingHoldbackStatus holdback_status_override,
    std::optional<base::TimeDelta> ttl,
    bool should_ignore_saver_modes) {
  PrefetchService* prefetch_service =
      BrowserContextImpl::From(GetBrowserContext())->GetPrefetchService();
  if (!prefetch_service) {
    return nullptr;
  }

  PrefetchType prefetch_type(PreloadingTriggerType::kEmbedder,
                             use_prefetch_proxy);
  auto request = PrefetchRequest::CreateBrowserInitiated(
      *this, prefetch_url, prefetch_type, embedder_histogram_suffix, referrer,
      referring_origin, std::move(no_vary_search_hint), std::move(priority),
      std::move(preload_pipeline_info), std::move(attempt),
      holdback_status_override, std::move(ttl), should_ignore_saver_modes);

  return prefetch_service->AddPrefetchRequestWithHandle(std::move(request));
}

std::unique_ptr<PrerenderHandle> WebContentsImpl::StartPrerendering(
    const GURL& prerendering_url,
    PreloadingTriggerType trigger_type,
    const std::string& embedder_histogram_suffix,
    net::HttpRequestHeaders additional_headers,
    std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
    ui::PageTransition page_transition,
    bool should_warm_up_compositor,
    bool should_prepare_paint_tree,
    PreloadingHoldbackStatus holdback_status_override,
    scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
    PreloadingAttempt* preloading_attempt,
    base::RepeatingCallback<bool(const GURL&,
                                 const std::optional<UrlMatchType>&)>
        url_match_predicate,
    base::RepeatingCallback<void(NavigationHandle&)>
        prerender_navigation_handle_callback,
    bool allow_reuse) {
  PrerenderAttributes attributes(
      prerendering_url, trigger_type, embedder_histogram_suffix,
      /*speculation_rules_params=*/std::nullopt, content::Referrer(),
      no_vary_search_hint,
      /*initiator_render_frame_host=*/nullptr, GetWeakPtr(), page_transition,
      should_warm_up_compositor, should_prepare_paint_tree,
      blink::mojom::SpeculationAction::kPrerender,
      std::move(url_match_predicate),
      std::move(prerender_navigation_handle_callback),
      base::WrapRefCounted(
          static_cast<PreloadPipelineInfoImpl*>(preload_pipeline_info.get())),
      allow_reuse,
      /*form_submission=*/false);
#if BUILDFLAG(IS_ANDROID)
  attributes.additional_headers = std::move(additional_headers);
#else
  CHECK(additional_headers.IsEmpty())
      << "additional_headers is supported only on Android WebView.";
#endif  // BUILDFLAG(IS_ANDROID)
  attributes.holdback_status_override = holdback_status_override;

  PrerenderHostId prerender_host_id =
      GetPrerenderHostRegistry()->CreateAndStartHost(attributes,
                                                     preloading_attempt);

  if (prerender_host_id) {
    return std::make_unique<PrerenderHandleImpl>(
        GetPrerenderHostRegistry()->GetWeakPtr(), prerender_host_id,
        prerendering_url, std::move(no_vary_search_hint));
  }
  return nullptr;
}

void WebContentsImpl::CancelAllPrerendering() {
  GetPrerenderHostRegistry()->CancelAllHosts(
      PrerenderFinalStatus::kAllPrerenderingCanceled);
}

bool WebContentsImpl::IsAllowedToStartPrerendering() {
  return GetPrerenderHostRegistry()->IsAllowedToStartPrerenderingForEmbedder();
}

void WebContentsImpl::BackNavigationLikely(PreloadingPredictor predictor,
                                           WindowOpenDisposition disposition) {
  CHECK(!IsBeingDestroyed());

  // See the comment of `last_back_navigation_hint_time_` for why this cooldown
  // exists. The choice of 5 seconds is arbitrary.
  constexpr base::TimeDelta kCooldown = base::Seconds(5);
  base::TimeTicks now = ui::EventTimeForNow();
  if (now - last_back_navigation_hint_time_ < kCooldown) {
    return;
  }
  last_back_navigation_hint_time_ = now;

  if (disposition != WindowOpenDisposition::CURRENT_TAB) {
    RecordPrerenderBackNavigationEligibility(
        predictor, PrerenderBackNavigationEligibility::kTargetingOtherWindow,
        nullptr);
    return;
  }

  GetPrerenderHostRegistry()->BackNavigationLikely(predictor);
}

void WebContentsImpl::SetOwnerLocationForDebug(
    std::optional<base::Location> owner_location) {
  ownership_location_ = owner_location;
}

void WebContentsImpl::AboutToBeDiscarded(WebContents* new_contents) {
  observers_.NotifyObservers(&WebContentsObserver::AboutToBeDiscarded,
                             new_contents);
}

void WebContentsImpl::NotifyWasDiscarded() {
  observers_.NotifyObservers(&WebContentsObserver::WasDiscarded);
}

base::ScopedClosureRunner WebContentsImpl::CreateDisallowCustomCursorScope(
    int max_dimension_dips) {
  auto* render_widget_host_base = GetPrimaryMainFrame()
                                      ->GetRenderWidgetHost()
                                      ->GetRenderWidgetHostViewBase();

  // It's possible for |render_widget_host_base| to be null if the renderer
  // crashed. To avoid race conditions, null-check here. See crbug.com/1421552
  // as well.
  if (!render_widget_host_base) {
    return base::ScopedClosureRunner();
  }

  auto* cursor_manager = render_widget_host_base->GetCursorManager();
  return cursor_manager->CreateDisallowCustomCursorScope(max_dimension_dips);
}

bool WebContentsImpl::CancelPrerendering(FrameTreeNode* frame_tree_node,
                                         PrerenderFinalStatus final_status) {
  if (!frame_tree_node) {
    return false;
  }

  DCHECK_EQ(this, FromFrameTreeNode(frame_tree_node));

  // A prerendered page is identified by its root FrameTreeNode id, so if the
  // given `frame_tree_node` is in any way embedded, we need to iterate up to
  // the prerender root.
  if (frame_tree_node->GetParentOrOuterDocumentOrEmbedder()) {
    return frame_tree_node->GetParentOrOuterDocumentOrEmbedder()
        ->CancelPrerendering(PrerenderCancellationReason(final_status));
  }
  return GetPrerenderHostRegistry()->CancelHost(
      frame_tree_node->frame_tree().delegate()->GetPrerenderHostId(),
      final_status);
}

ui::mojom::VirtualKeyboardMode WebContentsImpl::GetVirtualKeyboardMode() const {
  return primary_frame_tree_.root()
      ->current_frame_host()
      ->GetPage()
      .virtual_keyboard_mode();
}

void WebContentsImpl::SetOverscrollNavigationEnabled(bool enabled) {
  GetView()->SetOverscrollControllerEnabled(enabled);
}

BackForwardTransitionAnimationManager*
WebContentsImpl::GetBackForwardTransitionAnimationManager() {
  return GetView()->GetBackForwardTransitionAnimationManager();
}

#if BUILDFLAG(IS_ANDROID)
void WebContentsImpl::SetLongPressLinkSelectText(bool enabled) {
  if (long_press_link_select_text_ == enabled) {
    return;
  }
  long_press_link_select_text_ = enabled;
  NotifyPreferencesChanged();
}

void WebContentsImpl::SetCanAcceptLoadDrops(bool enabled) {
  if (renderer_preferences_.can_accept_load_drops == enabled) {
    return;
  }
  renderer_preferences_.can_accept_load_drops = enabled;
  SyncRendererPrefs();
}

bool WebContentsImpl::GetCanAcceptLoadDropsForTesting() {
  return renderer_preferences_.can_accept_load_drops;
}

#endif

net::handles::NetworkHandle WebContentsImpl::GetTargetNetwork() {
  return target_network_;
}

void WebContentsImpl::GetMediaCaptureRawDeviceIdsOpened(
    blink::mojom::MediaStreamType type,
    base::OnceCallback<void(std::vector<std::string>)> callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  CHECK(type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE ||
        type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE);

  MediaStreamManager* media_stream_manager =
      BrowserMainLoop::GetInstance()->media_stream_manager();
  if (!media_stream_manager) {
    std::move(callback).Run({});
    return;
  }

  media_stream_manager->GetRawDeviceIdsOpenedForFrame(
      GetPrimaryMainFrame(), type,
      base::BindPostTaskToCurrentDefault(std::move(callback)));
}

void WebContentsImpl::WarmUpAndroidSpareRenderer() {
  if (GetBrowserContext()->ShutdownStarted()) {
    return;
  }
  if (!GetLastCommittedURL().SchemeIsHTTPOrHTTPS() &&
      features::kAndroidSpareRendererOnlyWarmupAfterWebPageLoaded.Get()) {
    return;
  }
  int renderer_timeout_seconds =
      features::kAndroidSpareRendererTimeoutSeconds.Get();
  if (renderer_timeout_seconds < 0) {
    SpareRenderProcessHostManagerImpl::Get().WarmupSpare(GetBrowserContext());
  } else {
    base::TimeDelta timeout = base::Seconds(renderer_timeout_seconds);
    SpareRenderProcessHostManagerImpl::Get().WarmupSpare(GetBrowserContext(),
                                                         timeout);
  }
}

void WebContentsImpl::EmitTracingSlice(const std::string& title) {
  TRACE_STATE("content.web_contents.lifecycle", nullptr,
              tracing_track_->track(), [&](perfetto::EventContext ctx) {
                if (!ctx.ShouldFilterDynamicEventNames() && !title.empty()) {
                  ctx.event()->set_name(title);
                } else {
                  ctx.event()->set_name("WebContents");
                }
              });
}

}  // namespace content
