/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_layers_AsyncPanZoomController_h #define mozilla_layers_AsyncPanZoomController_h #include "mozilla/layers/GeckoContentController.h" #include "mozilla/layers/RepaintRequest.h" #include "mozilla/layers/SampleTime.h" #include "mozilla/layers/ScrollbarData.h" #include "mozilla/layers/ZoomConstraints.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/Monitor.h" #include "mozilla/RecursiveMutex.h" #include "mozilla/RefPtr.h" #include "mozilla/ScrollTypes.h" #include "mozilla/StaticPrefs_apz.h" #include "mozilla/UniquePtr.h" #include "InputData.h" #include "Axis.h" // for Axis, Side, etc. #include "ExpectedGeckoMetrics.h" #include "FlingAccelerator.h" #include "InputQueue.h" #include "APZUtils.h" #include "LayersTypes.h" #include "mozilla/gfx/Matrix.h" #include "nsRegion.h" #include "nsTArray.h" #include "PotentialCheckerboardDurationTracker.h" #include "RecentEventsBuffer.h" // for RecentEventsBuffer #include "SampledAPZCState.h" #include namespace mozilla { namespace ipc { class SharedMemoryBasic; } // namespace ipc namespace wr { struct SampledScrollOffset; } // namespace wr namespace layers { class AsyncDragMetrics; class APZCTreeManager; struct ScrollableLayerGuid; class CompositorController; class GestureEventListener; struct AsyncTransform; class AsyncPanZoomAnimation; class StackScrollerFlingAnimation; template class GenericFlingAnimation; class AndroidFlingPhysics; class DesktopFlingPhysics; class InputBlockState; struct FlingHandoffState; class TouchBlockState; class PanGestureBlockState; class OverscrollHandoffChain; struct OverscrollHandoffState; class StateChangeNotificationBlocker; class CheckerboardEvent; class OverscrollEffectBase; class WidgetOverscrollEffect; class GenericOverscrollEffect; class AndroidSpecificState; struct KeyboardScrollAction; struct ZoomTarget; namespace apz { struct AsyncScrollThumbTransformer; } // Base class for grouping platform-specific APZC state variables. class PlatformSpecificStateBase { public: virtual ~PlatformSpecificStateBase() = default; virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; } // PLPPI = "ParentLayer pixels per (Screen) inch" virtual AsyncPanZoomAnimation* CreateFlingAnimation( AsyncPanZoomController& aApzc, const FlingHandoffState& aHandoffState, float aPLPPI); virtual UniquePtr CreateVelocityTracker(Axis* aAxis); static void InitializeGlobalState() {} }; /* * Represents a transform from the ParentLayer coordinate space of an APZC * to the ParentLayer coordinate space of its parent APZC. * Each layer along the way contributes to the transform. We track * contributions that are perspective transforms separately, as sometimes * these require special handling. */ struct AncestorTransform { gfx::Matrix4x4 mTransform; gfx::Matrix4x4 mPerspectiveTransform; AncestorTransform() = default; AncestorTransform(const gfx::Matrix4x4& aTransform, bool aTransformIsPerspective) { (aTransformIsPerspective ? mPerspectiveTransform : mTransform) = aTransform; } AncestorTransform(const gfx::Matrix4x4& aTransform, const gfx::Matrix4x4& aPerspectiveTransform) : mTransform(aTransform), mPerspectiveTransform(aPerspectiveTransform) {} gfx::Matrix4x4 CombinedTransform() const { return mTransform * mPerspectiveTransform; } bool ContainsPerspectiveTransform() const { return !mPerspectiveTransform.IsIdentity(); } gfx::Matrix4x4 GetPerspectiveTransform() const { return mPerspectiveTransform; } friend AncestorTransform operator*(const AncestorTransform& aA, const AncestorTransform& aB) { return AncestorTransform{ aA.mTransform * aB.mTransform, aA.mPerspectiveTransform * aB.mPerspectiveTransform}; } }; // Flags returned by AsyncPanZoomController::ArePointerEventsConsumable(). // See the function for more details. struct PointerEventsConsumableFlags { // The APZC has room to pan or zoom in response to the touch event. bool mHasRoom = false; // The panning or zooming is allowed by the touch-action property. bool mAllowedByTouchAction = false; bool IsConsumable() const { return mHasRoom && mAllowedByTouchAction; } friend bool operator==(const PointerEventsConsumableFlags& aLhs, const PointerEventsConsumableFlags& aRhs); friend std::ostream& operator<<(std::ostream& aOut, const PointerEventsConsumableFlags& aFlags); }; /** * Controller for all panning and zooming logic. Any time a user input is * detected and it must be processed in some way to affect what the user sees, * it goes through here. Listens for any input event from InputData and can * optionally handle WidgetGUIEvent-derived touch events, but this must be done * on the main thread. Note that this class completely cross-platform. * * Input events originate on the UI thread of the platform that this runs on, * and are then sent to this class. This class processes the event in some way; * for example, a touch move will usually lead to a panning of content (though * of course there are exceptions, such as if content preventDefaults the event, * or if the target frame is not scrollable). The compositor interacts with this * class by locking it and querying it for the current transform matrix based on * the panning and zooming logic that was invoked on the UI thread. * * Currently, each outer DOM window (i.e. a website in a tab, but not any * subframes) has its own AsyncPanZoomController. In the future, to support * asynchronously scrolled subframes, we want to have one AsyncPanZoomController * per frame. */ class AsyncPanZoomController { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController) typedef mozilla::MonitorAutoLock MonitorAutoLock; typedef mozilla::gfx::Matrix4x4 Matrix4x4; typedef mozilla::layers::RepaintRequest::ScrollOffsetUpdateType RepaintUpdateType; public: enum GestureBehavior { // The platform code is responsible for forwarding gesture events here. We // will not attempt to generate gesture events from MultiTouchInputs. DEFAULT_GESTURES, // An instance of GestureEventListener is used to detect gestures. This is // handled completely internally within this class. USE_GESTURE_DETECTOR }; /** * Gets the DPI from the tree manager. */ float GetDPI() const; /** * Constant describing the tolerance in distance we use, multiplied by the * device DPI, before we start panning the screen. This is to prevent us from * accidentally processing taps as touch moves, and from very short/accidental * touches moving the screen. * Note: It's an abuse of the 'Coord' class to use it to represent a 2D * distance, but it's the closest thing we currently have. */ ScreenCoord GetTouchStartTolerance() const; /** * Same as GetTouchStartTolerance, but the tolerance for how far the touch * has to move before it starts allowing touchmove events to be dispatched * to content, for non-scrollable content. */ ScreenCoord GetTouchMoveTolerance() const; /** * Same as GetTouchStartTolerance, but the tolerance for how close the second * tap has to be to the first tap in order to be counted as part of a * multi-tap gesture (double-tap or one-touch-pinch). */ ScreenCoord GetSecondTapTolerance() const; AsyncPanZoomController(LayersId aLayersId, APZCTreeManager* aTreeManager, const RefPtr& aInputQueue, GeckoContentController* aController, GestureBehavior aGestures = DEFAULT_GESTURES); // -------------------------------------------------------------------------- // These methods must only be called on the gecko thread. // /** * Read the various prefs and do any global initialization for all APZC * instances. This must be run on the gecko thread before any APZC instances * are actually used for anything meaningful. */ static void InitializeGlobalState(); // -------------------------------------------------------------------------- // These methods must only be called on the controller/UI thread. // /** * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom * in. The actual animation is done on the sampler thread after being set * up. */ void ZoomToRect(const ZoomTarget& aZoomTarget, const uint32_t aFlags); /** * Updates any zoom constraints contained in the tag. */ void UpdateZoomConstraints(const ZoomConstraints& aConstraints); /** * Schedules a runnable to run on the controller/UI thread at some time * in the future. */ void PostDelayedTask(already_AddRefed aTask, int aDelayMs); // -------------------------------------------------------------------------- // These methods must only be called on the sampler thread. // /** * Advances any animations currently running to the given timestamp. * This may be called multiple times with the same timestamp. * * The return value indicates whether or not any currently running animation * should continue. If true, the compositor should schedule another composite. */ bool AdvanceAnimations(const SampleTime& aSampleTime); bool UpdateAnimation(const RecursiveMutexAutoLock& aProofOfLock, const SampleTime& aSampleTime, nsTArray>* aOutDeferredTasks); // -------------------------------------------------------------------------- // These methods must only be called on the updater thread. // /** * A shadow layer update has arrived. |aScrollMetdata| is the new * ScrollMetadata for the container layer corresponding to this APZC. * |aIsFirstPaint| is a flag passed from the shadow * layers code indicating that the scroll metadata being sent with this call * are the initial metadata and the initial paint of the frame has just * happened. */ void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint, bool aThisLayerTreeUpdated); /** * The platform implementation must set the compositor controller so that we * can request composites. */ void SetCompositorController(CompositorController* aCompositorController); // -------------------------------------------------------------------------- // These methods can be called from any thread. // /** * Shut down the controller/UI thread state and prepare to be * deleted (which may happen from any thread). */ void Destroy(); /** * Returns true if Destroy() has already been called on this APZC instance. */ bool IsDestroyed() const; /** * Returns the transform to take something from the coordinate space of the * last thing we know gecko painted, to the coordinate space of the last thing * we asked gecko to paint. In cases where that last request has not yet been * processed, this is needed to transform input events properly into a space * gecko will understand. */ Matrix4x4 GetTransformToLastDispatchedPaint( const AsyncTransformComponents& aComponents = LayoutAndVisual) const; /** * Returns the number of CSS pixels of checkerboard according to the metrics * in this APZC. The argument provided by the caller is the composition bounds * of this APZC, additionally clipped by the composition bounds of any * ancestor APZCs, accounting for all the async transforms. */ uint32_t GetCheckerboardMagnitude( const ParentLayerRect& aClippedCompositionBounds) const; /** * Report the number of CSSPixel-milliseconds of checkerboard to telemetry. * See GetCheckerboardMagnitude for documentation of the * aClippedCompositionBounds argument that needs to be provided by the caller. */ void ReportCheckerboard(const SampleTime& aSampleTime, const ParentLayerRect& aClippedCompositionBounds); /** * Flush any active checkerboard report that's in progress. This basically * pretends like any in-progress checkerboard event has terminated, and pushes * out the report to the checkerboard reporting service and telemetry. If the * checkerboard event has not really finished, it will start a new event * on the next composite. */ void FlushActiveCheckerboardReport(); /** * See documentation on corresponding method in APZPublicUtils.h */ static gfx::IntSize GetDisplayportAlignmentMultiplier( const ScreenSize& aBaseSize); enum class ZoomInProgress { No, Yes, }; /** * Recalculates the displayport. Ideally, this should paint an area bigger * than the composite-to dimensions so that when you scroll down, you don't * checkerboard immediately. This includes a bunch of logic, including * algorithms to bias painting in the direction of the velocity and other * such things. */ static const ScreenMargin CalculatePendingDisplayPort( const FrameMetrics& aFrameMetrics, const ParentLayerPoint& aVelocity, ZoomInProgress aZoomInProgress); nsEventStatus HandleDragEvent(const MouseInput& aEvent, const AsyncDragMetrics& aDragMetrics, OuterCSSCoord aInitialThumbPos); /** * Handler for events which should not be intercepted by the touch listener. */ nsEventStatus HandleInputEvent( const InputData& aEvent, const ScreenToParentLayerMatrix4x4& aTransformToApzc); /** * Handler for gesture events. * Currently some gestures are detected in GestureEventListener that calls * APZC back through this handler in order to avoid recursive calls to * APZC::HandleInputEvent() which is supposed to do the work for * ReceiveInputEvent(). */ nsEventStatus HandleGestureEvent(const InputData& aEvent); /** * Start autoscrolling this APZC, anchored at the provided location. */ void StartAutoscroll(const ScreenPoint& aAnchorLocation); /** * Stop autoscrolling this APZC. */ void StopAutoscroll(); /** * Populates the provided object (if non-null) with the scrollable guid of * this apzc. */ void GetGuid(ScrollableLayerGuid* aGuidOut) const; /** * Returns the scrollable guid of this apzc. */ ScrollableLayerGuid GetGuid() const; /** * Returns true if this APZC instance is for the layer identified by the guid. */ bool Matches(const ScrollableLayerGuid& aGuid); /** * Returns true if the tree manager of this APZC is the same as the one * passed in. */ bool HasTreeManager(const APZCTreeManager* aTreeManager) const; void StartAnimation(AsyncPanZoomAnimation* aAnimation); /** * Cancels any currently running animation. * aFlags is a bit-field to provide specifics of how to cancel the animation. * See CancelAnimationFlags. */ void CancelAnimation(CancelAnimationFlags aFlags = Default); /** * Clear any overscroll on this APZC. */ void ClearOverscroll(); void ClearPhysicalOverscroll(); /** * Returns whether this APZC is for an element marked with the 'scrollgrab' * attribute. */ bool HasScrollgrab() const { return mScrollMetadata.GetHasScrollgrab(); } /** * Returns whether this APZC has scroll snap points. */ bool HasScrollSnapping() const { return mScrollMetadata.GetSnapInfo().HasScrollSnapping(); } /** * Returns whether this APZC has room to be panned (in any direction). */ bool IsPannable() const; /** * Returns whether this APZC represents a scroll info layer. */ bool IsScrollInfoLayer() const; /** * Returns true if the APZC has been flung with a velocity greater than the * stop-on-tap fling velocity threshold (which is pref-controlled). */ bool IsFlingingFast() const; /** * Returns whether this APZC is currently autoscrolling. */ bool IsAutoscroll() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mState == AUTOSCROLL; } /** * Returns the identifier of the touch in the last touch event processed by * this APZC. This should only be called when the last touch event contained * only one touch. */ int32_t GetLastTouchIdentifier() const; /** * Returns the matrix that transforms points from global screen space into * this APZC's ParentLayer space. * To respect the lock ordering, mRecursiveMutex must NOT be held when calling * this function (since this function acquires the tree lock). */ ScreenToParentLayerMatrix4x4 GetTransformToThis() const; /** * Convert the vector |aVector|, rooted at the point |aAnchor|, from * this APZC's ParentLayer coordinates into screen coordinates. * The anchor is necessary because with 3D tranforms, the location of the * vector can affect the result of the transform. * To respect the lock ordering, mRecursiveMutex must NOT be held when calling * this function (since this function acquires the tree lock). */ ScreenPoint ToScreenCoordinates(const ParentLayerPoint& aVector, const ParentLayerPoint& aAnchor) const; /** * Convert the vector |aVector|, rooted at the point |aAnchor|, from * screen coordinates into this APZC's ParentLayer coordinates. * The anchor is necessary because with 3D tranforms, the location of the * vector can affect the result of the transform. * To respect the lock ordering, mRecursiveMutex must NOT be held when calling * this function (since this function acquires the tree lock). */ ParentLayerPoint ToParentLayerCoordinates(const ScreenPoint& aVector, const ScreenPoint& aAnchor) const; /** * Same as above, but uses an ExternalPoint as the anchor. */ ParentLayerPoint ToParentLayerCoordinates(const ScreenPoint& aVector, const ExternalPoint& aAnchor) const; /** * Combines an offset defined as an external point, with a window-relative * offset to give an absolute external point. */ static ExternalPoint ToExternalPoint(const ExternalPoint& aScreenOffset, const ScreenPoint& aScreenPoint); /** * Gets a vector where the head is the given point, and the tail is * the touch start position. */ ScreenPoint PanVector(const ExternalPoint& aPos) const; // Return whether or not a wheel event will be able to scroll in either // direction. bool CanScroll(const InputData& aEvent) const; // Return the directions in which this APZC allows handoff (as governed by // overscroll-behavior). ScrollDirections GetAllowedHandoffDirections() const; // Return the directions in which this APZC allows overscrolling. ScrollDirections GetOverscrollableDirections() const; // Return whether or not a scroll delta will be able to scroll in either // direction. bool CanScroll(const ParentLayerPoint& aDelta) const; // Return whether or not a scroll delta will be able to scroll in either // direction with wheel. bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const; // Return whether or not there is room to scroll this APZC // in the given direction. bool CanScroll(ScrollDirection aDirection) const; // Return the directions in which this APZC is able to scroll. SideBits ScrollableDirections() const; // Return true if there is room to scroll along with moving the dynamic // toolbar. // // NOTE: This function should be used only for the root content APZC. bool CanVerticalScrollWithDynamicToolbar() const; // Return true if there is room to scroll downwards. bool CanScrollDownwards() const; /** * Convert a point on the scrollbar from this APZC's ParentLayer coordinates * to OuterCSS coordinates relative to the beginning of the scroll track. * Only the component in the direction of scrolling is returned. */ OuterCSSCoord ConvertScrollbarPoint(const ParentLayerPoint& aScrollbarPoint, const ScrollbarData& aThumbData) const; void NotifyMozMouseScrollEvent(const nsString& aString) const; bool OverscrollBehaviorAllowsSwipe() const; //|Metrics()| and |Metrics() const| are getter functions that both return // mScrollMetadata.mMetrics const FrameMetrics& Metrics() const; FrameMetrics& Metrics(); /** * Get the GeckoViewMetrics to be sent to Gecko for the current composite. */ GeckoViewMetrics GetGeckoViewMetrics() const; // Helper function to compare root frame metrics and update them // Returns true when the metrics have changed and were updated. bool UpdateRootFrameMetricsIfChanged(GeckoViewMetrics& aMetrics); // Returns the cached current frame time. SampleTime GetFrameTime() const; bool IsZero(const ParentLayerPoint& aPoint) const; bool IsZero(ParentLayerCoord aCoord) const; bool FuzzyGreater(ParentLayerCoord aCoord1, ParentLayerCoord aCoord2) const; private: // Get whether the horizontal content of the honoured target of auto-dir // scrolling starts from right to left. If you don't know of auto-dir // scrolling or what a honoured target means, // @see mozilla::WheelDeltaAdjustmentStrategy bool IsContentOfHonouredTargetRightToLeft(bool aHonoursRoot) const; protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~AsyncPanZoomController(); /** * Helper method for touches beginning. Sets everything up for panning and any * multitouch gestures. */ nsEventStatus OnTouchStart(const MultiTouchInput& aEvent); /** * Helper method for touches moving. Does any transforms needed when panning. */ nsEventStatus OnTouchMove(const MultiTouchInput& aEvent); /** * Helper method for touches ending. Redraws the screen if necessary and does * any cleanup after a touch has ended. */ nsEventStatus OnTouchEnd(const MultiTouchInput& aEvent); /** * Helper method for touches being cancelled. Treated roughly the same as a * touch ending (OnTouchEnd()). */ nsEventStatus OnTouchCancel(const MultiTouchInput& aEvent); /** * Helper method for scales beginning. Distinct from the OnTouch* handlers in * that this implies some outside implementation has determined that the user * is pinching. */ nsEventStatus OnScaleBegin(const PinchGestureInput& aEvent); /** * Helper method for scaling. As the user moves their fingers when pinching, * this changes the scale of the page. */ nsEventStatus OnScale(const PinchGestureInput& aEvent); /** * Helper method for scales ending. Redraws the screen if necessary and does * any cleanup after a scale has ended. */ nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent); /** * Helper methods for handling pan events. */ nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent); nsEventStatus OnPanCancelled(const PanGestureInput& aEvent); nsEventStatus OnPanBegin(const PanGestureInput& aEvent); enum class FingersOnTouchpad { Yes, No, }; nsEventStatus OnPan(const PanGestureInput& aEvent, FingersOnTouchpad aFingersOnTouchpad); nsEventStatus OnPanEnd(const PanGestureInput& aEvent); nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent); nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent); nsEventStatus HandleEndOfPan(); nsEventStatus OnPanInterrupted(const PanGestureInput& aEvent); /** * Helper methods for handling scroll wheel events. */ nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent); /** * Gets the scroll wheel delta's values in parent-layer pixels from the * original delta's values of a wheel input. */ ParentLayerPoint GetScrollWheelDelta(const ScrollWheelInput& aEvent) const; /** * This function is like GetScrollWheelDelta(aEvent). * The difference is the four added parameters provide values as alternatives * to the original wheel input's delta values, so |aEvent|'s delta values are * ignored in this function, we only use some other member variables and * functions of |aEvent|. */ ParentLayerPoint GetScrollWheelDelta(const ScrollWheelInput& aEvent, double aDeltaX, double aDeltaY, double aMultiplierX, double aMultiplierY) const; /** * This deleted function is used for: * 1. avoiding accidental implicit value type conversions of input delta * values when callers intend to call the above function; * 2. decoupling the manual relationship between the delta value type and the * above function. If by any chance the defined delta value type in * ScrollWheelInput has changed, this will automatically result in build * time failure, so we can learn of it the first time and accordingly * redefine those parameters' value types in the above function. */ template ParentLayerPoint GetScrollWheelDelta(ScrollWheelInput&, T, T, T, T) = delete; /** * Helper methods for handling keyboard events. */ nsEventStatus OnKeyboard(const KeyboardInput& aEvent); CSSPoint GetKeyboardDestination(const KeyboardScrollAction& aAction) const; // Returns the corresponding ScrollSnapFlags for the given |aAction|. // See https://drafts.csswg.org/css-scroll-snap/#scroll-types ScrollSnapFlags GetScrollSnapFlagsForKeyboardAction( const KeyboardScrollAction& aAction) const; /** * Helper methods for long press gestures. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsEventStatus OnLongPress(const TapGestureInput& aEvent); nsEventStatus OnLongPressUp(const TapGestureInput& aEvent); /** * Helper method for single tap gestures. */ nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent); /** * Helper method for a single tap confirmed. */ nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent); /** * Helper method for double taps. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsEventStatus OnDoubleTap(const TapGestureInput& aEvent); /** * Helper method for double taps where the double-tap gesture is disabled. */ nsEventStatus OnSecondTap(const TapGestureInput& aEvent); /** * Helper method to cancel any gesture currently going to Gecko. Used * primarily when a user taps the screen over some clickable content but then * pans down instead of letting go (i.e. to cancel a previous touch so that a * new one can properly take effect. */ nsEventStatus OnCancelTap(const TapGestureInput& aEvent); /** * The following five methods modify the scroll offset. For the APZC * representing the RCD-RSF, they also recalculate the offset of the layout * viewport. */ /** * Scroll the scroll frame to an X,Y offset. */ void SetVisualScrollOffset(const CSSPoint& aOffset); /** * Scroll the scroll frame to an X,Y offset, clamping the resulting scroll * offset to the scroll range. */ void ClampAndSetVisualScrollOffset(const CSSPoint& aOffset); /** * Scroll the scroll frame by an X,Y offset. * The resulting scroll offset is not clamped to the scrollable rect; * the caller must ensure it stays within range. */ void ScrollBy(const CSSPoint& aOffset); /** * Scroll the scroll frame by an X,Y offset, clamping the resulting * scroll offset to the scroll range. */ void ScrollByAndClamp(const CSSPoint& aOffset); /** * Scales the viewport by an amount (note that it multiplies this scale in to * the current scale, it doesn't set it to |aScale|). Also considers a focus * point so that the page zooms inward/outward from that point. */ void ScaleWithFocus(float aScale, const CSSPoint& aFocus); /** * Schedules a composite on the compositor thread. */ void ScheduleComposite(); /** * Schedules a composite, and if enough time has elapsed since the last * paint, a paint. */ void ScheduleCompositeAndMaybeRepaint(); /** * Gets the start point of the current touch. * This only makes sense if a touch is currently happening and OnTouchMove() * or the equivalent for pan gestures is being invoked. */ ParentLayerPoint PanStart() const; /** * Gets a vector of the velocities of each axis. */ const ParentLayerPoint GetVelocityVector() const; /** * Sets the velocities of each axis. */ void SetVelocityVector(const ParentLayerPoint& aVelocityVector); /** * Gets the first touch point from a MultiTouchInput. This gets only * the first one and assumes the rest are either missing or not relevant. */ ParentLayerPoint GetFirstTouchPoint(const MultiTouchInput& aEvent); /** * Gets the relevant point in the event * (eg. first touch, or pinch focus point) of the given InputData. */ ExternalPoint GetExternalPoint(const InputData& aEvent); /** * Gets the relevant point in the event, in external screen coordinates. */ ExternalPoint GetFirstExternalTouchPoint(const MultiTouchInput& aEvent); /** * Gets the amount by which this APZC is overscrolled along both axes. */ ParentLayerPoint GetOverscrollAmount() const; private: // Internal version of GetOverscrollAmount() which does not set // the test async properties. ParentLayerPoint GetOverscrollAmountInternal() const; protected: /** * Returns SideBits where this APZC is overscrolled. */ SideBits GetOverscrollSideBits() const; /** * Restore the amount by which this APZC is overscrolled along both axes * to the specified amount. This is for test-related use; overscrolling * as a result of user input should happen via OverscrollBy(). */ void RestoreOverscrollAmount(const ParentLayerPoint& aOverscroll); /** * Sets the panning state basing on the pan direction angle and current * touch-action value. */ void HandlePanningWithTouchAction(double angle); /** * Sets the panning state ignoring the touch action value. */ void HandlePanning(double angle); /** * Update the panning state and axis locks. */ void HandlePanningUpdate(const ScreenPoint& aDelta); /** * Set and update the pinch lock */ void HandlePinchLocking(const PinchGestureInput& aEvent); /** * Sets up anything needed for panning. This takes us out of the "TOUCHING" * state and starts actually panning us. We provide the physical pixel * position of the start point so that the pan gesture is calculated * regardless of if the window/GeckoView moved during the pan. */ nsEventStatus StartPanning(const ExternalPoint& aStartPoint, const TimeStamp& aEventTime); /** * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for * both axes and factors in the time delta from the last update. */ void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent); /** * Does any panning required due to a new touch event. */ void TrackTouch(const MultiTouchInput& aEvent); /** * Register the start of a touch or pan gesture at the given position and * time. */ void StartTouch(const ParentLayerPoint& aPoint, TimeStamp aTimestamp); /** * Register the end of a touch or pan gesture at the given time. */ void EndTouch(TimeStamp aTimestamp, Axis::ClearAxisLock aClearAxisLock); /** * Utility function to send updated FrameMetrics to Gecko so that it can paint * the displayport area. Calls into GeckoContentController to do the actual * work. This call will use the current metrics. If this function is called * from a non-main thread, it will redispatch itself to the main thread, and * use the latest metrics during the redispatch. */ void RequestContentRepaint( RepaintUpdateType aUpdateType = RepaintUpdateType::eUserAction); /** * Send Metrics() to Gecko to trigger a repaint. This function may filter * duplicate calls with the same metrics. This function must be called on the * main thread. */ void RequestContentRepaint(const ParentLayerPoint& aVelocity, const ScreenMargin& aDisplayportMargins, RepaintUpdateType aUpdateType); /** * Gets the current frame metrics. This is *not* the Gecko copy stored in the * layers code. */ const FrameMetrics& GetFrameMetrics() const; /** * Gets the current scroll metadata. This is *not* the Gecko copy stored in * the layers code/ */ const ScrollMetadata& GetScrollMetadata() const; /** * Gets the pointer to the apzc tree manager. All the access to tree manager * should be made via this method and not via private variable since this * method ensures that no lock is set. */ APZCTreeManager* GetApzcTreeManager() const; void AssertOnSamplerThread() const; void AssertOnUpdaterThread() const; /** * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative * to the parent document. This excludes the transient compositor transform. * NOTE: This must be converted to LayoutDevicePoint relative to the child * document before sending over IPC to a child process. */ Maybe ConvertToGecko(const ScreenIntPoint& aPoint); enum AxisLockMode { FREE, /* No locking at all */ STANDARD, /* Default axis locking mode that remains locked until pan ends */ STICKY, /* Allow lock to be broken, with hysteresis */ DOMINANT_AXIS, /* Only allow movement on one axis */ }; static AxisLockMode GetAxisLockMode(); bool UsingStatefulAxisLock() const; enum PinchLockMode { PINCH_FREE, /* No locking at all */ PINCH_STANDARD, /* Default pinch locking mode that remains locked until pinch gesture ends*/ PINCH_STICKY, /* Allow lock to be broken, with hysteresis */ }; static PinchLockMode GetPinchLockMode(); // Helper function for OnSingleTapUp(), OnSingleTapConfirmed(), and // OnLongPressUp(). nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType, const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers); // Common processing at the end of a touch block. void OnTouchEndOrCancel(); LayersId mLayersId; RefPtr mCompositorController; /* Access to the following two fields is protected by the mRefPtrMonitor, since they are accessed on the UI thread but can be cleared on the updater thread. */ RefPtr mGeckoContentController; RefPtr mGestureEventListener; mutable Monitor mRefPtrMonitor MOZ_UNANNOTATED; // This is a raw pointer to avoid introducing a reference cycle between // AsyncPanZoomController and APZCTreeManager. Since these objects don't // live on the main thread, we can't use the cycle collector with them. // The APZCTreeManager owns the lifetime of the APZCs, so nulling this // pointer out in Destroy() will prevent accessing deleted memory. Atomic mTreeManager; /* Utility functions that return a addrefed pointer to the corresponding * fields. */ already_AddRefed GetGeckoContentController() const; already_AddRefed GetGestureEventListener() const; PlatformSpecificStateBase* GetPlatformSpecificState(); /** * Convenience functions to get the corresponding fields of mZoomContraints * while holding mRecursiveMutex. */ bool ZoomConstraintsAllowZoom() const; bool ZoomConstraintsAllowDoubleTapZoom() const; protected: // Both |mScrollMetadata| and |mLastContentPaintMetrics| are protected by the // monitor. Do not read from or modify them without locking. ScrollMetadata mScrollMetadata; // Protects |mScrollMetadata|, |mLastContentPaintMetrics|, |mState| and // |mLastSnapTargetIds|. Before manipulating |mScrollMetadata|, // |mLastContentPaintMetrics| or |mLastSnapTargetIds| the monitor should be // held. When setting |mState|, either the SetState() function can be used, or // the monitor can be held and then |mState| updated. // IMPORTANT: See the note about lock ordering at the top of // APZCTreeManager.h. This is mutable to allow entering it from 'const' // methods; doing otherwise would significantly limit what methods could be // 'const'. // FIXME: Please keep in mind that due to some existing coupled relationships // among the class members, we should be aware of indirect usage of the // monitor-protected members. That is, although this monitor isn't required to // be held before manipulating non-protected class members, some functions on // those members might indirectly manipulate the protected members; in such // cases, the monitor should still be held. Let's take mX.CanScroll for // example: // Axis::CanScroll(ParentLayerCoord) calls Axis::CanScroll() which calls // Axis::GetPageLength() which calls Axis::GetFrameMetrics() which calls // AsyncPanZoomController::GetFrameMetrics(), therefore, this monitor should // be held before calling the CanScroll function of |mX| and |mY|. These // coupled relationships bring us the burden of taking care of when the // monitor should be held, so they should be decoupled in the future. mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED; private: // Metadata of the container layer corresponding to this APZC. This is // stored here so that it is accessible from the UI/controller thread. // These are the metrics at last content paint, the most recent // values we were notified of in NotifyLayersUpdate(). Since it represents // the Gecko state, it should be used as a basis for untransformation when // sending messages back to Gecko. ScrollMetadata mLastContentPaintMetadata; FrameMetrics& mLastContentPaintMetrics; // for convenience, refers to // mLastContentPaintMetadata.mMetrics // The last content repaint request. RepaintRequest mLastPaintRequestMetrics; // The metrics that we expect content to have. This is updated when we // request a content repaint, and when we receive a shadow layers update. // This allows us to transform events into Gecko's coordinate space. ExpectedGeckoMetrics mExpectedGeckoMetrics; // This holds important state from the Metrics() at previous times // SampleCompositedAsyncTransform() was called. This will always have at least // one item. mRecursiveMutex must be held when using or modifying this member. // Samples should be inserted to the "back" of the deque and extracted from // the "front". std::deque mSampledState; // Groups state variables that are specific to a platform. // Initialized on first use. UniquePtr mPlatformSpecificState; // This flag is set to true when we are in a axis-locked pan as a result of // the touch-action CSS property. bool mPanDirRestricted; // This flag is set to true when we are in a pinch-locked state. ie: user // is performing a two-finger pan rather than a pinch gesture bool mPinchLocked; // Stores the pinch events that occured within a given timeframe. Used to // calculate the focusChange and spanDistance within a fixed timeframe. // RecentEventsBuffer is not threadsafe. Should only be accessed on the // controller thread. RecentEventsBuffer mPinchEventBuffer; // Most up-to-date constraints on zooming. These should always be reasonable // values; for example, allowing a min zoom of 0.0 can cause very bad things // to happen. Hold mRecursiveMutex when accessing this. ZoomConstraints mZoomConstraints; // The last time the compositor has sampled the content transform for this // frame. SampleTime mLastSampleTime; // The last sample time at which we submitted a checkerboarding report. SampleTime mLastCheckerboardReport; // Stores the previous focus point if there is a pinch gesture happening. Used // to allow panning by moving multiple fingers (thus moving the focus point). ParentLayerPoint mLastZoomFocus; // Stores the previous zoom level at which we last sent a ScaleGestureComplete // notification. CSSToParentLayerScale mLastNotifiedZoom; // Accessing mAnimation needs to be protected by mRecursiveMutex RefPtr mAnimation; UniquePtr mOverscrollEffect; // Zoom animation id, used for zooming in WebRender. This should only be // set on the APZC instance for the root content document (i.e. the one we // support zooming on), and is only used if WebRender is enabled. The // animation id itself refers to the transform animation id that was set on // the stacking context in the WR display list. By changing the transform // associated with this id, we can adjust the scaling that WebRender applies, // thereby controlling the zoom. Maybe mZoomAnimationId; // Position on screen where user first put their finger down. ExternalPoint mStartTouch; // Accessing mScrollPayload needs to be protected by mRecursiveMutex Maybe mScrollPayload; // Representing sampled scroll offset generation, this value is bumped up // every time this APZC sampled new scroll offset. APZScrollGeneration mScrollGeneration; friend class Axis; public: Maybe NotifyScrollSampling(); /** * Invoke |callable|, passing |mLastContentPaintMetrics| as argument, * while holding the APZC lock required to access |mLastContentPaintMetrics|. * This allows code outside of an AsyncPanZoomController method implementation * to access |mLastContentPaintMetrics| without having to make a copy of it. * Passes through the return value of |callable|. */ template auto CallWithLastContentPaintMetrics(const Callable& callable) const -> decltype(callable(mLastContentPaintMetrics)) { RecursiveMutexAutoLock lock(mRecursiveMutex); return callable(mLastContentPaintMetrics); } void SetZoomAnimationId(const Maybe& aZoomAnimationId); Maybe GetZoomAnimationId() const; /* =================================================================== * The functions and members in this section are used to expose * the current async transform state to callers. */ public: /** * Allows consumers of async transforms to specify for what purpose they are * using the async transform: * * |eForHitTesting| is intended for hit-testing and other uses that need * the most up-to-date transform, reflecting all events * that have been processed so far, even if the transform * is not yet reflected visually. * |eForCompositing| is intended for the transform that should be reflected * visually. * * For example, if an APZC has metrics with the mForceDisableApz flag set, * then the |eForCompositing| async transform will be empty, while the * |eForHitTesting| async transform will reflect processed input events * regardless of mForceDisableApz. */ enum AsyncTransformConsumer { eForHitTesting, eForCompositing, }; /** * Get the current scroll offset of the scrollable frame corresponding * to this APZC, including the effects of any asynchronous panning and * zooming, in ParentLayer pixels. */ ParentLayerPoint GetCurrentAsyncScrollOffset( AsyncTransformConsumer aMode) const; /** * Get the current visual viewport of the scrollable frame corresponding * to this APZC, including the effects of any asynchronous panning and * zooming, in CSS pixels. */ CSSRect GetCurrentAsyncVisualViewport(AsyncTransformConsumer aMode) const; /** * Return a visual effect that reflects this apzc's * overscrolled state, if any. */ AsyncTransformComponentMatrix GetOverscrollTransform( AsyncTransformConsumer aMode) const; /** * Returns the incremental transformation corresponding to the async pan/zoom * in progress. That is, when this transform is multiplied with the layer's * existing transform, it will make the layer appear with the desired pan/zoom * amount. * The transform can have both scroll and zoom components; the caller can * request just one or the other, or both, via the |aComponents| parameter. * When only the eLayout component is requested, the returned translation * should really be a LayerPoint, rather than a ParentLayerPoint, as it will * not be scaled by the asynchronous zoom. * |aMode| specifies whether the async transform is queried for the purpose of * hit testing (eHitTesting) in which case the latest values from |Metrics()| * are used, or for compositing (eCompositing) in which case a sampled value * from |mSampledState| is used. * |aSampleIndex| specifies which sample in |mSampledState| to use. */ AsyncTransform GetCurrentAsyncTransform( AsyncTransformConsumer aMode, AsyncTransformComponents aComponents = LayoutAndVisual, std::size_t aSampleIndex = 0) const; /** * Returns the same transform as GetCurrentAsyncTransform(), but includes * any transform due to axis over-scroll. */ AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll( AsyncTransformConsumer aMode, AsyncTransformComponents aComponents = LayoutAndVisual, std::size_t aSampleIndex = 0) const; AutoTArray GetSampledScrollOffsets() const; /** * Returns the "zoom" bits of the transform. This includes both the rasterized * (layout device to layer scale) and async (layer scale to parent layer * scale) components of the zoom. */ LayoutDeviceToParentLayerScale GetCurrentPinchZoomScale( AsyncTransformConsumer aMode) const; ParentLayerRect GetCompositionBounds() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mScrollMetadata.GetMetrics().GetCompositionBounds(); } LayoutDeviceToLayerScale GetCumulativeResolution() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mScrollMetadata.GetMetrics().GetCumulativeResolution(); } // Returns the delta for the given InputData. ParentLayerPoint GetDeltaForEvent(const InputData& aEvent) const; /** * Get the current scroll range of the scrollable frame coreesponding to this * APZC. */ CSSRect GetCurrentScrollRangeInCssPixels() const; private: /** * Advances to the next sample, if there is one, the list of sampled states * stored in mSampledState. This will make the result of * |GetCurrentAsyncTransform(eForCompositing)| and similar functions reflect * the async scroll offset and zoom of the next sample. See also * SampleCompositedAsyncTransform which creates the samples. */ void AdvanceToNextSample(); /** * Samples the composited async transform, storing the result into * mSampledState. This will make the result of * |GetCurrentAsyncTransform(eForCompositing)| and similar functions reflect * the async scroll offset and zoom stored in |Metrics()| when the sample * is activated via some future call to |AdvanceToNextSample|. * * Returns true if the newly sampled value is different from the last * sampled value. */ bool SampleCompositedAsyncTransform( const RecursiveMutexAutoLock& aProofOfLock); /** * Updates the sample at the front of mSampledState with the latest * metrics. This makes the result of * |GetCurrentAsyncTransform(eForCompositing)| reflect the current Metrics(). */ void ResampleCompositedAsyncTransform( const RecursiveMutexAutoLock& aProofOfLock); /* * Helper functions to query the async layout viewport, scroll offset, and * zoom either directly from |Metrics()|, or from cached variables that * store the required value from the last time it was sampled by calling * SampleCompositedAsyncTransform(), depending on who is asking. */ CSSRect GetEffectiveLayoutViewport(AsyncTransformConsumer aMode, const RecursiveMutexAutoLock& aProofOfLock, std::size_t aSampleIndex = 0) const; CSSPoint GetEffectiveScrollOffset(AsyncTransformConsumer aMode, const RecursiveMutexAutoLock& aProofOfLock, std::size_t aSampleIndex = 0) const; CSSToParentLayerScale GetEffectiveZoom( AsyncTransformConsumer aMode, const RecursiveMutexAutoLock& aProofOfLock, std::size_t aSampleIndex = 0) const; /** * Returns the visible portion of the content scrolled by this APZC, in * CSS pixels. The caller must have acquired the mRecursiveMutex lock. */ CSSRect GetVisibleRect(const RecursiveMutexAutoLock& aProofOfLock) const; /** * Returns a pair of displacements both in logical/physical units for * |aEvent|. */ std::tuple GetDisplacementsForPanGesture( const PanGestureInput& aEvent); private: friend class AutoApplyAsyncTestAttributes; bool SuppressAsyncScrollOffset() const; /** * Applies |mTestAsyncScrollOffset| and |mTestAsyncZoom| to this * AsyncPanZoomController. Calls |SampleCompositedAsyncTransform| to ensure * that the GetCurrentAsync* functions consider the test offset and zoom in * their computations. */ void ApplyAsyncTestAttributes(const RecursiveMutexAutoLock& aProofOfLock); /** * Sets this AsyncPanZoomController's FrameMetrics to |aPrevFrameMetrics| and * calls |SampleCompositedAsyncTransform| to unapply any test values applied * by |ApplyAsyncTestAttributes|. */ void UnapplyAsyncTestAttributes(const RecursiveMutexAutoLock& aProofOfLock, const FrameMetrics& aPrevFrameMetrics, const ParentLayerPoint& aPrevOverscroll); /* =================================================================== * The functions and members in this section are used to manage * the state that tracks what this APZC is doing with the input events. */ protected: enum PanZoomState { NOTHING, /* no touch-start events received */ FLING, /* all touches removed, but we're still scrolling page */ TOUCHING, /* one touch-start event received */ PANNING, /* panning the frame */ PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */ PANNING_LOCKED_Y, /* as above for Y axis */ PAN_MOMENTUM, /* like PANNING, but controlled by momentum PanGestureInput events */ PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */ ANIMATING_ZOOM, /* animated zoom to a new rect */ OVERSCROLL_ANIMATION, /* Spring-based animation used to relieve overscroll once the finger is lifted. */ SMOOTH_SCROLL, /* Smooth scrolling to destination, with physics controlled by prefs specific to the scroll origin. */ SMOOTHMSD_SCROLL, /* SmoothMSD scrolling to destination. Used by CSSOM-View smooth scroll-behavior */ WHEEL_SCROLL, /* Smooth scrolling to a destination for a wheel event. */ KEYBOARD_SCROLL, /* Smooth scrolling to a destination for a keyboard event. */ AUTOSCROLL, /* Autoscroll animation. */ SCROLLBAR_DRAG /* Async scrollbar drag. */ }; // This is in theory protected by |mRecursiveMutex|; that is, it should be // held whenever this is updated. In practice though... see bug 897017. PanZoomState mState; AxisX mX; AxisY mY; /** * Returns wheter the given input state is a user pan-gesture. * * Note: momentum pan gesture states are not considered a panning state. */ static bool IsPanningState(PanZoomState aState); /** * Returns wheter a delayed transform end is queued. */ bool IsDelayedTransformEndSet(); /** * Returns wheter a delayed transform end is queued. */ void SetDelayedTransformEnd(bool aDelayedTransformEnd); /** * Returns whether the specified PanZoomState does not need to be reset when * a scroll offset update is processed. */ static bool CanHandleScrollOffsetUpdate(PanZoomState aState); /** * Determine whether a main-thread scroll offset update should result in * a call to CancelAnimation() (which interrupts in-progress animations and * gestures). * * If the update is a relative update, |aRelativeDelta| contains its amount. * If the update is not a relative update, GetMetrics() should already reflect * the new offset at the time of the call. */ bool ShouldCancelAnimationForScrollUpdate( const Maybe& aRelativeDelta); private: friend class StateChangeNotificationBlocker; /** * A counter of how many StateChangeNotificationBlockers are active. * A non-zero count will prevent state change notifications from * being dispatched. Only code that holds mRecursiveMutex should touch this. */ int mNotificationBlockers; /** * Helper to set the current state, without content controller events * for the state change. This is useful in cases where the content * controller events may need to be delayed. */ PanZoomState SetStateNoContentControllerDispatch(PanZoomState aNewState); /** * Helper to set the current state. Holds mRecursiveMutex before actually * setting it and fires content controller events based on state changes. * Always set the state using this call, do not set it directly. */ void SetState(PanZoomState aNewState); /** * Helper for getting the current state which acquires mRecursiveMutex * before accessing the field. */ PanZoomState GetState() const; /** * Fire content controller notifications about state changes, assuming no * StateChangeNotificationBlocker has been activated. */ void DispatchStateChangeNotification(PanZoomState aOldState, PanZoomState aNewState); /** * Internal helpers for checking general state of this apzc. */ bool IsInTransformingState() const; static bool IsTransformingState(PanZoomState aState); /* =================================================================== * The functions and members in this section are used to manage * blocks of touch events and the state needed to deal with content * listeners. */ public: /** * Flush a repaint request if one is needed, without throttling it with the * paint throttler. */ void FlushRepaintForNewInputBlock(); /** * Given an input event and the touch block it belongs to, check if the * event can lead to a panning/zooming behavior. * This is used for logic related to the pointer events spec (figuring out * when to dispatch the pointercancel event), as well as an input to the * computation of the APZHandledResult for the event (used on Android to * govern dynamic toolbar and pull-to-refresh behaviour). */ PointerEventsConsumableFlags ArePointerEventsConsumable( TouchBlockState* aBlock, const MultiTouchInput& aInput); /** * Clear internal state relating to touch input handling. */ void ResetTouchInputState(); /** Clear internal state relating to pan gesture input handling. */ void ResetPanGestureInputState(); /** * Gets a ref to the input queue that is shared across the entire tree * manager. */ const RefPtr& GetInputQueue() const; private: void CancelAnimationAndGestureState(); RefPtr mInputQueue; InputBlockState* GetCurrentInputBlock() const; TouchBlockState* GetCurrentTouchBlock() const; bool HasReadyTouchBlock() const; PanGestureBlockState* GetCurrentPanGestureBlock() const; PinchGestureBlockState* GetCurrentPinchGestureBlock() const; private: /* =================================================================== * The functions and members in this section are used to manage * fling animations, smooth scroll animations, and overscroll * during a fling or smooth scroll. */ public: /** * Attempt a fling with the velocity specified in |aHandoffState|. * |aHandoffState.mIsHandoff| should be true iff. the fling was handed off * from a previous APZC, and determines whether acceleration is applied * to the fling. * We only accept the fling in the direction(s) in which we are pannable. * Returns the "residual velocity", i.e. the portion of * |aHandoffState.mVelocity| that this APZC did not consume. */ ParentLayerPoint AttemptFling(const FlingHandoffState& aHandoffState); ParentLayerPoint AdjustHandoffVelocityForOverscrollBehavior( ParentLayerPoint& aHandoffVelocity) const; private: friend class StackScrollerFlingAnimation; friend class AutoscrollAnimation; template friend class GenericFlingAnimation; friend class AndroidFlingPhysics; friend class DesktopFlingPhysics; friend class OverscrollAnimation; friend class SmoothMsdScrollAnimation; friend class GenericScrollAnimation; friend class WheelScrollAnimation; friend class ZoomAnimation; friend class GenericOverscrollEffect; friend class WidgetOverscrollEffect; friend struct apz::AsyncScrollThumbTransformer; FlingAccelerator mFlingAccelerator; // Indicates if the repaint-during-pinch timer is currently set bool mPinchPaintTimerSet; // Indicates a delayed transform end notification is queued, and the // transform-end timer is currently set. mRecursiveMutex must be held // when using or modifying this member. bool mDelayedTransformEnd; // Deal with overscroll resulting from a fling animation. This is only ever // called on APZC instances that were actually performing a fling. // The overscroll is handled by trying to hand the fling off to an APZC // later in the handoff chain, or if there are no takers, continuing the // fling and entering an overscrolled state. void HandleFlingOverscroll( const ParentLayerPoint& aVelocity, SideBits aOverscrollSideBits, const RefPtr& aOverscrollHandoffChain, const RefPtr& aScrolledApzc); void HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity, SideBits aOverscrollSideBits); // Start an overscroll animation with the given initial velocity. void StartOverscrollAnimation(const ParentLayerPoint& aVelocity, SideBits aOverscrollSideBits); // Start a smooth-scrolling animation to the given destination, with physics // based on the prefs for the indicated origin. void SmoothScrollTo(const CSSPoint& aDestination, const ScrollOrigin& aOrigin); // Start a smooth-scrolling animation to the given destination, with MSD // physics that is suited for scroll-snapping. void SmoothMsdScrollTo(CSSSnapTarget&& aDestination, ScrollTriggeredByScript aTriggeredByScript); // Returns whether overscroll is allowed during an event. bool AllowScrollHandoffInCurrentBlock() const; // Invoked by the pinch repaint timer. void DoDelayedRequestContentRepaint(); // Invoked by the on pan-end handler to ensure that scrollend is only // fired once when a momentum pan or scroll snap is triggered as a // result of the pan gesture. void DoDelayedTransformEndNotification(PanZoomState aOldState); // Compute the number of ParentLayer pixels per (Screen) inch at the given // point and in the given direction. float ComputePLPPI(ParentLayerPoint aPoint, ParentLayerPoint aDirection) const; Maybe GetCurrentAnimationDestination( const RecursiveMutexAutoLock& aProofOfLock) const; /* =================================================================== * The functions and members in this section are used to make ancestor chains * out of APZC instances. These chains can only be walked or manipulated * while holding the lock in the associated APZCTreeManager instance. */ public: void SetParent(AsyncPanZoomController* aParent) { mParent = aParent; } AsyncPanZoomController* GetParent() const { return mParent; } /* Returns true if there is no APZC higher in the tree with the same * layers id. */ bool HasNoParentWithSameLayersId() const { return !mParent || (mParent->mLayersId != mLayersId); } bool IsRootForLayersId() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mScrollMetadata.IsLayersIdRoot(); } bool IsRootContent() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return Metrics().IsRootContent(); } private: // |mTreeManager| belongs in this section but it's declaration is a bit // further above due to initialization-order constraints. RefPtr mParent; /* =================================================================== * The functions and members in this section are used for scrolling, * including handing off scroll to another APZC, and overscrolling. */ ScrollableLayerGuid::ViewID GetScrollId() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return Metrics().GetScrollId(); } public: ScrollableLayerGuid::ViewID GetScrollHandoffParentId() const { return mScrollMetadata.GetScrollParentId(); } /** * Attempt to scroll in response to a touch-move from |aStartPoint| to * |aEndPoint|, which are in our (transformed) screen coordinates. * Due to overscroll handling, there may not actually have been a touch-move * at these points, but this function will scroll as if there had been. * If this attempt causes overscroll (i.e. the layer cannot be scrolled * by the entire amount requested), the overscroll is passed back to the * tree manager via APZCTreeManager::DispatchScroll(). If the tree manager * does not find an APZC further in the handoff chain to accept the * overscroll, and this APZC is pannable, this APZC enters an overscrolled * state. * |aOverscrollHandoffChain| and |aOverscrollHandoffChainIndex| are used by * the tree manager to keep track of which APZC to hand off the overscroll * to; this function increments the chain and the index and passes it on to * APZCTreeManager::DispatchScroll() in the event of overscroll. * Returns true iff. this APZC, or an APZC further down the * handoff chain, accepted the scroll (possibly entering an overscrolled * state). If this returns false, the caller APZC knows that it should enter * an overscrolled state itself if it can. * aStartPoint and aEndPoint are modified depending on how much of the * scroll gesture was consumed by APZCs in the handoff chain. */ bool AttemptScroll(ParentLayerPoint& aStartPoint, ParentLayerPoint& aEndPoint, OverscrollHandoffState& aOverscrollHandoffState); void FlushRepaintForOverscrollHandoff(); /** * If overscrolled, start a snap-back animation and return true. Even if not * overscrolled, this function tries to snap back to if there's an applicable * scroll snap point. * Otherwise return false. */ bool SnapBackIfOverscrolled(); /** * NOTE: Similar to above but this function doesn't snap back to the scroll * snap point. */ bool SnapBackIfOverscrolledForMomentum(const ParentLayerPoint& aVelocity); /** * Build the chain of APZCs along which scroll will be handed off when * this APZC receives input events. * * Notes on lifetime and const-correctness: * - The returned handoff chain is |const|, to indicate that it cannot be * changed after being built. * - When passing the chain to a function that uses it without storing it, * pass it by reference-to-const (as in |const OverscrollHandoffChain&|). * - When storing the chain, store it by RefPtr-to-const (as in * |RefPtr|). This ensures the chain is * kept alive. Note that queueing a task that uses the chain as an * argument constitutes storing, as the task may outlive its queuer. * - When passing the chain to a function that will store it, pass it as * |const RefPtr&|. This allows the * function to copy it into the |RefPtr| * that will store it, while avoiding an unnecessary copy (and thus * AddRef() and Release()) when passing it. */ RefPtr BuildOverscrollHandoffChain(); private: /** * A helper function for calling APZCTreeManager::DispatchScroll(). * Guards against the case where the APZC is being concurrently destroyed * (and thus mTreeManager is being nulled out). */ bool CallDispatchScroll(ParentLayerPoint& aStartPoint, ParentLayerPoint& aEndPoint, OverscrollHandoffState& aOverscrollHandoffState); void RecordScrollPayload(const TimeStamp& aTimeStamp); /** * A helper function for overscrolling during panning. This is a wrapper * around OverscrollBy() that also implements restrictions on entering * overscroll based on the pan angle. */ void OverscrollForPanning(ParentLayerPoint& aOverscroll, const ScreenPoint& aPanDistance); /** * Try to overscroll by 'aOverscroll'. * If we are pannable on a particular axis, that component of 'aOverscroll' * is transferred to any existing overscroll. */ void OverscrollBy(ParentLayerPoint& aOverscroll); /* =================================================================== * The functions and members in this section are used to maintain the * area that this APZC instance is responsible for. This is used when * hit-testing to see which APZC instance should handle touch events. */ public: void SetAncestorTransform(const AncestorTransform& aAncestorTransform) { mAncestorTransform = aAncestorTransform; } Matrix4x4 GetAncestorTransform() const { return mAncestorTransform.CombinedTransform(); } bool AncestorTransformContainsPerspective() const { return mAncestorTransform.ContainsPerspectiveTransform(); } // Return the perspective transform component of the ancestor transform. Matrix4x4 GetAncestorTransformPerspective() const { return mAncestorTransform.GetPerspectiveTransform(); } // Returns whether or not this apzc contains the given screen point within // its composition bounds. bool Contains(const ScreenIntPoint& aPoint) const; bool IsInOverscrollGutter(const ScreenPoint& aHitTestPoint) const; bool IsInOverscrollGutter(const ParentLayerPoint& aHitTestPoint) const; bool IsOverscrolled() const; bool IsOverscrollAnimationRunning() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mState == OVERSCROLL_ANIMATION; } // IsPhysicallyOverscrolled() checks whether the APZC is overscrolled // by an overscroll effect which applies a transform to the APZC's contents. bool IsPhysicallyOverscrolled() const; private: bool IsInInvalidOverscroll() const; public: bool IsInPanningState() const; // Returns whether being in the middle of a gesture. E.g., this APZC has // started handling a pan gesture but hasn't yet received pan-end, etc. bool IsInScrollingGesture() const; private: /* This is the cumulative CSS transform for all the layers from (and * including) the parent APZC down to (but excluding) this one, and excluding * any perspective transforms. */ AncestorTransform mAncestorTransform; /* =================================================================== * The functions and members in this section are used for testing * and assertion purposes only. */ public: /** * Gets whether this APZC has performed async key scrolling. */ bool TestHasAsyncKeyScrolled() const { return mTestHasAsyncKeyScrolled; } /** * Set an extra offset for testing async scrolling. */ void SetTestAsyncScrollOffset(const CSSPoint& aPoint); /** * Set an extra offset for testing async scrolling. */ void SetTestAsyncZoom(const LayerToParentLayerScale& aZoom); LayersId GetLayersId() const { return mLayersId; } bool IsAsyncZooming() const { RecursiveMutexAutoLock lock(mRecursiveMutex); return mState == PINCHING || mState == ANIMATING_ZOOM; } private: // The timestamp of the latest touch start event. TimeStamp mTouchStartTime; // Used for interpolating touch events that cross the touch-start // tolerance threshold. struct TouchSample { ExternalPoint mPosition; TimeStamp mTimeStamp; }; // Information about the latest touch event. // This is only populated when we're in the TOUCHING state // (and thus the last touch event has only one touch point). TouchSample mLastTouch; // The time duration between mTouchStartTime and the touchmove event that // started the pan (the touchmove event that transitioned this APZC from the // TOUCHING state to one of the PANNING* states). Only valid while this APZC // is in a panning state. TimeDuration mTouchStartRestingTimeBeforePan; Maybe mMinimumVelocityDuringPan; // This variable needs to be protected by |mRecursiveMutex|. ScrollSnapTargetIds mLastSnapTargetIds; // Extra offset to add to the async scroll position for testing CSSPoint mTestAsyncScrollOffset; // Extra zoom to include in the aync zoom for testing LayerToParentLayerScale mTestAsyncZoom; uint8_t mTestAttributeAppliers; // Flag to track whether or not this APZC has ever async key scrolled. bool mTestHasAsyncKeyScrolled; /* =================================================================== * The functions and members in this section are used for checkerboard * recording. */ private: // Helper function to update the in-progress checkerboard event, if any. void UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock, uint32_t aMagnitude); // Mutex protecting mCheckerboardEvent Mutex mCheckerboardEventLock MOZ_UNANNOTATED; // This is created when this APZC instance is first included as part of a // composite. If a checkerboard event takes place, this is destroyed at the // end of the event, and a new one is created on the next composite. UniquePtr mCheckerboardEvent; // This is used to track the total amount of time that we could reasonably // be checkerboarding. Combined with other info, this allows us to // meaningfully say how frequently users actually encounter checkerboarding. PotentialCheckerboardDurationTracker mPotentialCheckerboardTracker; /* =================================================================== * The functions in this section are used for CSS scroll snapping. */ // If moving |aStartPosition| by |aDelta| should trigger scroll snapping, // adjust |aDelta| to reflect the snapping (that is, make it a delta that will // take us to the desired snap point). The delta is interpreted as being // relative to |aStartPosition|, and if a target snap point is found, // |aStartPosition| is also updated, to the value of the snap point. // |aUnit| affects the snapping behaviour (see ScrollSnapUtils:: // GetSnapPointForDestination). // Returns true iff. a target snap point was found. Maybe MaybeAdjustDeltaForScrollSnapping( ScrollUnit aUnit, ScrollSnapFlags aSnapFlags, ParentLayerPoint& aDelta, CSSPoint& aStartPosition); // A wrapper function of MaybeAdjustDeltaForScrollSnapping for // ScrollWheelInput. Maybe MaybeAdjustDeltaForScrollSnappingOnWheelInput( const ScrollWheelInput& aEvent, ParentLayerPoint& aDelta, CSSPoint& aStartPosition); Maybe MaybeAdjustDestinationForScrollSnapping( const KeyboardInput& aEvent, CSSPoint& aDestination, ScrollSnapFlags aSnapFlags); // Snap to a snap position nearby the current scroll position, if appropriate. void ScrollSnap(ScrollSnapFlags aSnapFlags); // Snap to a snap position nearby the destination predicted based on the // current velocity, if appropriate. void ScrollSnapToDestination(); // Snap to a snap position nearby the provided destination, if appropriate. void ScrollSnapNear(const CSSPoint& aDestination, ScrollSnapFlags aSnapFlags); // Find a snap point near |aDestination| that we should snap to. // Returns the snap point if one was found, or an empty Maybe otherwise. // |aUnit| affects the snapping behaviour (see ScrollSnapUtils:: // GetSnapPointForDestination). It should generally be determined by the // type of event that's triggering the scroll. Maybe FindSnapPointNear(const CSSPoint& aDestination, ScrollUnit aUnit, ScrollSnapFlags aSnapFlags); // If |aOriginalEvent| crosses the touch-start tolerance threshold, split it // into two events: one that just reaches the threshold, and the remainder. // // |aPanThreshold| is the touch-start tolerance, and |aVectorLength| is // the length of the vector from the touch-start position to |aOriginalEvent|. // These values could be computed from |aOriginalEvent| but they are // passed in for convenience since the caller also needs to compute them. // // |aExtPoint| is the position of |aOriginalEvent| in External coordinates, // and in case of a split is modified by the function to reflect the position // of of the first event. This is a workaround for the fact that recomputing // the External position from the returned event would require a round-trip // through |mScreenPoint| which is an integer. Maybe> MaybeSplitTouchMoveEvent( const MultiTouchInput& aOriginalEvent, ScreenCoord aPanThreshold, float aVectorLength, ExternalPoint& aExtPoint); friend std::ostream& operator<<( std::ostream& aOut, const AsyncPanZoomController::PanZoomState& aState); }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_PanZoomController_h