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

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

#include <algorithm>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "components/input/cursor_manager.h"
#include "components/input/features.h"
#include "components/input/render_widget_host_input_event_router.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/renderer_host/cross_process_frame_connector.h"
#include "content/browser/renderer_host/frame_connector.h"
#include "content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h"
#include "content/browser/renderer_host/input/touch_selection_controller_input_observer.h"
#include "content/browser/renderer_host/render_view_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_view_event_handler.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/features.h"
#include "content/common/input/synthetic_gesture_target.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/frame/frame_visual_properties.h"
#include "third_party/blink/public/common/input/web_touch_event.h"
#include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom.h"
#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/ime/mojom/text_input_state.mojom.h"
#include "ui/display/display_util.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/touch_selection/touch_selection_controller.h"

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

namespace content {

// static
RenderWidgetHostViewChildFrame* RenderWidgetHostViewChildFrame::Create(
    RenderWidgetHost* widget,
    const display::ScreenInfos& parent_screen_infos) {
  RenderWidgetHostViewChildFrame* view =
      new RenderWidgetHostViewChildFrame(widget, parent_screen_infos);
  view->Init();
  return view;
}

RenderWidgetHostViewChildFrame::RenderWidgetHostViewChildFrame(
    RenderWidgetHost* widget_host,
    const display::ScreenInfos& parent_screen_infos)
    : RenderWidgetHostViewBase(widget_host),
      frame_sink_id_(widget_host->GetFrameSinkId()),
      frame_connector_(nullptr) {
  // TODO(enne): this appears to have a null current() in some tests.
  screen_infos_ = parent_screen_infos;

  input_helper_ =
      std::make_unique<input::ChildFrameInputHelper>(this, frame_connector_);

  host()->render_frame_metadata_provider()->AddObserver(this);
}

RenderWidgetHostViewChildFrame::~RenderWidgetHostViewChildFrame() {
  // TODO(wjmaclean): The next two lines are a speculative fix for
  // https://crbug.com/760074, based on the theory that perhaps something is
  // destructing the class without calling Destroy() first.
  if (frame_connector_)
    DetachFromTouchSelectionClientManagerIfNecessary();

  if (auto* frame_sink_manager = GetHostFrameSinkManager()) {
    if (has_frame_sink_hierarchy_registered_) {
      CHECK(parent_frame_sink_id_.is_valid());
      frame_sink_manager->UnregisterFrameSinkHierarchy(parent_frame_sink_id_,
                                                       frame_sink_id_);
      has_frame_sink_hierarchy_registered_ = false;
    }
    if (is_frame_sink_id_owner()) {
      frame_sink_manager->InvalidateFrameSinkId(frame_sink_id_, this, {});
    }
  }
}

void RenderWidgetHostViewChildFrame::Init() {
  RegisterFrameSinkId();
  host()->SetView(this);
  GetTextInputManager();
}

void RenderWidgetHostViewChildFrame::
    DetachFromTouchSelectionClientManagerIfNecessary() {
  if (!selection_controller_client_)
    return;

  auto* root_view = view_for_touch_selection_client_manager_.get();
  view_for_touch_selection_client_manager_.reset();
  if (root_view) {
    auto* manager = root_view->GetTouchSelectionControllerClientManager();
    if (manager) {
      manager->RemoveObserver(this);
#if BUILDFLAG(IS_ANDROID)
      auto* observer = root_view->GetTouchSelectionControllerInputObserver();
      if (observer) {
        host()->RemoveInputEventObserver(observer);
      }
#endif
    }
  } else {
    // We should never get here, but maybe we are? Test this out with a
    // diagnostic we can track. If we do get here, it would explain
    // https://crbug.com/760074.
    base::debug::DumpWithoutCrashing();
  }

  selection_controller_client_.reset();
}

void RenderWidgetHostViewChildFrame::SetFrameConnector(
    FrameConnector* frame_connector) {
  if (frame_connector_ == frame_connector)
    return;

  if (frame_connector_) {
    SetParentFrameSinkId(viz::FrameSinkId());

    // Unlocks the mouse if this RenderWidgetHostView holds the lock.
    UnlockPointer();
    DetachFromTouchSelectionClientManagerIfNecessary();

    auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
    if (root_view) {
      auto* input_transfer_handler =
          root_view->GetInputTransferHandlerObserver();
      if (input_transfer_handler) {
        host()->RemoveInputEventObserver(input_transfer_handler);
      }
    }
  }
  frame_connector_ = frame_connector;
  input_helper_->SetDelegate(frame_connector);

  if (!frame_connector_)
    return;

  RenderWidgetHostViewBase* parent_view =
      frame_connector_->GetParentRenderWidgetHostView();

  if (parent_view) {
    CHECK(parent_view->GetFrameSinkId().is_valid(), base::NotFatalUntil::M152);
    SetParentFrameSinkId(parent_view->GetFrameSinkId());
  }

  UpdateScreenInfo();

  auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
  if (root_view) {
    auto* input_transfer_handler = root_view->GetInputTransferHandlerObserver();
    if (input_transfer_handler) {
      host()->AddInputEventObserver(input_transfer_handler);
    }
    auto* manager = root_view->GetTouchSelectionControllerClientManager();
    if (manager) {
      // We have managers in Aura and Android, as well as outside of content/.
      // There is no manager for Mac OS.
      selection_controller_client_ =
          std::make_unique<TouchSelectionControllerClientChildFrame>(this,
                                                                     manager);
      manager->AddObserver(this);
      view_for_touch_selection_client_manager_ = root_view->GetWeakPtr();

#if BUILDFLAG(IS_ANDROID)
      auto* observer = root_view->GetTouchSelectionControllerInputObserver();
      if (observer) {
        host()->AddInputEventObserver(observer);
      }
#endif
    }
  }

  if (frame_connector_ && pending_sizing_info_) {
    CHECK(base::FeatureList::IsEnabled(blink::features::kResponsiveIframes),
          base::NotFatalUntil::M152);
    frame_connector_->SendIntrinsicSizingInfoToParent(
        std::move(pending_sizing_info_));
  }
}

void RenderWidgetHostViewChildFrame::UpdateIntrinsicSizingInfo(
    blink::mojom::IntrinsicSizingInfoPtr sizing_info) {
  if (frame_connector_) {
    frame_connector_->SendIntrinsicSizingInfoToParent(std::move(sizing_info));
  } else if (base::FeatureList::IsEnabled(
                 blink::features::kResponsiveIframes)) {
    pending_sizing_info_ = std::move(sizing_info);
  }
}

std::unique_ptr<SyntheticGestureTarget>
RenderWidgetHostViewChildFrame::CreateSyntheticGestureTarget() {
  // Sythetic gestures should be sent to the root view.
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::OnManagerWillDestroy(
    TouchSelectionControllerClientManager* manager) {
  // We get the manager via the observer callback instead of through the
  // frame_connector_ since our connection to the root_view may disappear by
  // the time this function is called, but before frame_connector_ is reset.
  manager->RemoveObserver(this);
  selection_controller_client_.reset();
}

void RenderWidgetHostViewChildFrame::InitAsChild(gfx::NativeView parent_view) {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::SetSize(const gfx::Size& size) {
  // Resizing happens in FrameConnector for child frames.
}

void RenderWidgetHostViewChildFrame::SetBounds(const gfx::Rect& rect) {
  // Resizing happens in FrameConnector for child frames.
  if (rect != last_screen_rect_) {
    last_screen_rect_ = rect;
    host()->SendScreenRects();
  }
}

void RenderWidgetHostViewChildFrame::Focus() {
  if (!frame_connector_) {
    return;
  }
  if (frame_connector_->HasFocus() ==
      FrameConnector::RootViewFocusState::kNotFocused) {
    return frame_connector_->FocusRootView();
  }
}

bool RenderWidgetHostViewChildFrame::HasFocus() {
  if (!frame_connector_) {
    return false;
  }
  return frame_connector_->HasFocus() ==
         FrameConnector::RootViewFocusState::kFocused;
}

bool RenderWidgetHostViewChildFrame::IsSurfaceAvailableForCopy() {
  return GetLocalSurfaceId().is_valid();
}

void RenderWidgetHostViewChildFrame::ShowWithVisibility(
    PageVisibilityState /*page_visibility*/) {
  if (frame_connector_) {
    frame_connector_->SetKeepSurfaceAlive(true);
  }

  if (!host()->IsHidden()) {
    return;
  }

  if (!CanBecomeVisible())
    return;

  host()->WasShown({} /* record_tab_switch_time_request */);

  if (frame_connector_)
    frame_connector_->SetVisibilityForChildViews(true);
}

void RenderWidgetHostViewChildFrame::Show() {
  ShowWithVisibility(PageVisibilityState::kVisible);
}

void RenderWidgetHostViewChildFrame::Hide() {
  if (frame_connector_) {
    frame_connector_->SetKeepSurfaceAlive(false);
  }

  if (host()->IsHidden()) {
    return;
  }

  host()->WasHidden();

  if (frame_connector_)
    frame_connector_->SetVisibilityForChildViews(false);
}

bool RenderWidgetHostViewChildFrame::IsShowing() {
  return !host()->IsHidden();
}

void RenderWidgetHostViewChildFrame::WasOccluded() {
  Hide();
}

gfx::Rect RenderWidgetHostViewChildFrame::GetViewBoundsHelper(
    bool without_transform) {
  gfx::Rect screen_space_rect;
  if (frame_connector_) {
    screen_space_rect = frame_connector_->GetRectInParentViewInDip();

    RenderWidgetHostViewBase* parent_view =
        frame_connector_->GetParentRenderWidgetHostView();

    // The parent_view can be null in tests when using a TestWebContents.
    if (parent_view) {
      // Translate screen_space_rect by the parent's RenderWidgetHostView
      // offset.
      gfx::Vector2d offset;
      if (without_transform) {
        offset =
            parent_view->GetViewBoundsWithoutTransform().OffsetFromOrigin();
      } else {
        offset = parent_view->GetViewBounds().OffsetFromOrigin();
      }
      screen_space_rect.Offset(offset);
    }
    // TODO(wjmaclean): GetViewBounds is a bit of a mess. It's used to determine
    // the size of the renderer content and where to place context menus and so
    // on. We want the location of the frame in screen coordinates to place
    // popups but we want the size in local coordinates to produce the right-
    // sized CompositorFrames. https://crbug.com/928825.
    screen_space_rect.set_size(frame_connector_->GetLocalFrameSizeInDip());
  }
  return screen_space_rect;
}

gfx::Rect RenderWidgetHostViewChildFrame::GetViewBounds() {
  return GetViewBoundsHelper(/*without_transform=*/false);
}

gfx::Rect RenderWidgetHostViewChildFrame::GetViewBoundsWithoutTransform() {
  return GetViewBoundsHelper(/*without_transform=*/true);
}

gfx::Size RenderWidgetHostViewChildFrame::GetVisibleViewportSize() {
  // For subframes, the visual viewport corresponds to the main frame size so
  // this method would not even be called, the main frame's value should be
  // used instead. However a nested WebContents will have a ChildFrame view used
  // for the main frame.
  CHECK(host()->owner_delegate(), base::NotFatalUntil::M152);

  gfx::Rect requested_rect(GetRequestedRendererSize());
  requested_rect.Inset(insets_);
  return requested_rect.size();
}

gfx::Size RenderWidgetHostViewChildFrame::GetVisibleViewportSizeDevicePx() {
  // For subframes, the visual viewport corresponds to the main frame size so
  // this method would not even be called, the main frame's value should be
  // used instead. However a nested WebContents will have a ChildFrame view used
  // for the main frame.
  CHECK(host()->owner_delegate(), base::NotFatalUntil::M152);

  gfx::Rect requested_rect(GetRequestedRendererSizeDevicePx());
  auto scaled_insets = ScaleToCeiledInsets(insets_, GetDeviceScaleFactor());
  requested_rect.Inset(scaled_insets);
  return requested_rect.size();
}

void RenderWidgetHostViewChildFrame::SetInsets(const gfx::Insets& insets) {
  // Insets are used only for <webview> and are used to let the UI know it's
  // being obscured (for e.g. by the virtual keyboard).
  insets_ = insets;
  host()->SynchronizeVisualProperties(!insets_.IsEmpty());
}

gfx::NativeView RenderWidgetHostViewChildFrame::GetNativeView() {
  if (!frame_connector_)
    return gfx::NativeView();

  RenderWidgetHostView* parent_view =
      frame_connector_->GetParentRenderWidgetHostView();
  return parent_view ? parent_view->GetNativeView() : gfx::NativeView();
}

gfx::NativeViewAccessible
RenderWidgetHostViewChildFrame::GetNativeViewAccessible() {
  if (!GetRootView()) {
    return gfx::NativeViewAccessible();
  }
  return GetRootView()->GetNativeViewAccessible();
}

void RenderWidgetHostViewChildFrame::UpdateFrameSinkIdRegistration() {
  RenderWidgetHostViewBase::UpdateFrameSinkIdRegistration();
  if (is_frame_sink_id_owner()) {
    GetHostFrameSinkManager()->RegisterFrameSinkId(
        frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
    GetHostFrameSinkManager()->SetFrameSinkDebugLabel(
        frame_sink_id_, "RenderWidgetHostViewChildFrame");
  }
}

void RenderWidgetHostViewChildFrame::UpdateBackgroundColor() {
  CHECK(GetBackgroundColor(), base::NotFatalUntil::M152);

  SkColor color = *GetBackgroundColor();
  // TODO(crbug.com/535539883): CHECK-exclusion: Convert to a CHECK once we
  // are confident it won't be triggered.
  DCHECK(SkColorGetA(color) == SK_AlphaOPAQUE ||
         SkColorGetA(color) == SK_AlphaTRANSPARENT);
  if (host()->owner_delegate()) {
    host()->owner_delegate()->SetBackgroundOpaque(SkColorGetA(color) ==
                                                  SK_AlphaOPAQUE);
  }
}

std::optional<DisplayFeature>
RenderWidgetHostViewChildFrame::GetDisplayFeature() {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::
    DisableDisplayFeatureOverrideForEmulation() {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::OverrideDisplayFeatureForEmulation(
    const DisplayFeature*) {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::NotifyHostAndDelegateOnWasShown(
    std::optional<blink::RecordContentToVisibleTimeRequest>) {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::
    RequestSuccessfulPresentationTimeFromHostOrDelegate(
        blink::RecordContentToVisibleTimeRequest) {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::
    CancelSuccessfulPresentationTimeRequestForHostAndDelegate() {
  NOTREACHED();
}

gfx::Size RenderWidgetHostViewChildFrame::GetCompositorViewportPixelSize() {
  if (frame_connector_)
    return frame_connector_->GetLocalFrameSizeInPixels();
  return gfx::Size();
}

#if BUILDFLAG(IS_ANDROID)
bool RenderWidgetHostViewChildFrame::IsTouchSequencePotentiallyActiveOnViz() {
  RenderWidgetHostViewBase* root_view = GetRootView();
  if (!root_view) {
    return false;
  }
  return root_view->IsTouchSequencePotentiallyActiveOnViz();
}

void RenderWidgetHostViewChildFrame::RequestInputBackForDragAndDrop(
    WeakDocumentPtr source_document,
    blink::mojom::DragDataPtr drag_data,
    blink::DragOperationsMask drag_operations_mask,
    SkBitmap bitmap,
    gfx::Vector2d cursor_offset_in_dip,
    gfx::Rect drag_obj_rect_in_dip,
    blink::mojom::DragEventSourceInfoPtr event_info) {
  RenderWidgetHostViewBase* root_view = GetRootView();
  CHECK(root_view);
  root_view->RequestInputBackForDragAndDrop(
      std::move(source_document), std::move(drag_data), drag_operations_mask,
      std::move(bitmap), std::move(cursor_offset_in_dip),
      std::move(drag_obj_rect_in_dip), std::move(event_info));
}

void RenderWidgetHostViewChildFrame::ReportScrollJankStats(
    uint32_t total_frames,
    uint32_t janky_frames) {
  if (auto* root_view = GetRootView()) {
    root_view->ReportScrollJankStats(total_frames, janky_frames);
  }
}
#endif

RenderWidgetHostViewBase* RenderWidgetHostViewChildFrame::GetRootView() {
  return frame_connector_ ? frame_connector_->GetRootRenderWidgetHostView()
                          : nullptr;
}

#if BUILDFLAG(IS_WIN)
bool RenderWidgetHostViewChildFrame::ShouldInitiateStylusWriting() {
  auto* root = GetRootView();
  return root ? root->ShouldInitiateStylusWriting()
              : RenderWidgetHostViewBase::ShouldInitiateStylusWriting();
}

void RenderWidgetHostViewChildFrame::OnStartStylusWriting() {
  auto* root = GetRootView();
  if (!root) {
    return;
  }
  root->StartStylusWritingFromChildHostView(
      this, base::BindRepeating(
                &RenderWidgetHostViewChildFrame::OnFocusHandwritingTarget,
                weak_factory_.GetWeakPtr()));
}

void RenderWidgetHostViewChildFrame::OnEditElementFocusedForStylusWriting(
    blink::mojom::StylusWritingFocusResultPtr focus_result) {
  auto* root = GetRootView();
  if (!root) {
    if (StylusHandwritingControllerWin::GetInstance()) {
      StylusHandwritingControllerWin::GetInstance()->OnFocusFailed();
    }
    return;
  }
  // Transform proximate character bounds from child frame widget space to root
  // view widget space before forwarding.
  if (focus_result && focus_result->proximate_bounds) {
    for (auto& rect : focus_result->proximate_bounds->widget_bounds_in_dips) {
      RenderWidgetHostViewBase::TransformPointAndRectToRootView(this, root,
                                                                nullptr, &rect);
    }
  }
  root->OnEditElementFocusedForStylusWriting(std::move(focus_result));
}

void RenderWidgetHostViewChildFrame::OnFocusHandwritingTarget(
    const gfx::Rect& focus_screen_rect_in_dips,
    const gfx::Size& tolerance_screen_distance_in_dips) {
  // TODO(crbug.com/355578906): Consider `tolerance_screen_distance_in_dips`.
  if (!host()) {
    return;
  }

  // Convert the screen rect to child frame local coordinates by subtracting
  // screen origin.
  gfx::Rect local_rect = focus_screen_rect_in_dips;
  local_rect.Offset(-GetViewBounds().OffsetFromOrigin());
  host()->UpdateElementFocusForStylusWriting(local_rect);
}
#endif  // BUILDFLAG(IS_WIN)

void RenderWidgetHostViewChildFrame::InitAsPopup(
    RenderWidgetHostView* parent_host_view,
    const gfx::Rect& bounds,
    const gfx::Rect& anchor_rect) {
  NOTREACHED();
}

void RenderWidgetHostViewChildFrame::UpdateCursor(const ui::Cursor& cursor) {
  if (frame_connector_)
    frame_connector_->UpdateCursor(cursor);
}

void RenderWidgetHostViewChildFrame::UpdateScreenInfo() {
  if (frame_connector_)
    screen_infos_ = frame_connector_->GetScreenInfos();
}

void RenderWidgetHostViewChildFrame::SendInitialPropertiesIfNeeded() {
  if (initial_properties_sent_ || !frame_connector_)
    return;
  UpdateViewportIntersection(frame_connector_->GetIntersectionState(),
                             std::nullopt);
  SetIsInert();
  UpdateInheritedEffectiveTouchAction();
  UpdateRenderThrottlingStatus();
  initial_properties_sent_ = true;
}

void RenderWidgetHostViewChildFrame::SetIsLoading(bool is_loading) {
  // It is valid for an inner WebContents's SetIsLoading() to end up here.
  // This is because an inner WebContents's main frame's RenderWidgetHostView
  // is a RenderWidgetHostViewChildFrame. In contrast, when there is no
  // inner/outer WebContents, only subframe's RenderWidgetHostView can be a
  // RenderWidgetHostViewChildFrame which do not get a SetIsLoading() call.
}

void RenderWidgetHostViewChildFrame::RenderProcessGone() {
  if (frame_connector_)
    frame_connector_->RenderProcessGone();
  Destroy();
}

void RenderWidgetHostViewChildFrame::Destroy() {
  host()->render_frame_metadata_provider()->RemoveObserver(this);

  // FrameSinkIds registered with RenderWidgetHostInputEventRouter
  // have already been cleared when RenderWidgetHostViewBase notified its
  // observers of our impending destruction.
  if (frame_connector_) {
    frame_connector_->SetView(nullptr, /*allow_paint_holding=*/false);
    SetFrameConnector(nullptr);
  }

  // We notify our observers about shutdown here since we are about to release
  // host_ and do not want any event calls coming from
  // RenderWidgetHostInputEventRouter afterwards.
  NotifyObserversAboutShutdown();

  RenderWidgetHostViewBase::Destroy();

  delete this;
}

void RenderWidgetHostViewChildFrame::UpdateTooltipUnderCursor(
    const std::u16string& tooltip_text) {
  if (!frame_connector_)
    return;

  auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
  if (!root_view)
    return;

  auto* cursor_manager = root_view->GetCursorManager();
  // If there's no CursorManager then we're on Android, and setting tooltips
  // is a null-opt there, so it's ok to early out.
  if (!cursor_manager)
    return;

  if (cursor_manager->IsViewUnderCursor(this))
    root_view->UpdateTooltip(tooltip_text);
}

void RenderWidgetHostViewChildFrame::UpdateTooltipFromKeyboard(
    const std::u16string& tooltip_text,
    const gfx::Rect& bounds) {
  if (!frame_connector_)
    return;

  auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
  if (!root_view)
    return;

  // TODO(bebeaudr): Keyboard-triggered tooltips are not positioned correctly
  // when set for an element in an OOPIF. See https://crbug.com/1210269.
  gfx::Rect adjusted_bounds(TransformPointToRootCoordSpace(bounds.origin()),
                            bounds.size());
  root_view->UpdateTooltipFromKeyboard(tooltip_text, adjusted_bounds);
}

void RenderWidgetHostViewChildFrame::ClearKeyboardTriggeredTooltip() {
  if (!frame_connector_)
    return;

  auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
  if (!root_view)
    return;

  root_view->ClearKeyboardTriggeredTooltip();
}

RenderWidgetHostViewBase* RenderWidgetHostViewChildFrame::GetParentViewInput() {
  if (!frame_connector_)
    return nullptr;
  return frame_connector_->GetParentRenderWidgetHostView();
}

void RenderWidgetHostViewChildFrame::RegisterFrameSinkId() {
  UpdateFrameSinkIdRegistration();
}

void RenderWidgetHostViewChildFrame::UnregisterFrameSinkId() {
  CHECK(host(), base::NotFatalUntil::M152);
  if (host()->delegate() && host()->delegate()->GetInputEventRouter()) {
    host()->delegate()->GetInputEventRouter()->RemoveFrameSinkIdOwner(
        frame_sink_id_);
  }
  DetachFromTouchSelectionClientManagerIfNecessary();
}

void RenderWidgetHostViewChildFrame::UpdateViewportIntersection(
    const blink::mojom::ViewportIntersectionState& intersection_state,
    const std::optional<blink::VisualProperties>& visual_properties) {
  if (host()) {
    host()->SetIntersectsViewport(
        !intersection_state.viewport_intersection.IsEmpty());

    // Do not send |visual_properties| to main frames.
    CHECK(!visual_properties.has_value() || !host()->owner_delegate(),
          base::NotFatalUntil::M152);

    bool is_fenced_frame = host()->frame_tree()->is_fenced_frame();
    if (!host()->owner_delegate() || is_fenced_frame) {
      host()->GetAssociatedFrameWidget()->SetViewportIntersection(
          intersection_state.Clone(), visual_properties);
    }
  }
}

void RenderWidgetHostViewChildFrame::SetIsInert() {
  // Do not send inert to main frames.
  if (host() && frame_connector_ && !host()->owner_delegate()) {
    host_->GetAssociatedFrameWidget()->SetIsInertForSubFrame(
        frame_connector_->IsInert());
  }
}

void RenderWidgetHostViewChildFrame::UpdateInheritedEffectiveTouchAction() {
  // Do not send inherited touch action to main frames.
  if (host_ && frame_connector_ && !host()->owner_delegate()) {
    host_->GetAssociatedFrameWidget()
        ->SetInheritedEffectiveTouchActionForSubFrame(
            frame_connector_->InheritedEffectiveTouchAction());
  }
}

void RenderWidgetHostViewChildFrame::UpdateRenderThrottlingStatus() {
  // Send throttling status to subframes and embedded main frames except fenced
  // frames.
  if (host() && frame_connector_ && !host()->frame_tree()->is_fenced_frame()) {
    host_->GetAssociatedFrameWidget()->UpdateRenderThrottlingStatusForSubFrame(
        frame_connector_->IsThrottled(), frame_connector_->IsSubtreeThrottled(),
        frame_connector_->IsDisplayLocked());
  }
}

void RenderWidgetHostViewChildFrame::StopFlingingIfNecessary(
    const blink::WebGestureEvent& event,
    blink::mojom::InputEventResultState ack_result) {
  input_helper_->StopFlingingIfNecessary(event, ack_result);
}

void RenderWidgetHostViewChildFrame::GestureEventAck(
    const blink::WebGestureEvent& event,
    blink::mojom::InputEventResultSource ack_source,
    blink::mojom::InputEventResultState ack_result) {
  TRACE_EVENT1("input", "RenderWidgetHostViewChildFrame::GestureEventAck",
               "type", blink::WebInputEvent::GetName(event.GetType()));

#if !BUILDFLAG(IS_ANDROID)
  HandleSwipeToMoveCursorGestureAck(event);
#endif
  input_helper_->GestureEventAckHelper(event, ack_source, ack_result);
}

void RenderWidgetHostViewChildFrame::OnUnconfirmedTapConvertedToTap() {
  auto* root_view = GetRootView();
  if (!root_view) {
    return;
  }
  root_view->OnUnconfirmedTapConvertedToTap();
}

void RenderWidgetHostViewChildFrame::ForwardTouchpadZoomEventIfNecessary(
    const blink::WebGestureEvent& event,
    blink::mojom::InputEventResultState ack_result) {
  input_helper_->ForwardTouchpadZoomEventIfNecessary(event, ack_result);
}

void RenderWidgetHostViewChildFrame::SetParentFrameSinkId(
    const viz::FrameSinkId& parent_frame_sink_id) {
  if (parent_frame_sink_id_ == parent_frame_sink_id)
    return;

  auto* host_frame_sink_manager = GetHostFrameSinkManager();

  // Unregister hierarchy for the current parent, only if set and registered.
  if (parent_frame_sink_id_.is_valid() &&
      has_frame_sink_hierarchy_registered_) {
    if (host_frame_sink_manager) {
      host_frame_sink_manager->UnregisterFrameSinkHierarchy(
          parent_frame_sink_id_, frame_sink_id_);
    }
    has_frame_sink_hierarchy_registered_ = false;
  }

  parent_frame_sink_id_ = parent_frame_sink_id;

  // Register hierarchy for the new parent, only if set.
  if (parent_frame_sink_id_.is_valid() && host_frame_sink_manager) {
    has_frame_sink_hierarchy_registered_ =
        host_frame_sink_manager->RegisterFrameSinkHierarchy(
            parent_frame_sink_id_, frame_sink_id_);
  }
}

void RenderWidgetHostViewChildFrame::FirstSurfaceActivation(
    const viz::SurfaceInfo& surface_info) {
  if (frame_connector_)
    frame_connector_->FirstSurfaceActivation(surface_info);
}

void RenderWidgetHostViewChildFrame::TransformPointToRootSurface(
    gfx::PointF* point) {
  input_helper_->TransformPointToRootSurface(point);
}

gfx::Rect RenderWidgetHostViewChildFrame::GetBoundsInScreen() {
  gfx::Rect rect;
  if (frame_connector_) {
    RenderWidgetHostViewBase* root_view =
        frame_connector_->GetRootRenderWidgetHostView();

    // The root_view can be null in tests when using a TestWebContents.
    if (root_view)
      rect = root_view->GetBoundsInScreen();
  }
  return rect;
}

gfx::Rect RenderWidgetHostViewChildFrame::GetBoundsInScreenWithoutTransform() {
  gfx::Rect rect;
  if (frame_connector_) {
    RenderWidgetHostViewBase* root_view =
        frame_connector_->GetRootRenderWidgetHostView();

    // The root_view can be null in tests when using a TestWebContents.
    if (root_view) {
      rect = root_view->GetBoundsInScreenWithoutTransform();
    }
  }
  return rect;
}

void RenderWidgetHostViewChildFrame::DidStopFlinging() {
  if (selection_controller_client_)
    selection_controller_client_->DidStopFlinging();
}

blink::mojom::PointerLockResult RenderWidgetHostViewChildFrame::LockPointer(
    bool request_unadjusted_movement) {
  if (frame_connector_)
    return frame_connector_->LockPointer(request_unadjusted_movement);
  return blink::mojom::PointerLockResult::kWrongDocument;
}

blink::mojom::PointerLockResult
RenderWidgetHostViewChildFrame::ChangePointerLock(
    bool request_unadjusted_movement) {
  if (frame_connector_)
    return frame_connector_->ChangePointerLock(request_unadjusted_movement);
  return blink::mojom::PointerLockResult::kWrongDocument;
}

void RenderWidgetHostViewChildFrame::UnlockPointer() {
  if (host()->delegate() && host()->delegate()->HasPointerLock(host()) &&
      frame_connector_) {
    frame_connector_->UnlockPointer();
  }
}

bool RenderWidgetHostViewChildFrame::IsPointerLocked() {
  if (!host()->delegate())
    return false;

  return host()->delegate()->HasPointerLock(host());
}

const viz::FrameSinkId& RenderWidgetHostViewChildFrame::GetFrameSinkId() const {
  return frame_sink_id_;
}

const viz::LocalSurfaceId&

RenderWidgetHostViewChildFrame::GetLocalSurfaceId() const {
  if (frame_connector_)

    return frame_connector_->GetLocalSurfaceId();

  return viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId();
}

void RenderWidgetHostViewChildFrame::NotifyHitTestRegionUpdated(
    const viz::AggregatedHitTestRegion& region) {
  if (selection_controller_client_) {
    selection_controller_client_->OnHitTestRegionUpdated();
  }
  input_helper_->NotifyHitTestRegionUpdated(region);
}

bool RenderWidgetHostViewChildFrame::ScreenRectIsUnstableFor(
    const blink::WebInputEvent& event) {
  return input_helper_->ScreenRectIsUnstableFor(event);
}

bool RenderWidgetHostViewChildFrame::ScreenRectIsUnstableForIOv2For(
    const blink::WebInputEvent& event) {
  return input_helper_->ScreenRectIsUnstableForIOv2For(event);
}

void RenderWidgetHostViewChildFrame::PreProcessTouchEvent(
    const blink::WebTouchEvent& event) {
  if (event.GetType() != blink::WebInputEvent::Type::kTouchStart) {
    return;
  }

  if (!frame_connector_) {
    return;
  }

  FrameConnector::RootViewFocusState state = frame_connector_->HasFocus();
#if BUILDFLAG(IS_ANDROID)
  UMA_HISTOGRAM_ENUMERATION(
      "Android.FocusChanged.RenderWidgetHostViewChildFrame.RootViewFocusState",
      state);
#endif

  if (state == FrameConnector::RootViewFocusState::kNotFocused) {
    Focus();
  }
}

viz::FrameSinkId RenderWidgetHostViewChildFrame::GetRootFrameSinkId() {
  if (frame_connector_) {
    RenderWidgetHostViewBase* root_view =
        frame_connector_->GetRootRenderWidgetHostView();

    // The root_view can be null in tests when using a TestWebContents.
    if (root_view)
      return root_view->GetRootFrameSinkId();
  }
  return viz::FrameSinkId();
}

viz::SurfaceId RenderWidgetHostViewChildFrame::GetCurrentSurfaceId() const {
  return viz::SurfaceId(frame_sink_id_, GetLocalSurfaceId());
}

bool RenderWidgetHostViewChildFrame::HasSavedCompositorFrame() const {
  return GetLocalSurfaceId().is_valid();
}

bool RenderWidgetHostViewChildFrame::HasSize() const {
  return frame_connector_ && frame_connector_->HasSize();
}

double RenderWidgetHostViewChildFrame::GetCSSZoomFactor() const {
  return frame_connector_ ? frame_connector_->GetCssZoomFactor() : 1.0;
}

gfx::PointF RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpaceF(
    const gfx::PointF& point) const {
  return input_helper_->TransformPointToRootCoordSpace(point);
}

bool RenderWidgetHostViewChildFrame::TransformPointToCoordSpaceForView(
    const gfx::PointF& point,
    input::RenderWidgetHostViewInput* target_view,
    gfx::PointF* transformed_point) {
  return input_helper_->TransformPointToCoordSpaceForView(point, target_view,
                                                          transformed_point);
}

gfx::PointF RenderWidgetHostViewChildFrame::TransformRootPointToViewCoordSpace(
    const gfx::PointF& point) {
  return input_helper_->TransformRootPointToViewCoordSpace(point);
}

bool RenderWidgetHostViewChildFrame::IsRenderWidgetHostViewChildFrame() const {
  return true;
}

void RenderWidgetHostViewChildFrame::
    InvalidateLocalSurfaceIdAndAllocationGroup() {
  // Child frames do not manage their own LocalSurfaceId or allocation groups in
  // the browser process.
}

#if BUILDFLAG(IS_MAC)
void RenderWidgetHostViewChildFrame::SetActive(bool active) {}

void RenderWidgetHostViewChildFrame::ShowDefinitionForSelection() {
  if (frame_connector_) {
    frame_connector_->GetRootRenderWidgetHostView()
        ->ShowDefinitionForSelection();
  }
}

void RenderWidgetHostViewChildFrame::SpeakSelection() {}

void RenderWidgetHostViewChildFrame::SetWindowFrameInScreen(
    const gfx::Rect& rect) {}

void RenderWidgetHostViewChildFrame::ShowSharePicker(
    const std::string& title,
    const std::string& text,
    const GURL& url,
    const std::vector<std::string>& file_paths,
    blink::mojom::ShareService::ShareCallback callback) {
  if (GetRootRenderWidgetHostView()) {
    GetRootRenderWidgetHostView()->ShowSharePicker(title, text, url, file_paths,
                                                   std::move(callback));
  }
}

uint64_t RenderWidgetHostViewChildFrame::GetNSViewId() const {
  return 0;
}

#endif  // BUILDFLAG(IS_MAC)

void RenderWidgetHostViewChildFrame::CopyFromSurface(
    const gfx::Rect& src_subrect,
    const gfx::Size& output_size,
    base::TimeDelta timeout,
    base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback) {
  if (!IsSurfaceAvailableForCopy()) {
    std::move(callback).Run(base::unexpected<CopyFromSurfaceError>(
        CopyFromSurfaceError::kNotImplemented));
    return;
  }

  std::unique_ptr<viz::CopyOutputRequest> request =
      std::make_unique<viz::CopyOutputRequest>(
          viz::CopyOutputRequest::ResultFormat::RGBA,
          viz::CopyOutputRequest::ResultDestination::kSystemMemory,
          base::BindOnce(
              [](base::OnceCallback<void(const content::CopyFromSurfaceResult&)>
                     callback,
                 std::unique_ptr<viz::CopyOutputResult> result) {
                std::move(callback).Run(ToCopyFromSurfaceResult(
                    result->ScopedAccessSkBitmap()
                        .GetOutScopedBitmapAndMetadata()));
              },
              std::move(callback)));

  // Run result callback on the current thread in case `callback` needs to run
  // on the current thread. See http://crbug.com/1431363.
  request->set_result_task_runner(
      base::SingleThreadTaskRunner::GetCurrentDefault());

  if (src_subrect.IsEmpty()) {
    request->set_area(gfx::Rect(GetCompositorViewportPixelSize()));
  } else {
    // |src_subrect| is in DIP coordinates; convert to Surface coordinates.
    request->set_area(
        gfx::ScaleToRoundedRect(src_subrect, GetDeviceScaleFactor()));
  }

  if (!output_size.IsEmpty()) {
    if (request->area().IsEmpty()) {
      // Viz would normally return an empty result for an empty source area.
      // However, this guard here is still necessary to protect against setting
      // an illegal scaling ratio.
      return;
    }
    request->set_result_selection(gfx::Rect(output_size));
    request->SetScaleRatio(
        gfx::Vector2d(request->area().width(), request->area().height()),
        gfx::Vector2d(output_size.width(), output_size.height()));
  }

  GetHostFrameSinkManager()->RequestCopyOfOutput(
      GetCurrentSurfaceId(), std::move(request),
      /*capture_exact_surface_id=*/false, timeout);
}

#if BUILDFLAG(IS_ANDROID)
void RenderWidgetHostViewChildFrame::OnReportScrollJankStats(
    uint32_t total_frames,
    uint32_t janky_frames) {
  ReportScrollJankStats(total_frames, janky_frames);
}
#endif

void RenderWidgetHostViewChildFrame::OnFirstSurfaceActivation(
    const viz::SurfaceInfo& surface_info) {}

void RenderWidgetHostViewChildFrame::OnFrameTokenChanged(
    uint32_t frame_token,
    base::TimeTicks activation_time) {
  OnFrameTokenChangedForView(frame_token, activation_time);
}

TouchSelectionControllerClientManager*
RenderWidgetHostViewChildFrame::GetTouchSelectionControllerClientManager() {
  if (!frame_connector_)
    return nullptr;
  auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
  if (!root_view)
    return nullptr;

  // There is only ever one manager, and it's owned by the root view.
  return root_view->GetTouchSelectionControllerClientManager();
}

void RenderWidgetHostViewChildFrame::
    OnRenderFrameMetadataChangedAfterActivation(
        base::TimeTicks activation_time) {
  if (selection_controller_client_) {
    const cc::RenderFrameMetadata& metadata =
        host()->render_frame_metadata_provider()->LastRenderFrameMetadata();
    selection_controller_client_->UpdateSelectionBoundsIfNeeded(
        metadata.selection, GetDeviceScaleFactor());
  }
}

void RenderWidgetHostViewChildFrame::TakeFallbackContentFrom(
    RenderWidgetHostView* view) {
  // This method only makes sense for top-level views.
}

blink::mojom::InputEventResultState
RenderWidgetHostViewChildFrame::FilterInputEvent(
    const blink::WebInputEvent& input_event) {
  return input_helper_->FilterInputEvent(input_event);
}

void RenderWidgetHostViewChildFrame::EnableAutoResize(
    const gfx::Size& min_size,
    const gfx::Size& max_size) {
  if (frame_connector_)
    frame_connector_->EnableAutoResize(min_size, max_size);
}

void RenderWidgetHostViewChildFrame::DisableAutoResize(
    const gfx::Size& new_size) {
  // For child frames, the size comes from the parent when auto-resize is
  // disabled so we ignore |new_size| here.
  if (frame_connector_)
    frame_connector_->DisableAutoResize();
}

viz::ScopedSurfaceIdAllocator
RenderWidgetHostViewChildFrame::DidUpdateVisualProperties(
    const cc::RenderFrameMetadata& metadata) {
  base::OnceCallback<void()> allocation_task = base::BindOnce(
      base::IgnoreResult(
          &RenderWidgetHostViewChildFrame::OnDidUpdateVisualPropertiesComplete),
      weak_factory_.GetWeakPtr(), metadata);
  return viz::ScopedSurfaceIdAllocator(std::move(allocation_task));
}

input::CursorManager* RenderWidgetHostViewChildFrame::GetCursorManager() {
  if (!GetRootView()) {
    return nullptr;
  }
  return GetRootView()->GetCursorManager();
}

ui::TextInputType RenderWidgetHostViewChildFrame::GetTextInputType() const {
  if (!text_input_manager_)
    return ui::TEXT_INPUT_TYPE_NONE;

  if (text_input_manager_->GetTextInputState())
    return text_input_manager_->GetTextInputState()->type;
  return ui::TEXT_INPUT_TYPE_NONE;
}

bool RenderWidgetHostViewChildFrame::GetTextRange(gfx::Range* range) const {
  if (!text_input_manager_ || !GetFocusedWidget()) {
    return false;
  }

  const ui::mojom::TextInputState* state =
      text_input_manager_->GetTextInputState();
  if (!state) {
    return false;
  }

  range->set_start(0);
  range->set_end(state->value ? state->value->length() : 0);
  return true;
}

RenderWidgetHostViewBase*
RenderWidgetHostViewChildFrame::GetRootRenderWidgetHostView() const {
  return frame_connector_ ? frame_connector_->GetRootRenderWidgetHostView()
                          : nullptr;
}

bool RenderWidgetHostViewChildFrame::CanBecomeVisible() {
  if (!frame_connector_)
    return true;

  if (frame_connector_->IsHidden())
    return false;

  RenderWidgetHostViewBase* parent_view = GetParentViewInput();
  if (!parent_view || !parent_view->IsRenderWidgetHostViewChildFrame()) {
    // Root frame does not have a CSS visibility property.
    return true;
  }

  return static_cast<RenderWidgetHostViewChildFrame*>(parent_view)
      ->CanBecomeVisible();
}

void RenderWidgetHostViewChildFrame::OnDidUpdateVisualPropertiesComplete(
    const cc::RenderFrameMetadata& metadata) {
  if (frame_connector_)
    frame_connector_->DidUpdateVisualProperties(metadata);
  host()->SynchronizeVisualProperties();
}

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

ui::Compositor* RenderWidgetHostViewChildFrame::GetCompositor() {
  if (!GetRootView())
    return nullptr;
  return GetRootView()->GetCompositor();
}

#if !BUILDFLAG(IS_ANDROID)
void RenderWidgetHostViewChildFrame::HandleSwipeToMoveCursorGestureAck(
    const blink::WebGestureEvent& event) {
  if (!selection_controller_client_) {
    return;
  }

  switch (event.GetType()) {
    case blink::WebInputEvent::Type::kGestureScrollBegin: {
      if (!event.data.scroll_begin.cursor_control) {
        break;
      }
      swipe_to_move_cursor_activated_ = true;
      selection_controller_client_->OnSwipeToMoveCursorBegin();
      break;
    }
    case blink::WebInputEvent::Type::kGestureScrollEnd: {
      if (!swipe_to_move_cursor_activated_) {
        break;
      }
      swipe_to_move_cursor_activated_ = false;
      selection_controller_client_->OnSwipeToMoveCursorEnd();
      break;
    }
    default:
      break;
  }
}
#endif

}  // namespace content
