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

#ifndef CC_INPUT_INPUT_HANDLER_H_
#define CC_INPUT_INPUT_HANDLER_H_

#include <memory>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/time/time.h"
#include "base/types/optional_ref.h"
#include "cc/cc_export.h"
#include "cc/input/browser_controls_offset_tag_modifications.h"
#include "cc/input/browser_controls_state.h"
#include "cc/input/compositor_input_interfaces.h"
#include "cc/input/event_listener_properties.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/overscroll_behavior.h"
#include "cc/input/scroll_state.h"
#include "cc/input/scrollbar.h"
#include "cc/input/touch_action.h"
#include "cc/metrics/events_metrics_manager.h"
#include "cc/metrics/frame_sequence_metrics.h"
#include "cc/paint/element_id.h"
#include "cc/trees/scroll_source_type.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "ui/events/types/scroll_input_type.h"
#include "ui/events/types/scroll_types.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d_f.h"

namespace gfx {
class Point;
class SizeF;
}  // namespace gfx

namespace ui {
class LatencyInfo;
}  // namespace ui

namespace cc {

class CompositorDelegateForInput;
class LatencyInfoSwapPromiseMonitor;
class LayerImpl;
class ScrollbarController;
class ScrollElasticityHelper;
class Viewport;
class InputHandlerUtils;

enum class PointerResultType { kUnhandled = 0, kScrollbarScroll };

// These enum values are reported in UMA. So these values should never be
// removed or changed.
enum class ScrollBeginThreadState {
  kScrollingOnCompositor = 0,
  kScrollingOnCompositorBlockedOnMain = 1,
  kScrollingOnMain = 2,
  kRasterInducingScroll = 3,
  kRasterInducingScrollBlockedOnMain = 4,
  kMaxValue = kRasterInducingScrollBlockedOnMain,
};

struct CC_EXPORT InputHandlerPointerResult {
  InputHandlerPointerResult() = default;
  // Tells what type of processing occurred in the input handler as a result of
  // the pointer event.
  PointerResultType type = PointerResultType::kUnhandled;

  // Tells what scroll_units should be used.
  ui::ScrollGranularity scroll_units =
      ui::ScrollGranularity::kScrollByPrecisePixel;

  // If the input handler processed the event as a scrollbar scroll, it will
  // return a gfx::Vector2dF that produces the necessary scroll. However,
  // it is still the client's responsibility to generate the gesture scrolls
  // instead of the input handler performing it as a part of handling the
  // pointer event (due to the latency attribution that happens at the
  // InputHandlerProxy level).
  gfx::Vector2dF scroll_delta;

  // Used to determine which scroll_node needs to be scrolled. The primary
  // purpose of this is to avoid hit testing for gestures that already know
  // which scroller to target.
  ElementId target_scroller;
};

struct CC_EXPORT InputHandlerScrollResult {
  InputHandlerScrollResult() = default;
  // Did any layer scroll as a result this ScrollUpdate call?
  bool did_scroll = false;
  // Was any of the scroll delta argument to this ScrollUpdate call not used?
  bool did_overscroll_root = false;
  // The total overscroll that has been accumulated by all ScrollUpdate calls
  // that have had overscroll since the last ScrollBegin call. This resets upon
  // a ScrollUpdate with no overscroll.
  gfx::Vector2dF accumulated_root_overscroll;
  // The amount of the scroll delta argument to this ScrollUpdate call that was
  // not used for scrolling.
  gfx::Vector2dF unused_scroll_delta;
  // How the browser should handle the overscroll navigation based on the css
  // property scroll-boundary-behavior.
  OverscrollBehavior overscroll_behavior;
  // The current offset of the currently scrolling node. It is in DIP or
  // physical pixels depending on the use-zoom-for-dsf flag. If the currently
  // scrolling node is the viewport, this would be the sum of the scroll offsets
  // of the inner and outer node, representing the visual scroll offset.
  gfx::PointF current_visual_offset;
  // Used only in scroll unification. Tells the caller that we have performed
  // the scroll (i.e. updated the offset in the scroll tree) on the compositor
  // thread, but we will need a main thread lifecycle update + commit before
  // the user will see the new pixels (for example, because the scroller does
  // not have a composited layer).
  bool needs_main_thread_repaint = false;
};

class CC_EXPORT InputHandlerClient {
 public:
  enum class ScrollEventDispatchMode {
    // Scroll events arriving will be enqueued to be dispatched during the next
    // `DeliverInputForBeginFrame`.
    kEnqueueScrollEvents,

    // Scroll events arriving will be dispatched immediately, if
    // `DeliverInputForBeginFrame` was called while scrolling, with no input
    // events in the queue. This will occur until frame production has started,
    // or completed.
    kDispatchScrollEventsImmediately,

    // If there are no queued events when `DeliverInputForBeginFrame` is called,
    // while we are scrolling. We will generate a new prediction, and then
    // dispatch a synthetic `GestureScrollUpdate` using the prediction.
    kUseScrollPredictorForEmptyQueue,

    // Will perform as `kDispatchScrollEventsImmediately` until the deadline.
    // Instead of immediately resuming frame production, we will first attempt
    // to generate a new prediction to dispatch. As in
    // `kUseScrollPredictorForEmptyQueue`. After which we will resume frame
    // production and enqueuing input.
    kUseScrollPredictorForDeadline,

    // Will perform as `kDispatchScrollEventsImmediately` until the deadline.
    // However no input arriving after the deadline will dispatch. Event if
    // frame production has yet to complete.
    kDispatchScrollEventsUntilDeadline,
  };

  InputHandlerClient(const InputHandlerClient&) = delete;
  virtual ~InputHandlerClient() = default;

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

  virtual void WillShutdown() = 0;
  virtual void Animate(base::TimeTicks time) = 0;
  virtual void ReconcileElasticOverscrollAndRootScroll() = 0;
  virtual void SetPrefersReducedMotion(bool prefers_reduced_motion) = 0;
  virtual void UpdateRootLayerStateForSynchronousInputHandler(
      const gfx::PointF& total_scroll_offset,
      const gfx::PointF& max_scroll_offset,
      const gfx::SizeF& scrollable_size,
      float page_scale_factor,
      float min_page_scale_factor,
      float max_page_scale_factor) = 0;
  virtual void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) = 0;
  virtual void DeliverInputForHighLatencyMode() = 0;
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  virtual void WillHandleScrollUpdateForInternalBeginFrame(const viz::BeginFrameArgs& args) {}
#endif // BUILDFLAG(ARKWEB_INPUT_EVENTS)
  virtual void DeliverInputForDeadline() = 0;
  virtual void DidFinishImplFrame() = 0;
  virtual bool HasQueuedInput() const = 0;
  virtual void SetScrollEventDispatchMode(ScrollEventDispatchMode mode,
                                          double scroll_deadline_ratio) = 0;

 protected:
  InputHandlerClient() = default;
};

// The InputHandler is a way for the embedders to interact with the input system
// running on the compositor thread. Each instance of a compositor (i.e. a
// LayerTreeHostImpl) is associated with one InputHandler instance. The
// InputHandler sits in between the embedder (the UI compositor or Blink) and
// the compositor (LayerTreeHostImpl); as such, it must be bound to both.
//
// To use the input handler, instantiate it by passing in the compositor's
// CompositorDelegateForInput to the Create factory method. The compositor
// assumes ownership of the InputHandler and will bind itself. Then, implement
// the InputHandlerClient interface and bind it to the handler by calling
// BindToClient on the input handler. This should all be done on the
// input-handling thread (i.e. the "compositor" thread if one exists).
//
// Many methods are virtual for input_handler_proxy_unittest.cc.
// TODO: consider revising these tests to reduce reliance on mocking.
class CC_EXPORT InputHandler : public InputDelegateForCompositor {
 public:
  friend class InputHandlerUtils;
  // Creates an instance of the InputHandler and binds it to the layer tree
  // delegate. The delegate owns the InputHandler so their lifetimes
  // are tied together, hence, this returns a WeakPtr.
  static base::WeakPtr<InputHandler> Create(
      CompositorDelegateForInput& compositor_delegate);

  // Note these are used in a histogram. Do not reorder or delete existing
  // entries.
  enum class ScrollThread {
    // kScrollOnMainThread is not used anymore. However we'll keep this entry
    // as per the comment above.
    kScrollOnMainThread_NotUsed = 0,
    kScrollOnImplThread,
    kScrollIgnored,
    // kScrollUnknown is not used anymore. However we'll keep this entry as per
    // the comment above.
    kScrollUnknown_NotUsed,
    kLastScrollStatus = kScrollUnknown_NotUsed,
  };

  explicit InputHandler(CompositorDelegateForInput& compositor_delegate);
  ~InputHandler() override;

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

  struct ScrollStatus {
    ScrollThread thread = ScrollThread::kScrollOnImplThread;

    // If nonzero, it tells the caller that the input handler detected a case
    // where it cannot reliably target a scroll node and needs the main thread
    // to perform a hit test. If nonzero, this will be one or more values from
    // MainThreadScrollingReason::kHitTestReasons.
    uint32_t main_thread_hit_test_reasons =
        MainThreadScrollingReason::kNotScrollingOnMain;

    // A nonzero value means we have performed the scroll (i.e. updated the
    // offset in the scroll tree) on the compositor thread, but we will need a
    // main thread lifecycle update + commit before the user will see the new
    // pixels (for example, because the scroller does not have a composited
    // layer). If nonzero, this will be one or more values from the
    // MainThreadScrollingReason::kRepaintReasons.
    uint32_t main_thread_repaint_reasons =
        MainThreadScrollingReason::kNotScrollingOnMain;

    // TODO(crbug.com/40735567): This is a temporary workaround for GuestViews
    // as they create viewport nodes and want to bubble scroll if the
    // viewport cannot scroll in the given delta directions. There should be
    // a parameter to ThreadInputHandler to specify whether unused delta is
    // consumed by the viewport or bubbles to the parent.
    bool viewport_cannot_scroll = false;

    // This bool indicates if this scroll operation is expected to require
    // raster work to be done on worker threads before the scroll tree
    // can be activated.
    bool raster_inducing = false;
  };

  // ViewportScrollResult records, for a scroll gesture affecting a page's
  // viewport:
  // - the amount from the scroll gesture's delta that actually resulted in
  //   scrolling: |consumed_delta|,
  // - the amount from the scroll gesture's delta that applied to the content of
  //   the page, i.e. excluding movement of browser controls.
  // - the distribution of the scroll gesture's delta between the inner and
  //   outer viewports, {inner,outer}_viewport_consumed_delta_
  // TODO(tdresser): eventually |consumed_delta| should equal
  // |content_scrolled_delta|. See crbug.com/510045 for details.
  struct ViewportScrollResult {
    gfx::Vector2dF consumed_delta;
    gfx::Vector2dF content_scrolled_delta;
    gfx::Vector2dF outer_viewport_scrolled_delta;
    gfx::Vector2dF inner_viewport_scrolled_delta;
  };

  enum class TouchStartOrMoveEventListenerType {
    kNoHandler,
    kHandler,
    kHandlerOnScrollingLayer
  };

  // This struct contains the information about a snap animation executed on the
  // impl thread.
  // TODO(crbug.com/374801328): Put other snap-animation-related info into this
  // struct, i.e. updated_snapped_elements_ so InputHandler keeps only one map.
  struct SnapAnimationData {
    // The ids of the elements to which the element associated with this state
    // is snapping to. This is set at the end of a scroll when a snap animation
    // is kicked off and cleared when that animation ends.
    TargetSnapAreaElementIds animating_snap_target_ids_;
  };

  virtual base::WeakPtr<InputHandler> AsWeakPtr();

  // Binds a client to this handler to receive notifications. Only one client
  // can be bound to an InputHandler. The client must live at least until the
  // handler calls WillShutdown() on the client.
  virtual void BindToClient(InputHandlerClient* client);

  // Selects a ScrollNode to be "latched" for scrolling using the
  // |scroll_state| start position. The selected node remains latched until the
  // gesture is ended by a call to ScrollEnd.  Returns SCROLL_STARTED if a node
  // at the coordinates can be scrolled and was latched, SCROLL_ON_MAIN_THREAD
  // if the scroll event should instead be delegated to the main thread, or
  // kScrollUnknown if there is nothing to be scrolled at the given
  // coordinates.
  virtual ScrollStatus ScrollBegin(ScrollState* scroll_state,
                                   ui::ScrollInputType type);

  // Similar to ScrollBegin, except the hit test is skipped and scroll always
  // targets at the root layer.
  virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state,
                                       ui::ScrollInputType type);

  // Scroll the layer selected by |ScrollBegin| by given |scroll_state| delta.
  // Internally, the delta is transformed to local layer's coordinate space for
  // scrolls gestures that are direct manipulation (e.g. touch). If the
  // viewport is latched, and it can no longer scroll, the root overscroll
  // accumulated within this ScrollBegin() scope is reported in the return
  // value's |accumulated_overscroll| field. Should only be called if
  // ScrollBegin() returned SCROLL_STARTED.
  //
  // Is a no-op if no scroller was latched to in ScrollBegin and returns an
  // empty-initialized InputHandlerScrollResult.
  //
  // |delayed_by| is the delay from the event that caused the scroll. This is
  // taken into account when determining the duration of the animation if one
  // is created.
  virtual InputHandlerScrollResult ScrollUpdate(
      ScrollState scroll_state,
      base::TimeDelta delayed_by = base::TimeDelta());

  // Stop scrolling the selected layer. Must be called only if ScrollBegin()
  // returned SCROLL_STARTED. No-op if ScrollBegin wasn't called or didn't
  // result in a successful scroll latch. Snap to a snap position if
  // |should_snap| is true.
  virtual void ScrollEnd(bool should_snap = false);

  // Called to notify every time scroll-begin/end is attempted by an input
  // event.
  virtual void RecordScrollBegin(ui::ScrollInputType input_type,
                                 ScrollBeginThreadState scroll_start_state);
  virtual void RecordScrollEnd(ui::ScrollInputType input_type);

  virtual PointerResultType HitTest(const gfx::PointF& mouse_position);
  virtual InputHandlerPointerResult MouseMoveAt(
      const gfx::Point& mouse_position);
  // TODO(arakeri): Pass in the modifier instead of a bool once the refactor
  // (crbug.com/1022097) is done. For details, see crbug.com/1016955.
  virtual InputHandlerPointerResult MouseDown(const gfx::PointF& mouse_position,
                                              bool shift_modifier);
  virtual InputHandlerPointerResult MouseUp(const gfx::PointF& mouse_position);
  virtual void MouseLeave();

  // Returns visible_frame_element_id from the layer hit by the given point.
  // If the hit test failed, an invalid element ID is returned.
  virtual ElementId FindFrameElementIdAtPoint(
      const gfx::PointF& mouse_position);

  // Requests a callback to UpdateRootLayerStateForSynchronousInputHandler()
  // giving the current root scroll and page scale information.
  virtual void RequestUpdateForSynchronousInputHandler();

  // Called when the root scroll offset has been changed in the synchronous
  // input handler by the application (outside of input event handling). Offset
  // is expected in "content/page coordinates".
  virtual void SetSynchronousInputHandlerRootScrollOffset(
      const gfx::PointF& root_content_offset);

  virtual void SetBypassVsyncCondition(int32_t condition);

  virtual void PinchGestureBegin(const gfx::Point& anchor,
                                 ui::ScrollInputType source);
  virtual void PinchGestureUpdate(float magnify_delta,
                                  const gfx::Point& anchor);
  virtual void PinchGestureEnd(const gfx::Point& anchor);

  // Request another callback to InputHandlerClient::Animate().
  virtual void SetNeedsAnimateInput();

  // Returns true if there is an active scroll on the viewport.
  virtual bool IsCurrentlyScrollingViewport() const;

  virtual EventListenerProperties GetEventListenerProperties(
      EventListenerClass event_class) const;

  // Returns true if |viewport_point| hits a wheel event handler region that
  // could block scrolling.
  virtual bool HasBlockingWheelEventHandlerAt(
      const gfx::Point& viewport_point) const;

  // It returns the type of a touch start or move event listener that intersects
  // with |viewport_touch_rect|. Whether the page should be given the
  // opportunity to suppress scrolling by consuming touch events that started at
  // |viewport_touch_rect|, and whether |viewport_touch_rect| is on the
  // currently scrolling layer.
  // |out_touch_action| is assigned the allowed touch
  // action for the |viewport_touch_rect|. In the case there are no touch
  // handlers or touch action regions, |out_touch_action| is assigned
  // TouchAction::kAuto since the default touch action is auto.
  virtual TouchStartOrMoveEventListenerType
  EventListenerTypeForTouchStartOrMoveAt(const gfx::Rect& viewport_touch_rect,
                                         TouchAction* out_touch_action);

  // Calling `CreateLatencyInfoSwapPromiseMonitor()` to get a scoped
  // `LatencyInfoSwapPromiseMonitor`. During the life time of the
  // `LatencyInfoSwapPromiseMonitor`, if `SetNeedsRedraw()` or
  // `SetNeedsRedrawRect()` is called on `LayerTreeHostImpl`, the original
  // latency info will be turned into a `LatencyInfoSwapPromise`.
  virtual std::unique_ptr<LatencyInfoSwapPromiseMonitor>
  CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency);

  // Returns a new instance of `EventsMetricsManager::ScopedMonitor` to monitor
  // the scope of handling an event. If `done_callback` is not a null callback,
  // it will be called when the scope ends. If During the lifetime of the scoped
  // monitor, `SetNeedsOneBeginImplFrame()` or `SetNeedsRedraw()` are called on
  // `LayerTreeHostImpl` or a scroll animation is updated, the callback will be
  // called in the end with `handled` argument set to true, denoting that the
  // event was handled and the client should return `EventMetrics` associated
  // with the event if it is interested in reporting event latency metrics for
  // it.
  virtual std::unique_ptr<EventsMetricsManager::ScopedMonitor>
  GetScopedEventMetricsMonitor(
      EventsMetricsManager::ScopedMonitor::DoneCallback done_callback);

  virtual ScrollElasticityHelper* CreateScrollElasticityHelper();
  virtual void DestroyScrollElasticityHelper();

  // Called by the single-threaded UI Compositor to get or set the scroll offset
  // on the impl side. Returns false if |element_id| isn't in the active tree.
  virtual bool GetScrollOffsetForLayer(ElementId element_id,
                                       gfx::PointF* offset);
  virtual bool ScrollLayerTo(ElementId element_id, const gfx::PointF& offset);

  // Sets the initial and target offset for scroll snapping for the currently
  // scrolling node and the given natural displacement. Also sets the target
  // element of the snap's scrolling animation.
  // |natural_displacement_in_viewport| is the estimated total scrolling for
  // the active scroll sequence.
  // Returns false if their is no position to snap to.
  virtual bool GetSnapFlingInfoAndSetAnimatingSnapTarget(
      const gfx::Vector2dF& current_delta,
      const gfx::Vector2dF& natural_displacement_in_viewport,
      gfx::PointF* initial_offset,
      gfx::PointF* target_offset);

  // |did_finish| is true if the animation reached its target position (i.e.
  // it wasn't aborted).
  virtual void ScrollEndForSnapFling(bool did_finish);

  // Notifies when any input event is received, irrespective of whether it is
  // being handled by the InputHandler or not.
  virtual void NotifyInputEvent(bool is_fling);

  // Returns true if ScrollbarController is in the middle of a scroll operation.
  virtual bool ScrollbarScrollIsActive();

  // Defers posting BeginMainFrame tasks. This is used during the main thread
  // hit test for a GestureScrollBegin, to avoid posting a frame before the
  // compositor thread has had a chance to update the scroll offset.
  virtual void SetDeferBeginMainFrame(bool defer_begin_main_frame) const;

  virtual void UpdateBrowserControlsState(
      BrowserControlsState constraints,
      BrowserControlsState current,
      bool animate,
      base::optional_ref<const BrowserControlsOffsetTagModifications>
          offset_tag_modifications);

  virtual void SetIsHandlingTouchSequence(bool is_handling_touch_sequence);

  // Returns the ElementId of the currently latched scroller, or invalid id.
  virtual ElementId LatchedScrollerElementId() const;

  bool CanConsumeDelta(const ScrollState& scroll_state,
                       const ScrollNode& scroll_node);
  // Returns the amount of delta that can be applied to scroll_node, taking
  // page scale into account.
  gfx::Vector2dF ComputeScrollDelta(
      const ScrollNode& scroll_node,
      const gfx::Vector2dF& delta,
      const ScrollState* scroll_state = nullptr) const;

  gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node,
                                  const gfx::Vector2dF& delta,
                                  const gfx::Point& viewport_point,
                                  bool is_direct_manipulation);

  // Returns the size of one page of the given scroll node in physical pixels
  // for the purposes of scrolling.
  gfx::Size PageSize(const ScrollNode& scroll_node) const;
  float LineStep() const;

  // Resolves a delta in the given granularity for the |scroll_node| into
  // physical pixels to scroll.
  gfx::Vector2dF ResolveScrollGranularityToPixels(
      const ScrollNode& scroll_node,
      const gfx::Vector2dF& scroll_delta,
      ui::ScrollGranularity granularity);

  bool CurrentScrollNeedsFrameAlignment() const;

  // Used to set the pinch gesture active state when the pinch gesture is
  // handled on another layer tree. In a page with OOPIFs, only the main
  // frame's layer tree directly handles pinch events. But layer trees for
  // sub-frames need to know when pinch gestures are active so they can
  // throttle the re-rastering. This function allows setting this flag on
  // OOPIF layer trees using information sent (initially) from the main-frame.
  void set_external_pinch_gesture_active(bool external_pinch_gesture_active) {
    external_pinch_gesture_active_ = external_pinch_gesture_active;
    // Only one of the flags should ever be true at any given time.
    DCHECK(!pinch_gesture_active_ || !external_pinch_gesture_active_);
  }

  bool pinch_gesture_active() const {
    return pinch_gesture_active_ || external_pinch_gesture_active_;
  }

  void set_force_smooth_wheel_scrolling_for_testing(bool enabled) {
    force_smooth_wheel_scrolling_for_testing_ = enabled;
  }

  gfx::Vector2dF accumulated_root_overscroll_for_testing() const {
    return accumulated_root_overscroll_;
  }

  bool animating_for_snap_for_testing(
      ElementId element_id = ElementId()) const {
    return IsAnimatingForSnap(element_id);
  }

  base::flat_map<ElementId, SnapAnimationData>&
  get_snap_animation_data_map_for_testing() {
    return snap_animation_data_map_;
  }

  const std::unique_ptr<SnapSelectionStrategy>& snap_strategy_for_testing()
      const {
    return snap_strategy_;
  }

  // Detects whether or not the scroll generating the |result| affected the
  // inner or outer viewports.
  void SetViewportConsumedDelta(const ViewportScrollResult& result);

  // =========== InputDelegateForCompositor Interface - This section implements
  // the interface that LayerTreeHostImpl uses to communicate with the input
  // system.
  void ProcessCommitDeltas(
      CompositorCommitData* commit_data,
      const MutatorHost* main_thread_mutator_host) override;
  void TickAnimations(base::TimeTicks monotonic_time) override;
  void WillShutdown() override;
  void WillDraw() override;
  void WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
  void DidCommit() override;
  void DidActivatePendingTree() override;
  void DidFinishImplFrame() override;
  void OnBeginImplFrameDeadline() override;
  void RootLayerStateMayHaveChanged() override;
  void DidRegisterScrollbar(ElementId scroll_element_id,
                            ScrollbarOrientation orientation) override;
  void DidUnregisterScrollbar(ElementId scroll_element_id,
                              ScrollbarOrientation orientation) override;
  void ScrollOffsetAnimationFinished(ElementId element_id) override;
  void ElasticOverscrollAnimationFinished(ElementId finished_id) override;
  void SetPrefersReducedMotion(bool prefers_reduced_motion) override;
  bool IsCurrentlyScrolling() const override;
  ActivelyScrollingType GetActivelyScrollingType() const override;
  bool IsHandlingTouchSequence() const override;
  bool IsCurrentScrollMainRepainted() const override;
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  void HandleScrollUpdateForInternalBeginFrame(const viz::BeginFrameArgs& args) override;
#endif // BUILDFLAG(ARKWEB_INPUT_EVENTS)
  bool HasQueuedInput() const override;
#if BUILDFLAG(ARKWEB_TEST)
  virtual InputHandlerUtils* handler_utils() {
#else
  InputHandlerUtils* handler_utils() {
#endif // BUILDFLAG(ARKWEB_TEST)
    return handler_utils_.get();
  }

 private:
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest,
                           AbortAnimatedScrollBeforeStartingAutoscroll);
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AnimatedScrollYielding);
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AutoscrollOnDeletedScrollbar);
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest,
                           ThumbDragAfterJumpClickOrThumbClick);
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, ScrollOnLargeThumb);
  FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AutoscrollTaskAbort);

  // This method gets the scroll offset for a regular scroller, or the combined
  // visual and layout offsets of the viewport.
  gfx::PointF GetVisualScrollOffset(const ScrollNode& scroll_node) const;
  bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor);
  bool IsAnimatingForSnap(ElementId element_id) const;

  ScrollNode* CurrentlyScrollingNode();
  const ScrollNode* CurrentlyScrollingNode() const;
  void ClearCurrentlyScrollingNode();
  ScrollTree& GetScrollTree();
  ScrollTree& GetScrollTree() const;
  Viewport& GetViewport() const;

  ScrollNode* InnerViewportScrollNode() const;
  ScrollNode* OuterViewportScrollNode() const;

  void SetNeedsCommit();
  LayerTreeImpl& ActiveTree();
  LayerTreeImpl& ActiveTree() const;

  void UpdateRootLayerStateForSynchronousInputHandler();

  // Called during ScrollBegin once a scroller was successfully latched to
  // (i.e.  it can and will consume scroll delta on the compositor thread). The
  // latched scroller is now available in CurrentlyScrollingNode().
  // TODO(bokan): There's some debate about the name of this method. We should
  // get consensus on terminology to use and apply it consistently.
  // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520
  void DidLatchToScroller(const ScrollState& scroll_state,
                          ui::ScrollInputType type);

  // This function keeps track of sources of scrolls that are handled in the
  // compositor side. The information gets shared by the main thread as part of
  // the begin_main_frame_state. Finally Use counters are updated in the main
  // thread side to keep track of the frequency of scrolling with different
  // sources per page load. TODO(crbug.com/40506330): Use GRC API to plumb the
  // scroll source info for Use Counters.
  void UpdateScrollSourceInfo(const ScrollState& scroll_state,
                              ui::ScrollInputType type);

  // Applies the scroll_state to the currently latched scroller. See comment in
  // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|.
  void ScrollLatchedScroller(ScrollState& scroll_state,
                             base::TimeDelta delayed_by);

  enum class SnapReason { kGestureScrollEnd, kScrollOffsetAnimationFinished };

  // Creates an animation curve and returns true if we need to update the
  // scroll position to a snap point. Otherwise returns false.
  bool SnapAtScrollEnd(SnapReason reason);

  // `layer` is returned from a regular hit test, and
  // `first_scrollable_or_opaque_to_hit_test_layer` is the first scroller,
  // scrollbar, or layer opaque to hit test, when we perform a hit test for
  // all layers from top to bottom in z order.
  // This function returns true if we know which scroller to scroll, and
  // `out_node_to_scroll` is set to the scroll node to scroll. It returns
  // false when `layer` covers `first_layer_scrollable_or_opaque_to_hit_test`
  // but they have different nearest scroll ancestors, and we don't know
  // which scroll ancestor to scroll. This includes the case that a scroller is
  // masked by a mask layer for mask image, clip-path, rounded border, etc.
  bool IsInitialScrollHitTestReliable(
      const LayerImpl* layer,
      const LayerImpl* first_layer_scrollable_or_opaque_to_hit_test,
      ScrollNode*& out_node_to_scroll) const;

  // Returns the ScrollNode we should use to scroll, accounting for viewport
  // scroll chaining rules.
  ScrollNode* GetNodeToScroll(ScrollNode* node) const;
  ScrollNode* GetNodeToScrollForLayer(const LayerImpl* layer) const;

  // Given a starting node (determined by hit-test), walks up the scroll tree
  // looking for the first node that can consume scroll from the given
  // scroll_state and returns the first such node. If none is found, or if
  // starting_node is nullptr, returns nullptr;
  ScrollNode* FindNodeToLatch(ScrollState* scroll_state,
                              ScrollNode* starting_node,
                              ui::ScrollInputType type);

  bool CanPropagate(ScrollNode* scroll_node, float x, float y);

  // Performs a hit test to determine the ScrollNode to use when scrolling at
  // |viewport_point|. If no layer is hit, this falls back to the inner
  // viewport scroll node. Returns:
  // - If |hit_test_sucessful| is false, hit testing has failed and the
  //   compositor cannot determine the correct scroll node (e.g. see comments in
  //   IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this
  //   case.
  // - If |hit_test_successful| is true, returns the ScrollNode to use in
  //   |scroll_node|. This can be nullptr if no layer was hit and there are no
  //   viewport nodes (e.g. OOPIF, UI compositor).
  struct ScrollHitTestResult {
    raw_ptr<ScrollNode> scroll_node;
    bool hit_test_successful;
    uint32_t main_thread_hit_test_reasons =
        MainThreadScrollingReason::kNotScrollingOnMain;
  };
  ScrollHitTestResult HitTestScrollNode(
      const gfx::PointF& device_viewport_point) const;

  bool ShouldAnimateScroll(const ScrollState& scroll_state) const;

  std::optional<gfx::PointF> ScrollAnimationUpdateTarget(
      const ScrollNode& scroll_node,
      const gfx::Vector2dF& scroll_delta,
      base::TimeDelta delayed_by);

  // Transforms viewport start point and scroll delta to local start point and
  // local delta, respectively. If the transformation of either the start or end
  // point of a scroll is clipped, the function returns false.
  bool CalculateLocalScrollDeltaAndStartPoint(
      const ScrollNode& scroll_node,
      const gfx::PointF& viewport_point,
      const gfx::Vector2dF& viewport_delta,
      gfx::Vector2dF* out_local_scroll_delta,
      gfx::PointF* out_local_start_point = nullptr);
  gfx::Vector2dF ScrollNodeWithViewportSpaceDelta(
      const ScrollNode& scroll_node,
      const gfx::PointF& viewport_point,
      const gfx::Vector2dF& viewport_delta);
  gfx::Vector2dF ScrollNodeWithLocalDelta(
      const ScrollNode& scroll_node,
      const gfx::Vector2dF& local_delta) const;
  // This helper returns an adjusted version of |delta| where the scroll delta
  // is cleared in any axis in which user scrolling is disabled (e.g. by
  // |overflow-x: hidden|).
  gfx::Vector2dF UserScrollableDelta(const ScrollNode& node,
                                     const gfx::Vector2dF& delta) const;

  void AdjustScrollDeltaForScrollbarSnap(ScrollState& scroll_state);

  FrameSequenceTrackerType GetTrackerTypeForScroll(
      ui::ScrollInputType input_type) const;

  ScrollbarController* scrollbar_controller_for_testing() const {
    return scrollbar_controller_.get();
  }

  std::optional<gfx::PointF> ConstrainFling(gfx::PointF original);

  // Estimate how to adjust the height of the snapport rect based on the state
  // of browser controls that are being shown or hidden during a scroll gesture
  // before the Blink WebView is resized to reflect the new state.
  double PredictViewportBoundsDelta(gfx::Vector2dF scroll_distance);

  std::unique_ptr<SnapSelectionStrategy> CreateSnapStrategy(
      const ScrollState& scroll_state,
      const gfx::PointF& current_offset,
      SnapReason snap_reason) const;

  // This returns the ScrollNode associated with the CurrentlyScrollingNode()
  // that is currently animating, if one exists.
  // It is usually the same ScrollNode as the CurrentlyScrollingNode(), except
  // when the inner viewport node is animating, in which case the
  // CurrentlyScrollingNode() is still the outer viewport node.
  ScrollNode* GetAnimatingNodeForCurrentScrollingNode();
  // These get and set the ElementIds of the elements to which |element_id| is
  // snapping while the snap animation is running.
  TargetSnapAreaElementIds GetAnimatingSnapTargetsForElement(
      ElementId element_id) const;
  void SetAnimatingSnapTargetsForElement(
      ElementId element_id,
      TargetSnapAreaElementIds target_ids = TargetSnapAreaElementIds());
  void ClearAnimatingSnapTargetsForElement(ElementId element_id);

  inline void EnsureSnapAnimationData(ElementId element_id);

  // Add |element_id| to the set of scroll containers for which an impl scroll
  // has ended between the last commit and the next one. The main thread will
  // enqueue scrollend events for these elements.
  void InsertPendingScrollendContainer(const ElementId& element_id);

  // Stop scrolling for |scroll_node| or |CurrentlyScrollingNode()|. If
  // |scroll_node| is not null, we assume it is the ScrollNode for which the
  // scroll has ended. Otherwise, we assume the scroll has ended for
  // |CurrentlyScrollingNode()|.
  void ScrollEnd(ScrollNode* scroll_node, bool should_snap = false);

  void LimitDeltaToScrollerSize(const ScrollState& scroll_state,
                                const ScrollNode& scroll_node,
                                gfx::Vector2dF& delta) const;

  void UpdateLastLatchedScrollSourceType();

  // The input handler is owned by the delegate so their lifetimes are tied
  // together.
  const raw_ref<CompositorDelegateForInput> compositor_delegate_;

  raw_ptr<InputHandlerClient> input_handler_client_ = nullptr;

  // An object to implement the ScrollElasticityHelper interface and
  // hold all state related to elasticity. May be nullptr if never requested.
  std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_;

  // Manages composited scrollbar hit testing.
  std::unique_ptr<ScrollbarController> scrollbar_controller_;

  // Overscroll delta accumulated on the viewport throughout a scroll gesture;
  // reset when the gesture ends.
  gfx::Vector2dF accumulated_root_overscroll_;

  // Unconsumed scroll delta sent to the main thread for firing overscroll DOM
  // events. Resets after each commit.
  gfx::Vector2dF overscroll_delta_for_main_thread_;

  // The source device type that started the scroll gesture. Only set between a
  // ScrollBegin and ScrollEnd.
  std::optional<ui::ScrollInputType> latched_scroll_type_;

  // Tracks the last scroll update/begin state received. Used to infer the most
  // recent scroll type and direction.
  std::optional<ScrollState> last_scroll_begin_state_;
  std::optional<ScrollState> last_scroll_update_state_;

  // If a scroll snap is being animated, then the value of this will be the
  // element id(s) of the target(s). Otherwise, the ids will be invalid.
  // At the end of a scroll animation, the target should be set as the scroll
  // node's snap target.
  // TODO(crbug.com/372627916): Delete when cleaning up
  // MultipleImplOnlyScrollAnimations flag.
  TargetSnapAreaElementIds scroll_animating_snap_target_ids_;

  enum SnapFlingState {
    kNoFling,
    kNativeFling,
    kConstrainedNativeFling,
    kSnapFling
  };
  SnapFlingState snap_fling_state_ = kNoFling;
  std::optional<gfx::RangeF> fling_snap_constrain_x_;
  std::optional<gfx::RangeF> fling_snap_constrain_y_;

  // A set of elements that scroll-snapped to a new target since the last
  // begin main frame. The snap target ids of these elements will be sent to
  // the main thread in the next begin main frame.
  base::flat_map<ElementId, TargetSnapAreaElementIds> updated_snapped_elements_;

  // This maps each element being scrolled to information about its snap
  // target(s). When a container is scrolled, an entry is created when a snap
  // animation is kicked off and erase when that snap animation ends.
  // If |updated_snapped_elements_| is moved into ImplSnapState, the entries
  // should live beyond the snap animation, i.e. indefinitely, like
  // |updated_snapped_elements_| currently does.
  base::flat_map<ElementId, SnapAnimationData> snap_animation_data_map_;

  ElementId scroll_element_id_mouse_currently_over_;
  ElementId scroll_element_id_mouse_currently_captured_;

  // Set in ScrollBegin and outlives the currently scrolling node so it can be
  // used to send the scrollend and overscroll DOM events from the main thread
  // when scrolling occurs on the compositor thread. This value is cleared at
  // the first commit after a GSE.
  ElementId last_latched_scroller_;

  // Scroll animation can finish either before or after GSE arrival.
  // deferred_scroll_ends_ is set when the GSE has arrived before scroll
  // animation completion. ScrollEnd will get called once the animation is
  // over.
  base::flat_set<ElementId> deferred_scroll_ends_;

  // True iff some of the delta has been consumed for the current scroll
  // sequence on the specific axis.
  bool did_scroll_x_for_scroll_gesture_ = false;
  bool did_scroll_y_for_scroll_gesture_ = false;

  // did_scroll_x/y_for_scroll_gesture_ is true when contents consume the delta,
  // but delta_consumed_for_scroll_gesture_ can be true when only browser
  // controls consume all the delta.
  bool delta_consumed_for_scroll_gesture_ = false;

  // True if any of the non-zero deltas in a begin/update/end sequence was
  // applied to the layout viewport.
  bool outer_viewport_consumed_delta_ = false;

  // True if any of the non-zero deltas in a begin/update/end sequence was
  // applied to the visual viewport.
  bool inner_viewport_consumed_delta_ = false;

  // TODO(bokan): Mac doesn't yet have smooth scrolling for wheel; however, to
  // allow consistency in tests we use this bit to override that decision.
  // https://crbug.com/574283.
  bool force_smooth_wheel_scrolling_for_testing_ = false;

  // This value is used to allow the compositor to throttle re-rastering during
  // pinch gestures, when the page scale factor may be changing frequently. It
  // is set in one of two ways:
  // i) In a layer tree serving the root of the frame/compositor tree, it is
  // directly set during processing of GesturePinch events on the impl thread
  // (only the root layer tree has access to these).
  // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it
  // is set from the main thread during the commit process, using information
  // sent from the root layer tree via IPC messaging.
  bool pinch_gesture_active_ = false;
  bool external_pinch_gesture_active_ = false;
  bool pinch_gesture_end_should_clear_scrolling_node_ = false;

  // These are used to transfer usage of different types of scrolling to the
  // main thread.
  bool has_pinch_zoomed_ = false;
  bool has_scrolled_by_wheel_ = false;
  bool has_scrolled_by_touch_ = false;
  bool has_scrolled_by_precisiontouchpad_ = false;
  bool has_scrolled_by_scrollbar_ = false;

  bool prefers_reduced_motion_ = false;

#if BUILDFLAG(ARKWEB_VSYNC_SCHEDULE)
  int32_t condition_ = 0;
#endif

  bool is_handling_touch_sequence_ = false;

  // This tracks the strategy cc will use to snap at the end of the current
  // scroll based on the scroll updates so far. The |current_offset|
  // in SnapSelectionStrategy is set based on whether the scroll is animated
  // or non animated. For non-animated scrolls, it is the same as the
  // offset in the ScrollTree. For animated scrolls it is the target offset that
  // is being animated to.
  std::unique_ptr<SnapSelectionStrategy> snap_strategy_;

  // The set of scroll containers for which an impl scroll ended between the
  // last commit and the next one.
  base::flat_set<ElementId> pending_scrollend_containers_;

  base::TimeTicks last_scroll_begin_time_;

  std::unique_ptr<InputHandlerUtils> handler_utils_;

  // https://drafts.csswg.org/css-scroll-snap-1/#scroll-types.
  ScrollSourceType last_latched_scroll_source_type_ = ScrollSourceType::kNone;

  // Must be the last member to ensure this is destroyed first in the
  // destruction order and invalidates all weak pointers.
  base::WeakPtrFactory<InputHandler> weak_factory_{this};
};

}  // namespace cc

#endif  // CC_INPUT_INPUT_HANDLER_H_