blob: 8832e2b9cfe3914c2ac882db87bdbc7a94e15065 [file] [log] [blame]
// 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/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/time/time.h"
#include "cc/cc_export.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 "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;
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,
kMaxValue = kScrollingOnMain,
};
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:
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;
protected:
InputHandlerClient() = default;
};
// Data passed from the input handler to the main thread. Used to notify the
// main thread about changes that have occurred as a result of input since the
// last commit.
struct InputHandlerCommitData {
// Defined in input_handler.cc to avoid inlining since flat_set has
// non-trivial size destructor.
InputHandlerCommitData();
~InputHandlerCommitData();
// Unconsumed scroll delta since the last commit.
gfx::Vector2dF overscroll_delta;
// Elements that have scroll snapped to a new target since the last commit.
base::flat_set<ElementId> updated_snapped_elements;
// If a scroll was active at any point since the last commit, this will
// identify the scroller (even if it has since ended).
ElementId last_latched_scroller;
// True if a scroll gesture has ended since the last commit.
bool scroll_gesture_did_end = false;
// The following bits are set if a gesture of any type was started since
// the last commit.
bool has_pinch_zoomed = false;
bool has_scrolled_by_wheel = false;
bool has_scrolled_by_touch = false;
bool has_scrolled_by_precisiontouchpad = false;
};
// 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:
// 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 {
SCROLL_ON_MAIN_THREAD = 0,
SCROLL_ON_IMPL_THREAD,
SCROLL_IGNORED,
// SCROLL_UNKOWN is not used anymore. However we'll keep this entry as per
// the comment above.
SCROLL_UNKNOWN,
LAST_SCROLL_STATUS = SCROLL_UNKNOWN
};
explicit InputHandler(CompositorDelegateForInput& compositor_delegate);
~InputHandler() override;
InputHandler(const InputHandler&) = delete;
InputHandler& operator=(const InputHandler&) = delete;
struct ScrollStatus {
ScrollThread thread = ScrollThread::SCROLL_ON_IMPL_THREAD;
// This should be set to nonzero iff `thread` is SCROLL_ON_MAIN_THREAD.
uint32_t main_thread_scrolling_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
// Used only in scroll unification. 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.
uint32_t main_thread_hit_test_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
// Used only in scroll unification. 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 enum. (Unification avoids setting
// main_thread_scrolling_reasons, to keep that field consistent with
// semantics of ScrollThread::SCROLL_ON_IMPL_THREAD.)
uint32_t main_thread_repaint_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
// TODO(crbug.com/1155758): 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;
};
enum class TouchStartOrMoveEventListenerType {
NO_HANDLER,
HANDLER,
HANDLER_ON_SCROLLING_LAYER
};
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
// SCROLL_IGNORED 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 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 at
// |viewport_point|. Whether the page should be given the opportunity to
// suppress scrolling by consuming touch events that started at
// |viewport_point|, and whether |viewport_point| is on the currently
// scrolling layer.
// |out_touch_action| is assigned the allowed touch action for the
// |viewport_point|. 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::Point& viewport_point,
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);
virtual bool ScrollingShouldSwitchtoMainThread();
// 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& 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();
// 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);
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);
gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node,
const gfx::Vector2dF& delta,
const gfx::Point& viewport_point,
bool is_direct_manipulation);
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() const { return IsAnimatingForSnap(); }
// =========== 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 RootLayerStateMayHaveChanged() override;
void DidRegisterScrollbar(ElementId scroll_element_id,
ScrollbarOrientation orientation) override;
void DidUnregisterScrollbar(ElementId scroll_element_id,
ScrollbarOrientation orientation) override;
void ScrollOffsetAnimationFinished() override;
void SetPrefersReducedMotion(bool prefers_reduced_motion) override;
bool IsCurrentlyScrolling() const override;
ActivelyScrollingType GetActivelyScrollingType() const override;
bool IsCurrentScrollMainRepainted() const override;
private:
FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
AbortAnimatedScrollBeforeStartingAutoscroll);
FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
AnimatedScrollYielding);
FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
AutoscrollOnDeletedScrollbar);
FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
ThumbDragAfterJumpClick);
FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
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() 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;
bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status,
const ScrollNode* scroll_node) const;
bool IsTouchDraggingScrollbar(
LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
ui::ScrollInputType type);
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/691886): 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);
// Determines whether the given scroll node can scroll on the compositor
// thread or if there are any reasons it must be scrolled on the main thread
// or not at all. Note: in general, this is not sufficient to determine if a
// scroll can occur on the compositor thread. If hit testing to a scroll
// node, the caller must also check whether the hit point intersects a
// non-fast-scrolling-region of any ancestor scrolling layers. Can be removed
// after scroll unification https://crbug.com/476553.
InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
ScrollNode* scroll_node) const;
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_scrolling_layer_or_drawn_scrollbar| is returned from a hit test
// performed only on scrollers and scrollbars. Initial scroll hit testing can
// be unreliable if the latter is not the direct scroll ancestor of the
// former. In this case, we will fall back to main thread scrolling because
// the compositor thread doesn't know which layer to scroll. This happens when
// a layer covers a scroller that doesn't scroll the former, or a scroller is
// masked by a mask layer for mask image, clip-path, rounded border, etc.
//
// Note, position: fixed layers use the inner viewport as their ScrollNode
// (since they don't scroll with the outer viewport), however, scrolls from
// the fixed layer still chain to the outer viewport. It's also possible for a
// node to have the inner viewport as its ancestor without going through the
// outer viewport; however, it may still scroll using the viewport(). Hence,
// this method must use the same scroll chaining logic we use in ApplyScroll.
bool IsInitialScrollHitTestReliable(
const LayerImpl* layer,
const LayerImpl* first_scrolling_layer_or_drawn_scrollbar) const;
// Similar to above but includes complicated logic to determine whether the
// ScrollNode is able to be scrolled on the compositor or requires main
// thread scrolling. If main thread scrolling is required
// |scroll_on_main_thread| is set to true and the reason is given in
// |main_thread_scrolling_reason| to on of the enum values in
// main_thread_scrolling_reason.h. Can be removed after scroll unification
// https://crbug.com/476553.
ScrollNode* FindScrollNodeForCompositedScrolling(
const gfx::PointF& device_viewport_point,
LayerImpl* layer_hit_by_point,
bool* scroll_on_main_thread,
uint32_t* main_thread_scrolling_reason);
// Return all ScrollNode indices that have an associated layer with a non-fast
// region that intersects the point.
base::flat_set<int> NonFastScrollableNodes(
const gfx::PointF& device_viewport_point) const;
// Returns the ScrollNode we should use to scroll, accounting for viewport
// scroll chaining rules.
ScrollNode* GetNodeToScroll(ScrollNode* node) 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;
bool 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();
}
// 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.
absl::optional<ui::ScrollInputType> latched_scroll_type_;
// Tracks the last scroll update/begin state received. Used to infer the most
// recent scroll type and direction.
absl::optional<ScrollState> last_scroll_begin_state_;
absl::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.
TargetSnapAreaElementIds scroll_animating_snap_target_ids_;
// 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_;
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_end_ is set when the GSE has arrvied before scroll
// animation completion. ScrollEnd will get called once the animation is
// over.
bool deferred_scroll_end_ = false;
// Set to true when a scroll gesture being handled on the compositor has
// ended. i.e. When a GSE has arrived and any ongoing scroll animation has
// ended.
bool scroll_gesture_did_end_ = false;
// 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;
// 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;
// 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_