// 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.

#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <vector>

#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/input/touch_action.h"
#include "components/input/child_frame_input_helper.h"
#include "components/input/event_with_latency_info.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/frame_timing_details_map.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "components/viz/host/host_frame_sink_client.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/content_export.h"
#include "content/public/browser/touch_selection_controller_client_manager.h"
#include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
#include "third_party/blink/public/common/page/content_to_visible_time_request.h"
#include "third_party/blink/public/common/widget/visual_properties.h"
#include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom-forward.h"
#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-forward.h"
#include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_ui_types.h"

#if BUILDFLAG(IS_MAC)
#include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
#endif  // BUILDFLAG(IS_MAC)

namespace ui {
class Compositor;
}

namespace content {
class FrameConnector;
class RenderWidgetHost;
class RenderWidgetHostViewChildFrameTest;
class TouchSelectionControllerClientChildFrame;

// RenderWidgetHostViewChildFrame implements the view for a RenderWidgetHost
// associated with content being rendered in a separate process from
// content that is embedding it. This is not a platform-specific class; rather,
// the embedding renderer process implements the platform containing the
// widget, and the top-level frame's RenderWidgetHostView will ultimately
// manage all native widget interaction.
//
// See comments in render_widget_host_view.h about this class and its members.
class CONTENT_EXPORT RenderWidgetHostViewChildFrame
    : public RenderWidgetHostViewBase,
      public TouchSelectionControllerClientManager::Observer,
      public RenderFrameMetadataProvider::Observer,
      public viz::HostFrameSinkClient {
 public:
  // TODO(crbug.com/40170974): Pass multi-screen info from the parent.
  static RenderWidgetHostViewChildFrame* Create(
      RenderWidgetHost* widget,
      const display::ScreenInfos& parent_screen_infos);

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

  void SetFrameConnector(FrameConnector* frame_connector);

  // TouchSelectionControllerClientManager::Observer implementation.
  void OnManagerWillDestroy(
      TouchSelectionControllerClientManager* manager) override;

  // RenderWidgetHostView implementation.
  void InitAsChild(gfx::NativeView parent_view) override;
  void SetSize(const gfx::Size& size) override;
  void SetBounds(const gfx::Rect& rect) override;
  void Focus() override;
  bool HasFocus() override;
  bool IsSurfaceAvailableForCopy() override;
  void CopyFromSurface(
      const gfx::Rect& src_rect,
      const gfx::Size& output_size,
      base::TimeDelta timeout,
      base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback)
      override;
  void Hide() override;
  bool IsShowing() override;
  void WasOccluded() override;
  gfx::Rect GetViewBounds() override;
  gfx::Rect GetViewBoundsWithoutTransform() override;
  gfx::Size GetVisibleViewportSize() override;
  gfx::Size GetVisibleViewportSizeDevicePx() override;
  void SetInsets(const gfx::Insets& insets) override;
  gfx::NativeView GetNativeView() override;
  gfx::NativeViewAccessible GetNativeViewAccessible() override;
  bool IsPointerLocked() override;
  void TakeFallbackContentFrom(RenderWidgetHostView* view) override;
  ui::Compositor* GetCompositor() override;

  // RenderWidgetHostViewBase implementation.
#if BUILDFLAG(IS_ANDROID)
  bool IsTouchSequencePotentiallyActiveOnViz() override;
  void 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) override;
  void ReportScrollJankStats(uint32_t total_frames,
                             uint32_t janky_frames) override;
#endif
  RenderWidgetHostViewBase* GetRootView() override;
#if BUILDFLAG(IS_WIN)
  bool ShouldInitiateStylusWriting() override;
  void OnStartStylusWriting() override;
  void OnEditElementFocusedForStylusWriting(
      blink::mojom::StylusWritingFocusResultPtr focus_result) override;
#endif  // BUILDFLAG(IS_WIN)
  gfx::Size GetCompositorViewportPixelSize() override;
  void InitAsPopup(RenderWidgetHostView* parent_host_view,
                   const gfx::Rect& bounds,
                   const gfx::Rect& anchor_rect) override;
  void UpdateCursor(const ui::Cursor& cursor) override;
  void UpdateScreenInfo() override;
  void SendInitialPropertiesIfNeeded() override;
  void SetIsLoading(bool is_loading) override;
  void RenderProcessGone() override;
  void ShowWithVisibility(PageVisibilityState page_visibility) override;
  void Destroy() override;
  void UpdateTooltipUnderCursor(const std::u16string& tooltip_text) override;
  void UpdateTooltipFromKeyboard(const std::u16string& tooltip_text,
                                 const gfx::Rect& bounds) override;
  void ClearKeyboardTriggeredTooltip() override;
  void GestureEventAck(const blink::WebGestureEvent& event,
                       blink::mojom::InputEventResultSource ack_source,
                       blink::mojom::InputEventResultState ack_result) override;
  // Since the URL of content rendered by this class is not displayed in
  // the URL bar, this method does not need an implementation.
  void ResetFallbackToFirstNavigationSurface() override {}
  void OnUnconfirmedTapConvertedToTap() override;

  void TransformPointToRootSurface(gfx::PointF* point) override;
  gfx::Rect GetBoundsInScreen() override;
  gfx::Rect GetBoundsInScreenWithoutTransform() override;
  void DidStopFlinging() override;
  blink::mojom::PointerLockResult LockPointer(
      bool request_unadjusted_movement) override;
  blink::mojom::PointerLockResult ChangePointerLock(
      bool request_unadjusted_movement) override;
  void UnlockPointer() override;
  const viz::FrameSinkId& GetFrameSinkId() const override;
  const viz::LocalSurfaceId& GetLocalSurfaceId() const override;
  void NotifyHitTestRegionUpdated(const viz::AggregatedHitTestRegion&) override;
  bool ScreenRectIsUnstableFor(const blink::WebInputEvent& event) override;
  bool ScreenRectIsUnstableForIOv2For(
      const blink::WebInputEvent& event) override;
  void PreProcessTouchEvent(const blink::WebTouchEvent& event) override;
  viz::FrameSinkId GetRootFrameSinkId() override;
  viz::SurfaceId GetCurrentSurfaceId() const override;
  bool HasSavedCompositorFrame() const override;
  bool HasSize() const override;
  double GetCSSZoomFactor() const override;
  gfx::PointF TransformPointToRootCoordSpaceF(
      const gfx::PointF& point) const override;
  bool TransformPointToCoordSpaceForView(
      const gfx::PointF& point,
      input::RenderWidgetHostViewInput* target_view,
      gfx::PointF* transformed_point) override;
  void DidNavigate() override;
  gfx::PointF TransformRootPointToViewCoordSpace(
      const gfx::PointF& point) override;
  TouchSelectionControllerClientManager*
  GetTouchSelectionControllerClientManager() override;
  void UpdateIntrinsicSizingInfo(
      blink::mojom::IntrinsicSizingInfoPtr sizing_info) override;
  std::unique_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
      override;
  bool IsRenderWidgetHostViewChildFrame() const override;
  void InvalidateLocalSurfaceIdAndAllocationGroup() override;

#if BUILDFLAG(IS_MAC)
  // RenderWidgetHostView implementation.
  void SetActive(bool active) override;
  void ShowDefinitionForSelection() override;
  void SpeakSelection() override;
  void SetWindowFrameInScreen(const gfx::Rect& rect) override;
  void ShowSharePicker(
      const std::string& title,
      const std::string& text,
      const GURL& url,
      const std::vector<std::string>& file_paths,
      blink::mojom::ShareService::ShareCallback callback) override;
  uint64_t GetNSViewId() const override;
#endif  // BUILDFLAG(IS_MAC)

  blink::mojom::InputEventResultState FilterInputEvent(
      const blink::WebInputEvent& input_event) override;
  void EnableAutoResize(const gfx::Size& min_size,
                        const gfx::Size& max_size) override;
  void DisableAutoResize(const gfx::Size& new_size) override;
  viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(
      const cc::RenderFrameMetadata& metadata) override;
  input::CursorManager* GetCursorManager() override;

  // RenderFrameMetadataProvider::Observer implementation.
  void OnRenderFrameMetadataChangedBeforeActivation(
      const cc::RenderFrameMetadata& metadata) override {}
  void OnRenderFrameMetadataChangedAfterActivation(
      base::TimeTicks activation_time) override;
  void OnRenderFrameSubmission() override {}
  void OnLocalSurfaceIdChanged(
      const cc::RenderFrameMetadata& metadata) override {}
#if BUILDFLAG(IS_ANDROID)
  void OnReportScrollJankStats(uint32_t total_frames,
                               uint32_t janky_frames) override;
#endif

  // viz::HostFrameSinkClient implementation.
  void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
  void OnFrameTokenChanged(uint32_t frame_token,
                           base::TimeTicks activation_time) override;

  FrameConnector* FrameConnectorForTesting() const { return frame_connector_; }

  RenderWidgetHostViewBase* GetParentViewInput() override;

  void RegisterFrameSinkId();
  void UnregisterFrameSinkId();

  void UpdateViewportIntersection(
      const blink::mojom::ViewportIntersectionState& intersection_state,
      const std::optional<blink::VisualProperties>& visual_properties);

  // TODO(sunxd): Rename SetIsInert to UpdateIsInert.
  void SetIsInert();
  void UpdateInheritedEffectiveTouchAction();

  void UpdateRenderThrottlingStatus();

  ui::TextInputType GetTextInputType() const;

  // Retrieves the UTF-16 code unit range containing accessible text in the
  // view. Returns false if the information cannot be retrieved right now.
  bool GetTextRange(gfx::Range* range) const;

  RenderWidgetHostViewBase* GetRootRenderWidgetHostView() const;

  // Shows the view.
  void Show();

 protected:
  friend class RenderWidgetHostView;
  friend class RenderWidgetHostViewChildFrameTest;
  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewChildFrameTest,
                           ForwardsBeginFrameAcks);

  RenderWidgetHostViewChildFrame(
      RenderWidgetHost* widget,
      const display::ScreenInfos& parent_screen_infos);
  void Init();

  // Sets |parent_frame_sink_id_| and registers frame sink hierarchy. If the
  // parent was already set then it also unregisters hierarchy.
  void SetParentFrameSinkId(const viz::FrameSinkId& parent_frame_sink_id);

  // Clears current compositor surface, if one is in use.
  void ClearCompositorSurfaceIfNecessary();

  void ProcessFrameSwappedCallbacks();

  // RenderWidgetHostViewBase:
  void UpdateFrameSinkIdRegistration() override;
  void UpdateBackgroundColor() override;
  std::optional<DisplayFeature> GetDisplayFeature() override;
  void DisableDisplayFeatureOverrideForEmulation() override;
  void OverrideDisplayFeatureForEmulation(
      const DisplayFeature* display_feature) override;
  void NotifyHostAndDelegateOnWasShown(
      std::optional<blink::RecordContentToVisibleTimeRequest>) final;
  void RequestSuccessfulPresentationTimeFromHostOrDelegate(
      blink::RecordContentToVisibleTimeRequest) final;
  void CancelSuccessfulPresentationTimeRequestForHostAndDelegate() final;

  void StopFlingingIfNecessary(
      const blink::WebGestureEvent& event,
      blink::mojom::InputEventResultState ack_result) override;

  // The ID for FrameSink associated with this view.
  viz::FrameSinkId frame_sink_id_;

  // Surface-related state.
  viz::SurfaceInfo last_activated_surface_info_;
  gfx::Rect last_screen_rect_;

  // frame_connector_ provides a platform abstraction. Messages
  // sent through it are routed to the embedding renderer process.
  raw_ptr<FrameConnector> frame_connector_;

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

  void SetInputHelperForTesting(
      std::unique_ptr<input::ChildFrameInputHelper> input_helper) {
    input_helper_ = std::move(input_helper);
  }

 protected:
  ~RenderWidgetHostViewChildFrame() override;

 private:
  FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                           HiddenOOPIFWillNotGenerateCompositorFrames);
  FRIEND_TEST_ALL_PREFIXES(
      SitePerProcessBrowserTest,
      HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation);
  FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                           SubframeVisibleAfterRenderViewBecomesSwappedOut);
  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostInputEventRouterTest,
                           BubbleScrollEventNullSafetyDuringTeardown);
  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostInputEventRouterTest,
                           FilteredGestureDoesntInterruptBubbling);
  friend class RenderWidgetHostViewChildFrameTest;

  virtual void FirstSurfaceActivation(const viz::SurfaceInfo& surface_info);

  void DetachFromTouchSelectionClientManagerIfNecessary();

  gfx::Rect GetViewBoundsHelper(bool without_transform);

  // Returns false if the view cannot be shown. This is the case where the frame
  // associated with this view or a cross process ancestor frame has been hidden
  // using CSS.
  bool CanBecomeVisible();

  void OnDidUpdateVisualPropertiesComplete(
      const cc::RenderFrameMetadata& metadata);

  void ForwardTouchpadZoomEventIfNecessary(
      const blink::WebGestureEvent& event,
      blink::mojom::InputEventResultState ack_result) override;

  // TODO(crbug.com/375388841): Remove these once Aura also uses
  // TouchSelectionControllerInputObserver. These are not needed on Android
  // since it uses TouchSelectionControllerInputObserver.
#if !BUILDFLAG(IS_ANDROID)
  // Performs gesture ack handling needed for swipe-to-move-cursor gestures.
  void HandleSwipeToMoveCursorGestureAck(const blink::WebGestureEvent& event);

  // Whether a swipe-to-move-cursor gesture is activated.
  bool swipe_to_move_cursor_activated_ = false;
#endif

  std::vector<base::OnceClosure> frame_swapped_callbacks_;

  std::unique_ptr<input::ChildFrameInputHelper> input_helper_;

  // The surface client ID of the parent RenderWidgetHostView.  0 if none.
  viz::FrameSinkId parent_frame_sink_id_;

  // True if there is an active frame sink hierarchy registration for
  // `parent_frame_sink_id_` and `frame_sink_id_`.
  bool has_frame_sink_hierarchy_registered_ = false;

  gfx::Insets insets_;

  std::unique_ptr<TouchSelectionControllerClientChildFrame>
      selection_controller_client_;

  // Weak pointer to the view which owns the
  // TouchSelectionControllerClientManager that this object is registered with.
  base::WeakPtr<RenderWidgetHostViewBase>
      view_for_touch_selection_client_manager_;

  // If a new RWHVCF is created for a cross-origin navigation, the parent
  // will typically not notice and will not transmit a full complement of
  // properties.
  bool initial_properties_sent_ = false;

#if BUILDFLAG(IS_WIN)
  // Callback registered with the root view for handling the TSF
  // FocusHandwritingTarget response. Converts the screen rect to child frame
  // local coordinates and forwards to this view's host.
  void OnFocusHandwritingTarget(
      const gfx::Rect& focus_screen_rect_in_dips,
      const gfx::Size& tolerance_screen_distance_in_dips);
#endif  // BUILDFLAG(IS_WIN)

  // A queue for `IntrinsicSizingInfo` sent from the child renderer before the
  // frame connector is set.
  blink::mojom::IntrinsicSizingInfoPtr pending_sizing_info_;

  base::WeakPtrFactory<RenderWidgetHostViewChildFrame> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_CHILD_FRAME_H_
