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

#include "content/browser/renderer_host/render_widget_host_view_base.h"

#include <optional>

#include "base/check.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/notimplemented.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/input/event_with_latency_info.h"
#include "components/input/render_widget_host_input_event_router.h"
#include "components/input/render_widget_host_view_input_observer.h"
#include "components/viz/common/features.h"
#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/device_posture/device_posture_provider_impl.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_base.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_owner_delegate.h"
#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
#include "content/browser/renderer_host/scoped_view_transition_resources.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/browser/renderer_host/unbounded_surface_window.h"
#include "content/browser/renderer_host/visible_time_request_trigger.h"
#include "content/common/content_switches_internal.h"
#include "content/common/features.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/page_visibility_state.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom.h"
#include "third_party/blink/public/mojom/unbounded_element/unbounded_element.mojom.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/base/ui_base_types.h"
#include "ui/display/display_util.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/gesture_detection/filtered_gesture_provider.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/size_f.h"

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "content/browser/renderer_host/input/stylus_handwriting_controller_win.h"
#endif

namespace content {

void RenderWidgetHostViewBase::OnUnconfirmedTapConvertedToTap() {}

RenderWidgetHostViewBase::RenderWidgetHostViewBase(RenderWidgetHost* host)
    : host_(RenderWidgetHostImpl::From(host)),
      // `screen_infos_` must be initialized, to permit unconditional access to
      // its current display. A placeholder ScreenInfo is used here, so the
      // first call to UpdateScreenInfo will trigger the expected updates.
      screen_infos_(display::ScreenInfos(display::ScreenInfo())) {}

RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
  CHECK(!keyboard_locked_);
  CHECK(!IsPointerLocked());
#if BUILDFLAG(IS_WIN)
  if (StylusHandwritingControllerWin::IsHandwritingAPIAvailable()) {
    StylusHandwritingControllerWin::GetInstance()->OnHandwritingViewDestroyed(
        this);
  }
#endif  // BUILDFLAG(IS_WIN)
  // We call this here to guarantee that observers are notified before we go
  // away. However, some subclasses may wish to call this earlier in their
  // shutdown process, e.g. to force removal from
  // RenderWidgetHostInputEventRouter's surface map before relinquishing a
  // host pointer. There is no harm in calling NotifyObserversAboutShutdown()
  // twice, as the observers are required to de-register on the first call, and
  // so the second call does nothing.
  NotifyObserversAboutShutdown();
  // If we have a live reference to |text_input_manager_|, we should unregister
  // so that the |text_input_manager_| will free its state.
  if (text_input_manager_)
    text_input_manager_->Unregister(this);
}

RenderWidgetHostImpl* RenderWidgetHostViewBase::GetFocusedWidget() const {
  return host() && host()->delegate()
             ? host()->delegate()->GetFocusedRenderWidgetHost(host())
             : nullptr;
}

RenderWidgetHost* RenderWidgetHostViewBase::GetRenderWidgetHost() {
  return host();
}

void RenderWidgetHostViewBase::SetContentBackgroundColor(SkColor color) {
  if (content_background_color_ == color)
    return;

  content_background_color_ = color;
  UpdateBackgroundColor();
}

MouseWheelPhaseHandler* RenderWidgetHostViewBase::GetMouseWheelPhaseHandler() {
  return nullptr;
}

void RenderWidgetHostViewBase::UpdateIntrinsicSizingInfo(
    blink::mojom::IntrinsicSizingInfoPtr sizing_info) {}

gfx::Size RenderWidgetHostViewBase::GetCompositorViewportPixelSize() {
  return gfx::ScaleToCeiledSize(GetRequestedRendererSize(),
                                GetDeviceScaleFactor());
}

void RenderWidgetHostViewBase::SelectionBoundsChanged(
    const gfx::Rect& anchor_rect,
    base::i18n::TextDirection anchor_dir,
    const gfx::Rect& focus_rect,
    base::i18n::TextDirection focus_dir,
    const gfx::Rect& bounding_box,
    bool is_anchor_first) {
#if !BUILDFLAG(IS_ANDROID)
  if (GetTextInputManager())
    GetTextInputManager()->SelectionBoundsChanged(
        this, anchor_rect, anchor_dir, focus_rect, focus_dir, bounding_box,
        is_anchor_first);
#else
  NOTREACHED() << "Selection bounds should be routed through the compositor.";
#endif
}

RenderWidgetHostViewBase* RenderWidgetHostViewBase::GetRootView() {
  return this;
}

void RenderWidgetHostViewBase::SelectionChanged(const std::u16string& text,
                                                size_t offset,
                                                const gfx::Range& range) {
  if (GetTextInputManager())
    GetTextInputManager()->SelectionChanged(this, text, offset, range);
}

gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSize() {
  return GetViewBounds().size();
}

gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSizeDevicePx() {
  return gfx::ScaleToCeiledSize(GetRequestedRendererSize(),
                                GetDeviceScaleFactor());
}

ui::TextInputClient* RenderWidgetHostViewBase::GetTextInputClient() {
  return nullptr;
}

bool RenderWidgetHostViewBase::IsSurfaceAvailableForCopy() {
  return false;
}

// static
bool RenderWidgetHostViewBase::TransformPointAndRectToRootView(
    RenderWidgetHostViewBase* view,
    RenderWidgetHostViewBase* root_view,
    gfx::Point* transformed_point,
    gfx::Rect* transformed_rect) {
  gfx::Transform transform_to_main_frame;
  if (!view->GetTransformToViewCoordSpace(root_view,
                                          &transform_to_main_frame)) {
    return false;
  }

  if (transformed_point) {
    *transformed_point = transform_to_main_frame.MapPoint(*transformed_point);
  }

  if (transformed_rect) {
    *transformed_rect = transform_to_main_frame.MapRect(*transformed_rect);
  }

  return true;
}

void RenderWidgetHostViewBase::CopyMainAndPopupFromSurface(
    base::WeakPtr<RenderWidgetHostImpl> main_host,
    base::WeakPtr<DelegatedFrameHost> main_frame_host,
    base::WeakPtr<RenderWidgetHostImpl> popup_host,
    base::WeakPtr<DelegatedFrameHost> popup_frame_host,
    const gfx::Rect& src_subrect,
    const gfx::Size& dst_size,
    float scale_factor,
    base::TimeDelta timeout,
    base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback) {
  if (!main_host || !main_frame_host) {
    if (base::FeatureList::IsEnabled(
            features::kCopyFromSurfaceAlwaysCallCallback)) {
      std::move(callback).Run(base::unexpected<content::CopyFromSurfaceError>(
          content::CopyFromSurfaceError::kFrameGone));
    }
    return;
  }

#if BUILDFLAG(IS_ANDROID)
  NOTREACHED()
      << "RenderWidgetHostViewAndroid::CopyFromSurface calls "
         "DelegatedFrameHostAndroid::CopyFromCompositingSurface directly, "
         "and popups are not supported.";
#else
  RenderWidgetHostViewBase* main_view =
      main_host ? static_cast<RenderWidgetHostViewBase*>(main_host->GetView())
                : nullptr;
  UnboundedSurfaceWindow* unbounded_window =
      main_view && main_view->HasActiveUnboundedSurface()
          ? main_view->GetUnboundedSurfaceWindow()
          : nullptr;
  const bool has_popup = popup_host && popup_frame_host;
  CHECK(!has_popup || !unbounded_window);

  if (!has_popup && !unbounded_window) {
    // No popup or unbounded window - just call CopyFromCompositingSurface
    // once.
    main_frame_host->CopyFromCompositingSurface(src_subrect, dst_size, timeout,
                                                std::move(callback));
    return;
  }

  // First locate the secondary window relative to the main page, in DIPs
  const gfx::Point parent_location =
      main_view ? main_view->GetViewBounds().origin() : gfx::Point();
  gfx::Point secondary_location;
  if (popup_host && popup_frame_host) {
    secondary_location = popup_host->GetView()->GetViewBounds().origin();
  } else if (unbounded_window) {
    CHECK(base::FeatureList::IsEnabled(blink::features::kUnboundedElement),
          base::NotFatalUntil::M158);
    secondary_location = unbounded_window->GetBounds().origin();
  }

  const gfx::Point offset_dips =
      PointAtOffsetFromOrigin(secondary_location - parent_location);
  const gfx::Vector2d offset_physical =
      ScaleToFlooredPoint(offset_dips, scale_factor).OffsetFromOrigin();

  // Queue up the request for the MAIN frame image first, but with a
  // callback that launches a second request for the secondary image.
  auto main_image_done_callback = base::BindOnce(
      [](base::OnceCallback<void(const content::CopyFromSurfaceResult&)>
             final_callback,
         const gfx::Vector2d offset, const bool was_capturing_popup,
         base::WeakPtr<RenderWidgetHostImpl> main_host,
         base::WeakPtr<RenderWidgetHostImpl> popup_host,
         base::WeakPtr<DelegatedFrameHost> popup_frame_host,
         const gfx::Rect src_subrect, const gfx::Size dst_size,
         base::TimeDelta timeout,
         const content::CopyFromSurfaceResult& main_result) {
        RenderWidgetHostViewBase* main_view =
            main_host
                ? static_cast<RenderWidgetHostViewBase*>(main_host->GetView())
                : nullptr;
        UnboundedSurfaceWindow* unbounded_window =
            main_view && main_view->HasActiveUnboundedSurface()
                ? main_view->GetUnboundedSurfaceWindow()
                : nullptr;

        const bool has_popup = popup_host && popup_frame_host;
        const bool secondary_surface_available =
            was_capturing_popup ? has_popup : !!unbounded_window;
        if (!secondary_surface_available) {
          if (base::FeatureList::IsEnabled(
                  features::kCopyFromSurfaceAlwaysCallCallback)) {
            std::move(final_callback).Run(main_result);
          }
          return;
        }

        // Build a new callback that actually combines images.
        auto popup_done_callback = base::BindOnce(
            [](base::OnceCallback<void(const content::CopyFromSurfaceResult&)>
                   final_callback,
               const gfx::Vector2d offset,
               const content::CopyFromSurfaceResult& main_result,
               const content::CopyFromSurfaceResult& popup_result) {
              // If main_result is not available, there is nothing to combine
              // into.
              if (!main_result.has_value()) {
                std::move(final_callback).Run(main_result);
                return;
              }

              if (popup_result.has_value()) {
                // Draw secondary image into main_image only if secondary_image
                // is valid.
                SkCanvas canvas(main_result->bitmap, SkSurfaceProps{});
                canvas.drawImage(popup_result->bitmap.asImage(), offset.x(),
                                 offset.y());
              }

              std::move(final_callback).Run(main_result);
            },
            std::move(final_callback), offset, main_result);

        // Second, request the secondary image.
        if (was_capturing_popup) {
          CHECK(has_popup);
          gfx::Rect popup_subrect(src_subrect - offset);
          popup_frame_host->CopyFromCompositingSurface(
              popup_subrect, dst_size, timeout, std::move(popup_done_callback));
          return;
        } else {
          CHECK(unbounded_window);
          CHECK(
              base::FeatureList::IsEnabled(blink::features::kUnboundedElement),
              base::NotFatalUntil::M158);
          gfx::Rect popup_subrect(src_subrect - offset);
          unbounded_window->CopyFromSurface(popup_subrect, dst_size, timeout,
                                            std::move(popup_done_callback));
          return;
        }
      },
      std::move(callback), offset_physical, has_popup, main_host, popup_host,
      popup_frame_host, src_subrect, dst_size, timeout);

  // Request the main image (happens first).
  main_frame_host->CopyFromCompositingSurface(
      src_subrect, dst_size, timeout, std::move(main_image_done_callback));
#endif
}

void RenderWidgetHostViewBase::CopyFromSurface(
    const gfx::Rect& src_rect,
    const gfx::Size& output_size,
    base::TimeDelta timeout,
    base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback) {
  NOTIMPLEMENTED_LOG_ONCE();
  std::move(callback).Run(base::unexpected<content::CopyFromSurfaceError>(
      content::CopyFromSurfaceError::kNotImplemented));
}

ui::FilteredGestureProvider*
RenderWidgetHostViewBase::GetFilteredGestureProviderForTesting() {
  return nullptr;
}

std::unique_ptr<viz::ClientFrameSinkVideoCapturer>
RenderWidgetHostViewBase::CreateVideoCapturer() {
  std::unique_ptr<viz::ClientFrameSinkVideoCapturer> video_capturer =
      GetHostFrameSinkManager()->CreateVideoCapturer();
  video_capturer->ChangeTarget(viz::VideoCaptureTarget(GetFrameSinkId()),
                               /*sub_capture_target_version=*/0);
  return video_capturer;
}

std::u16string RenderWidgetHostViewBase::GetSelectedText() {
  if (!GetTextInputManager())
    return std::u16string();
  return GetTextInputManager()->GetTextSelection(this)->selected_text();
}

void RenderWidgetHostViewBase::SetBackgroundColor(SkColor color) {
  // TODO(danakj): OPAQUE colors only make sense for main frame widgets,
  // as child frames are always transparent background. We should move this to
  // `blink::WebView` instead.
  CHECK(SkColorGetA(color) == SK_AlphaOPAQUE ||
        SkColorGetA(color) == SK_AlphaTRANSPARENT);
  if (default_background_color_ == color)
    return;

  bool opaque = default_background_color_
                    ? SkColorGetA(*default_background_color_)
                    : SK_AlphaOPAQUE;
  default_background_color_ = color;
  UpdateBackgroundColor();
  if (opaque != (SkColorGetA(color) == SK_AlphaOPAQUE)) {
    if (host()->owner_delegate()) {
      host()->owner_delegate()->SetBackgroundOpaque(SkColorGetA(color) ==
                                                    SK_AlphaOPAQUE);
    }
  }
}

std::optional<SkColor> RenderWidgetHostViewBase::GetBackgroundColor() {
  if (content_background_color_)
    return content_background_color_;
  return default_background_color_;
}

bool RenderWidgetHostViewBase::IsBackgroundColorOpaque() {
  std::optional<SkColor> bg_color = GetBackgroundColor();
  return bg_color ? SkColorGetA(*bg_color) == SK_AlphaOPAQUE : true;
}

void RenderWidgetHostViewBase::CopyBackgroundColorIfPresentFrom(
    const RenderWidgetHostView& other) {
  const RenderWidgetHostViewBase& other_base =
      static_cast<const RenderWidgetHostViewBase&>(other);
  if (!other_base.content_background_color_ &&
      !other_base.default_background_color_) {
    return;
  }
  if (content_background_color_ == other_base.content_background_color_ &&
      default_background_color_ == other_base.default_background_color_) {
    return;
  }
  bool was_opaque = IsBackgroundColorOpaque();
  content_background_color_ = other_base.content_background_color_;
  default_background_color_ = other_base.default_background_color_;
  UpdateBackgroundColor();
  bool opaque = IsBackgroundColorOpaque();
  if (was_opaque != opaque && host()->owner_delegate()) {
    host()->owner_delegate()->SetBackgroundOpaque(opaque);
  }
}

bool RenderWidgetHostViewBase::IsPointerLocked() {
  return false;
}

const viz::DisplayHitTestQueryMap&
RenderWidgetHostViewBase::GetDisplayHitTestQuery() const {
  return GetHostFrameSinkManager()->GetDisplayHitTestQuery();
}

bool RenderWidgetHostViewBase::
    GetIsPointerLockedUnadjustedMovementForTesting() {
  return false;
}

bool RenderWidgetHostViewBase::CanBePointerLocked() {
  return HasFocus();
}

bool RenderWidgetHostViewBase::AccessibilityHasFocus() {
  return HasFocus();
}

bool RenderWidgetHostViewBase::LockKeyboard(
    std::optional<base::flat_set<ui::DomCode>> codes) {
  NOTIMPLEMENTED_LOG_ONCE();
  return false;
}

void RenderWidgetHostViewBase::UnlockKeyboard() {
  NOTIMPLEMENTED_LOG_ONCE();
}

bool RenderWidgetHostViewBase::IsKeyboardLocked() {
  return keyboard_locked_;
}

base::flat_map<std::string, std::string>
RenderWidgetHostViewBase::GetKeyboardLayoutMap() {
  NOTIMPLEMENTED_LOG_ONCE();
  return base::flat_map<std::string, std::string>();
}

bool RenderWidgetHostViewBase::HasFallbackSurface() const {
  NOTREACHED();
}

viz::SurfaceId RenderWidgetHostViewBase::GetFallbackSurfaceIdForTesting()
    const {
  NOTREACHED();
}

void RenderWidgetHostViewBase::SetWidgetType(WidgetType widget_type) {
  widget_type_ = widget_type;
}

WidgetType RenderWidgetHostViewBase::GetWidgetType() {
  return widget_type_;
}

gfx::Rect RenderWidgetHostViewBase::GetBoundsInScreenWithoutTransform() {
  return GetBoundsInScreen();
}

gfx::Rect RenderWidgetHostViewBase::GetViewBoundsWithoutTransform() {
  return GetViewBounds();
}

gfx::AcceleratedWidget
    RenderWidgetHostViewBase::AccessibilityGetAcceleratedWidget() {
  return gfx::kNullAcceleratedWidget;
}

gfx::NativeViewAccessible
    RenderWidgetHostViewBase::AccessibilityGetNativeViewAccessible() {
  return gfx::NativeViewAccessible();
}

gfx::NativeViewAccessible
RenderWidgetHostViewBase::AccessibilityGetNativeViewAccessibleForWindow() {
  return gfx::NativeViewAccessible();
}

ui::AXTreeID RenderWidgetHostViewBase::AccessibilityGetParentAXTreeID() {
  return ui::AXTreeIDUnknown();
}

bool RenderWidgetHostViewBase::ShouldInitiateStylusWriting() {
  return false;
}

bool RenderWidgetHostViewBase::RequestRepaintOnNewSurface() {
  return false;
}

// Send system cursor size to the renderer via UpdateScreenInfo().
void RenderWidgetHostViewBase::UpdateSystemCursorSize(
    const gfx::Size& cursor_size) {
  system_cursor_size_ = cursor_size;
  UpdateScreenInfo();
}

void RenderWidgetHostViewBase::UpdateScreenInfo() {
  bool force_sync_visual_properties = false;
  // Delegate, which is usually WebContentsImpl, do not send rect updates for
  // popups as they are not registered as FrameTreeNodes. Instead, send screen
  // rects directly to host and force synchronization of visual properties so
  // that blink knows host changed bounds. This only happens if the change was
  // instantiated by system server/compositor (for example, Wayland, which
  // may reposition a popup if part of it is going to be shown outside a
  // display's work area. Note that Wayland clients cannot know where their
  // windows are located and cannot adjust bounds).
  if (widget_type_ == WidgetType::kPopup) {
    if (host()) {
      force_sync_visual_properties = true;
      host()->SendScreenRects();
    }
  } else {
    if (host() && host()->delegate())
      host()->delegate()->SendScreenRects();
  }

  auto new_screen_infos = GetNewScreenInfosForUpdate();
  new_screen_infos.mutable_current().handwriting_radius = handwriting_radius_;

  if (scale_override_for_capture_ != 1.0f) {
    // If HiDPI capture mode is active, adjust the device scale factor to
    // increase the rendered pixel count. |new_screen_infos| always contains the
    // unmodified original values for the display, and a copy of it is saved in
    // |screen_infos_|, with a modification applied if applicable. When HiDPI
    // mode is turned off (the scale override is 1.0), the original
    // |new_screen_infos| value gets copied unchanged to |screen_infos_|.
    const float old_device_scale_factor =
        new_screen_infos.current().device_scale_factor;
    new_screen_infos.mutable_current().device_scale_factor =
        old_device_scale_factor * scale_override_for_capture_;
    DVLOG(1) << __func__ << ": Overriding device_scale_factor from "
             << old_device_scale_factor << " to "
             << new_screen_infos.current().device_scale_factor
             << " for capture.";
  }

#if BUILDFLAG(IS_OZONE)
  // There are platforms where no global screen coordinates are available for
  // client applications, and scaling is done in a per-window basis (rather than
  // per-display) and controlled by the display server. In such cases, the
  // ScreenInfo Web API is mostly pointless. To avoid distorted graphics in web
  // contents, override the display scale with the preferred window scale here.
  // TODO(crbug.com/336007385): Consolidate screen representation and a less
  // hacky scale handling in platforms that support per-window scaling.
  if (ui::OzonePlatform::GetInstance()
          ->GetPlatformRuntimeProperties()
          .supports_per_window_scaling) {
    const float window_scale =
        display::Screen::Get()
            ->GetPreferredScaleFactorForView(GetNativeView())
            .value_or(1.0f);
    auto& screen = new_screen_infos.mutable_current();
    const float old = screen.device_scale_factor;
    if (window_scale != old) {
      VLOG(1) << __func__ << ": Overriding scale for screen '" << screen.label
              << "' from " << old << " with windows scale " << window_scale;
      screen.device_scale_factor = window_scale;
      force_sync_visual_properties = true;
    }
  }
#endif  // BUILDFLAG(IS_OZONE)

  if (screen_infos_ == new_screen_infos && !force_sync_visual_properties)
    return;

  // We need to look at `orientation_type` which is marked as kUndefined at
  // startup. Unlike `orientation_angle` that uses 0 degrees as the default.
  // This accounts for devices which have a default landscape orientation, such
  // as tablets. We do not want the first UpdateScreenInfo to be treated as a
  // rotation.
  const bool has_rotation_changed =
      screen_infos_.current().orientation_type !=
          display::mojom::ScreenOrientation::kUndefined &&
      screen_infos_.current().orientation_type !=
          new_screen_infos.current().orientation_type;
  screen_infos_ = std::move(new_screen_infos);

  // Notify the associated RenderWidgetHostImpl when screen info has changed.
  // That will synchronize visual properties needed for frame tree rendering
  // and for web platform APIs that expose screen and window info and events.
  if (host()) {
    OnSynchronizedDisplayPropertiesChanged(has_rotation_changed);
    host()->NotifyScreenInfoChanged();
  }
}

void RenderWidgetHostViewBase::UpdateActiveState(bool active) {
  // Send active state through the delegate if there is one to make sure
  // it stays consistent across all widgets in the tab. Not every
  // RenderWidgetHost has a delegate (for example, drop-down widgets).
  if (host()->delegate())
    host()->delegate()->SendActiveState(active);
  else
    host()->SetActive(active);
}

void RenderWidgetHostViewBase::DidUnregisterFromTextInputManager(
    TextInputManager* text_input_manager) {
  CHECK(text_input_manager && text_input_manager_ == text_input_manager);

  text_input_manager_ = nullptr;
}

void RenderWidgetHostViewBase::EnableAutoResize(const gfx::Size& min_size,
                                                const gfx::Size& max_size) {
  host()->SetAutoResize(true, min_size, max_size);
  host()->SynchronizeVisualProperties();
}

bool RenderWidgetHostViewBase::IsAutoResizeEnabled() {
  return host()->auto_resize_enabled();
}

void RenderWidgetHostViewBase::DisableAutoResize(const gfx::Size& new_size) {
  // Note that for some subclasses, such as RenderWidgetHostViewAura, setting
  // the view size may trigger the synchronization on the visual properties. As
  // a result, it may crete the intermediate status that the view size has
  // changed while the auto resize status is obsolete, and then brings
  // unnecessary updates in view layout. Hence we should disable the auto
  // resize before setting the view size.
  host()->SetAutoResize(false, gfx::Size(), gfx::Size());
  if (!new_size.IsEmpty())
    SetSize(new_size);
  host()->SynchronizeVisualProperties();
}

viz::ScopedSurfaceIdAllocator
RenderWidgetHostViewBase::DidUpdateVisualProperties(
    const cc::RenderFrameMetadata& metadata) {
  // This doesn't suppress allocation. Derived classes that need suppression
  // should override this function.
  base::OnceCallback<void()> allocation_task =
      base::BindOnce(&RenderWidgetHostViewBase::SynchronizeVisualProperties,
                     weak_factory_.GetWeakPtr());
  return viz::ScopedSurfaceIdAllocator(std::move(allocation_task));
}

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

display::ScreenInfo RenderWidgetHostViewBase::GetScreenInfo() const {
  return screen_infos_.current();
}

display::ScreenInfos RenderWidgetHostViewBase::GetScreenInfos() const {
  return screen_infos_;
}

void RenderWidgetHostViewBase::ResetGestureDetection() {}

void RenderWidgetHostViewBase::SetShouldUseDefaultDeadlineOnResize(
    bool enable) {
  NOTIMPLEMENTED() << "Not supported on platform";
}

float RenderWidgetHostViewBase::GetDeviceScaleFactor() const {
  return GetScreenInfos().current().device_scale_factor;
}

base::WeakPtr<input::RenderWidgetHostViewInput>
RenderWidgetHostViewBase::GetInputWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

input::RenderInputRouter* RenderWidgetHostViewBase::GetViewRenderInputRouter() {
  return host() ? host()->GetRenderInputRouter() : nullptr;
}

void RenderWidgetHostViewBase::SetScaleOverrideForCapture(float scale) {
  DVLOG(1) << __func__ << ": override=" << scale;
  scale_override_for_capture_ = scale;
  UpdateScreenInfo();
}

float RenderWidgetHostViewBase::GetScaleOverrideForCapture() const {
  return scale_override_for_capture_;
}

void RenderWidgetHostViewBase::OnAutoscrollStart() {
  if (!GetMouseWheelPhaseHandler())
    return;

  // End the current scrolling seqeunce when autoscrolling starts.
  GetMouseWheelPhaseHandler()->DispatchPendingWheelEndEvent();
}

void RenderWidgetHostViewBase::OnAutoscrollTargetResolved(bool success) {
  host()->OnAutoscrollTargetResolved(success);
}

DevicePosturePlatformProvider*
RenderWidgetHostViewBase::GetDevicePosturePlatformProvider() {
  if (!host() || !host()->delegate()) {
    return nullptr;
  }

  DevicePostureProviderImpl* posture_provider =
      static_cast<DevicePostureProviderImpl*>(
          host()->delegate()->GetDevicePostureProvider());
  if (!posture_provider) {
    return nullptr;
  }

  return posture_provider->platform_provider();
}

gfx::Size RenderWidgetHostViewBase::GetVisibleViewportSize() {
  return GetViewBounds().size();
}

gfx::Size RenderWidgetHostViewBase::GetVisibleViewportSizeDevicePx() {
  return gfx::ScaleToCeiledSize(GetViewBounds().size(), GetDeviceScaleFactor());
}

void RenderWidgetHostViewBase::SetInsets(const gfx::Insets& insets) {
  NOTIMPLEMENTED_LOG_ONCE();
}

const viz::LocalSurfaceId&
RenderWidgetHostViewBase::IncrementSurfaceIdForNavigation() {
  NOTREACHED();
}

void RenderWidgetHostViewBase::OnOldViewDidNavigatePreCommit() {}

void RenderWidgetHostViewBase::OnNewViewDidNavigatePostCommit() {}

void RenderWidgetHostViewBase::OnFrameTokenChangedForView(
    uint32_t frame_token,
    base::TimeTicks activation_time) {
  if (host())
    host()->DidProcessFrame(frame_token, activation_time);
}

void RenderWidgetHostViewBase::ProcessMouseEvent(
    const blink::WebMouseEvent& event,
    const ui::LatencyInfo& latency) {
  // TODO(crbug.com/40564125): Figure out the reason |host| is null here in all
  // Process* functions.
  if (!host())
    return;

  // Ensure the event is not routed to a prerendered page.
  if (host()->frame_tree() && host()->frame_tree()->is_prerendering()) {
    NOTREACHED();
  }

  PreProcessMouseEvent(event);
  host()->ForwardMouseEventWithLatencyInfo(event, latency);
}

void RenderWidgetHostViewBase::ProcessMouseWheelEvent(
    const blink::WebMouseWheelEvent& event,
    const ui::LatencyInfo& latency) {
  if (!host())
    return;

  // Ensure the event is not routed to a prerendered page.
  if (host()->frame_tree() && host()->frame_tree()->is_prerendering()) {
    NOTREACHED();
  }

  host()->ForwardWheelEventWithLatencyInfo(event, latency);
}

void RenderWidgetHostViewBase::ProcessTouchEvent(
    const blink::WebTouchEvent& event,
    const ui::LatencyInfo& latency) {
  if (!host())
    return;

  // Ensure the event is not routed to a prerendered page.
  if (host()->frame_tree() && host()->frame_tree()->is_prerendering()) {
    NOTREACHED();
  }

  PreProcessTouchEvent(event);
  host()->GetRenderInputRouter()->ForwardTouchEventWithLatencyInfo(event,
                                                                   latency);
}

void RenderWidgetHostViewBase::ProcessGestureEvent(
    const blink::WebGestureEvent& event,
    const ui::LatencyInfo& latency) {
  if (!host())
    return;

  // Ensure the event is not routed to a prerendered page.
  if (host()->frame_tree() && host()->frame_tree()->is_prerendering()) {
    NOTREACHED();
  }

  host()->GetRenderInputRouter()->ForwardGestureEventWithLatencyInfo(event,
                                                                     latency);
}

gfx::PointF RenderWidgetHostViewBase::TransformPointToRootCoordSpaceF(
    const gfx::PointF& point) const {
  return RenderWidgetHostViewInput::TransformPointToRootCoordSpaceF(point);
}

gfx::PointF RenderWidgetHostViewBase::TransformRootPointToViewCoordSpace(
    const gfx::PointF& point) {
  return RenderWidgetHostViewInput::TransformRootPointToViewCoordSpace(point);
}

bool RenderWidgetHostViewBase::IsRenderWidgetHostViewChildFrame() const {
  return false;
}

bool RenderWidgetHostViewBase::HasSize() const {
  return true;
}

void RenderWidgetHostViewBase::Destroy() {
  host_ = nullptr;
}

bool RenderWidgetHostViewBase::CanSynchronizeVisualProperties() {
  return true;
}

double RenderWidgetHostViewBase::GetCSSZoomFactor() const {
  return 1.0;
}

void RenderWidgetHostViewBase::TextInputStateChanged(
    const ui::mojom::TextInputState& text_input_state) {
  if (GetTextInputManager())
    GetTextInputManager()->UpdateTextInputState(this, text_input_state);
}

void RenderWidgetHostViewBase::ImeCancelComposition() {
  if (GetTextInputManager())
    GetTextInputManager()->ImeCancelComposition(this);
}

void RenderWidgetHostViewBase::ImeCompositionRangeChanged(
    const gfx::Range& range,
    const std::optional<std::vector<gfx::Rect>>& character_bounds) {
  if (GetTextInputManager()) {
    GetTextInputManager()->ImeCompositionRangeChanged(this, range,
                                                      character_bounds);
  }
}

TextInputManager* RenderWidgetHostViewBase::GetTextInputManager() {
  if (text_input_manager_)
    return text_input_manager_;

  if (!host() || !host()->delegate())
    return nullptr;

  // This RWHV needs to be registered with the TextInputManager so that the
  // TextInputManager starts tracking its state, and observing its lifetime.
  text_input_manager_ = host()->delegate()->GetTextInputManager();
  if (text_input_manager_)
    text_input_manager_->Register(this);

  return text_input_manager_;
}

TouchSelectionControllerClientManager*
RenderWidgetHostViewBase::GetTouchSelectionControllerClientManager() {
  return nullptr;
}

TouchSelectionControllerInputObserver*
RenderWidgetHostViewBase::GetTouchSelectionControllerInputObserver() {
  return nullptr;
}

RenderWidgetHost::InputEventObserver*
RenderWidgetHostViewBase::GetInputTransferHandlerObserver() {
  return nullptr;
}

void RenderWidgetHostViewBase::SynchronizeVisualProperties() {
  if (host())
    host()->SynchronizeVisualProperties();
}

display::ScreenInfos RenderWidgetHostViewBase::GetNewScreenInfosForUpdate() {
  // RWHVChildFrame gets its ScreenInfos from the CrossProcessFrameConnector.
  CHECK(!IsRenderWidgetHostViewChildFrame());

  display::ScreenInfos screen_infos;

  if (auto* screen = display::Screen::Get()) {
    gfx::NativeView native_view = GetNativeView();
    const auto& display = native_view
                              ? screen->GetDisplayNearestView(native_view)
                              : screen->GetPrimaryDisplay();
    screen_infos = screen->GetScreenInfosNearestDisplay(display.id());
  } else {
    // If there is no Screen, create fake ScreenInfos (for tests).
    screen_infos = display::ScreenInfos(display::ScreenInfo());
  }

  // Set system cursor size separately as it's not a property of screen or
  // display.
  screen_infos.system_cursor_size = system_cursor_size_;

  return screen_infos;
}

void RenderWidgetHostViewBase::DidNavigate() {
  if (host())
    host()->SynchronizeVisualProperties();
}

void RenderWidgetHostViewBase::CreateUnboundedSurface(
    mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
    mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient> client,
    const gfx::Rect& bounds_in_dips,
    base::WeakPtr<RenderWidgetHostViewBase> subframe_view) {}

void RenderWidgetHostViewBase::UpdateUnboundedSurfaceBoundsInSubframeContext(
    const gfx::Rect& bounds_in_dips,
    RenderWidgetHostViewBase* subframe_view) {
  if (!unbounded_surface_window_) {
    return;
  }
  UpdateUnboundedSurfaceBounds(
      ConvertSubframeBoundsToScreen(bounds_in_dips, subframe_view));
}

gfx::Rect RenderWidgetHostViewBase::ConvertSubframeBoundsToScreen(
    const gfx::Rect& bounds_in_dips,
    RenderWidgetHostViewBase* subframe_view) {
  gfx::Rect transformed_bounds = bounds_in_dips;
  if (subframe_view) {
    TransformPointAndRectToRootView(subframe_view, this, nullptr,
                                    &transformed_bounds);
  }
  gfx::Rect bounds_in_screen = transformed_bounds;
  bounds_in_screen.Offset(GetViewBounds().OffsetFromOrigin());
  return bounds_in_screen;
}

void RenderWidgetHostViewBase::UpdateUnboundedSurfaceBounds(
    const gfx::Rect& bounds_in_screen) {
  if (unbounded_surface_window_) {
    unbounded_surface_window_->SetBounds(bounds_in_screen);
  }
}

void RenderWidgetHostViewBase::DismissUnboundedSurface() {
  if (unbounded_surface_window_) {
    unbounded_surface_window_->Dismiss();
  }
}

void RenderWidgetHostViewBase::DestroyUnboundedSurface(
    base::WeakPtr<UnboundedSurfaceWindow> window) {
  // Only destroy the window if it matches the active window to prevent
  // asynchronous destruction tasks from destroying a newly created window.
  // If the weak pointer is null, it means the window was already destroyed,
  // so we do nothing.
  if (window && unbounded_surface_window_.get() == window.get()) {
    unbounded_surface_window_.reset();
  }
}

bool RenderWidgetHostViewBase::HasActiveUnboundedSurface() const {
  return !!unbounded_surface_window_;
}

viz::FrameSinkId RenderWidgetHostViewBase::GetUnboundedSurfaceFrameSinkId()
    const {
  return unbounded_surface_window_ ? unbounded_surface_window_->GetFrameSinkId()
                                   : viz::FrameSinkId();
}

viz::LocalSurfaceId
RenderWidgetHostViewBase::GetUnboundedSurfaceLocalSurfaceId() const {
  return unbounded_surface_window_
             ? unbounded_surface_window_->GetLocalSurfaceId()
             : viz::LocalSurfaceId();
}

void RenderWidgetHostViewBase::GetUnboundedSurfaceCompositorFrameSink(
    mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink,
    mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client) {
  if (unbounded_surface_window_) {
    unbounded_surface_window_->GetCompositorFrameSink(std::move(sink),
                                                      std::move(client));
  }
}

UnboundedSurfaceWindow* RenderWidgetHostViewBase::GetUnboundedSurfaceWindow()
    const {
  CHECK(!unbounded_surface_window_ ||
            base::FeatureList::IsEnabled(blink::features::kUnboundedElement),
        base::NotFatalUntil::M158);
  return unbounded_surface_window_.get();
}

WebContentsAccessibility*
RenderWidgetHostViewBase::GetWebContentsAccessibility() {
  return nullptr;
}

void RenderWidgetHostViewBase::SetTooltipObserverForTesting(
    TooltipObserver* observer) {
  tooltip_observer_for_testing_ = observer;
}

ui::Compositor* RenderWidgetHostViewBase::GetCompositor() {
  return nullptr;
}

ui::mojom::VirtualKeyboardMode
RenderWidgetHostViewBase::GetVirtualKeyboardMode() {
  // Only platforms supporting these APIs will implement this.
  return ui::mojom::VirtualKeyboardMode::kUnset;
}

bool RenderWidgetHostViewBase::IsHTMLFormPopup() const {
  return false;
}

void RenderWidgetHostViewBase::OnShowWithPageVisibility(
    PageVisibilityState page_visibility) {
  if (!host())
    return;

  EnsurePlatformVisibility(page_visibility);

  VisibleTimeRequestTrigger& visible_time_request_trigger =
      host_->GetVisibleTimeRequestTrigger();

  // NB: don't call visible_time_request_trigger.TakeRequest() unless the
  // request will be used. If it isn't used here it must be left in the trigger
  // for the next call.

  const bool web_contents_is_visible =
      page_visibility == PageVisibilityState::kVisible;

  if (host_->IsHidden()) {
    // If the WebContents is becoming visible, ask the compositor to report the
    // visibility time for metrics. Otherwise the widget is being rendered even
    // though the WebContents is hidden or occluded, for example due to being
    // captured, so it should not be included in visibility time metrics.
    NotifyHostAndDelegateOnWasShown(
        web_contents_is_visible ? visible_time_request_trigger.TakeRequest()
                                : std::nullopt);
    return;
  }

  // `page_visibility` changed while the widget remains visible (kVisible ->
  // kHiddenButPainting or vice versa). Nothing to do except update the
  // visible time request, if any.
  if (web_contents_is_visible) {
    // The widget is already rendering, but now the WebContents is becoming
    // visible, so send any visibility time request to the compositor now.
    if (auto visible_time_request =
            visible_time_request_trigger.TakeRequest()) {
      RequestSuccessfulPresentationTimeFromHostOrDelegate(
          std::move(*visible_time_request));
    }
    return;
  }

  // The widget should keep rendering but the WebContents is no longer
  // visible. If the compositor didn't already report the visibility time,
  // it's too late. (For example, if the WebContents is being captured and
  // was put in the foreground and then quickly hidden again before the
  // compositor submitted a frame. The compositor will keep submitting
  // frames for the capture but they should not be included in the
  // visibility metrics.)
  CancelSuccessfulPresentationTimeRequestForHostAndDelegate();
  return;
}

void RenderWidgetHostViewBase::SetIsFrameSinkIdOwner(bool is_owner) {
  if (is_frame_sink_id_owner_ == is_owner) {
    return;
  }

  is_frame_sink_id_owner_ = is_owner;
  UpdateFrameSinkIdRegistration();
}

void RenderWidgetHostViewBase::UpdateFrameSinkIdRegistration() {
  // If Destroy() has been called before we get here, host_ may be null.
  if (!host() || !host()->delegate() ||
      !host()->delegate()->GetInputEventRouter()) {
    return;
  }

  // Let the page-level input event router know about our frame sink ID
  // for surface-based hit testing.
  auto* router = host()->delegate()->GetInputEventRouter();
  if (is_frame_sink_id_owner_) {
    if (!router->IsViewInMap(this)) {
      router->AddFrameSinkIdOwner(GetFrameSinkId(), this);
    }
  } else if (router->IsViewInMap(this)) {
    // Ensure this view is the owner before removing the associated FrameSinkId
    // from input tracking. Speculative views start as non-owing and will not
    // register until ownership has been transferred.
    router->RemoveFrameSinkIdOwner(GetFrameSinkId());
  }
}

void RenderWidgetHostViewBase::SetViewTransitionResources(
    std::unique_ptr<ScopedViewTransitionResources> resources) {
  view_transition_resources_ = std::move(resources);
}

std::optional<uint32_t>
RenderWidgetHostView::GetForceSpecifiedDeadlineForTesting() {
  return std::nullopt;
}

}  // namespace content
