1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_layers_Axis_h 8 #define mozilla_layers_Axis_h 9 10 #include <sys/types.h> // for int32_t 11 12 #include "APZUtils.h" 13 #include "AxisPhysicsMSDModel.h" 14 #include "mozilla/DataMutex.h" // for DataMutex 15 #include "mozilla/gfx/Types.h" // for Side 16 #include "mozilla/TimeStamp.h" // for TimeDuration 17 #include "nsTArray.h" // for nsTArray 18 #include "Units.h" 19 20 namespace mozilla { 21 namespace layers { 22 23 const float EPSILON = 0.0001f; 24 25 /** 26 * Compare two coordinates for equality, accounting for rounding error. 27 * Use both FuzzyEqualsAdditive() with COORDINATE_EPISLON, which accounts for 28 * things like the error introduced by rounding during a round-trip to app 29 * units, and FuzzyEqualsMultiplicative(), which accounts for accumulated error 30 * due to floating-point operations (which can be larger than COORDINATE_EPISLON 31 * for sufficiently large coordinate values). 32 */ 33 bool FuzzyEqualsCoordinate(float aValue1, float aValue2); 34 35 struct FrameMetrics; 36 class AsyncPanZoomController; 37 38 /** 39 * Interface for computing velocities along the axis based on 40 * position samples. 41 */ 42 class VelocityTracker { 43 public: 44 virtual ~VelocityTracker() = default; 45 46 /** 47 * Start tracking velocity along this axis, starting with the given 48 * initial position and corresponding timestamp. 49 */ 50 virtual void StartTracking(ParentLayerCoord aPos, TimeStamp aTimestamp) = 0; 51 /** 52 * Record a new position along this axis, at the given timestamp. 53 * Returns the average velocity between the last sample and this one, or 54 * or Nothing() if a reasonable average cannot be computed. 55 */ 56 virtual Maybe<float> AddPosition(ParentLayerCoord aPos, 57 TimeStamp aTimestamp) = 0; 58 /** 59 * Compute an estimate of the axis's current velocity, based on recent 60 * position samples. It's up to implementation how many samples to consider 61 * and how to perform the computation. 62 * If the tracker doesn't have enough samples to compute a result, it 63 * may return Nothing{}. 64 */ 65 virtual Maybe<float> ComputeVelocity(TimeStamp aTimestamp) = 0; 66 /** 67 * Clear all state in the velocity tracker. 68 */ 69 virtual void Clear() = 0; 70 }; 71 72 /** 73 * Helper class to maintain each axis of movement (X,Y) for panning and zooming. 74 * Note that everything here is specific to one axis; that is, the X axis knows 75 * nothing about the Y axis and vice versa. 76 */ 77 class Axis { 78 public: 79 explicit Axis(AsyncPanZoomController* aAsyncPanZoomController); 80 81 /** 82 * Notify this Axis that a new touch has been received, including a timestamp 83 * for when the touch was received. This triggers a recalculation of velocity. 84 * This can also used for pan gesture events. For those events, |aPos| is 85 * an invented position corresponding to the mouse position plus any 86 * accumulated displacements over the course of the pan gesture. 87 */ 88 void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, 89 TimeStamp aTimestamp); 90 91 public: 92 /** 93 * Notify this Axis that a touch has begun, i.e. the user has put their finger 94 * on the screen but has not yet tried to pan. 95 */ 96 void StartTouch(ParentLayerCoord aPos, TimeStamp aTimestamp); 97 98 /** 99 * Notify this Axis that a touch has ended gracefully. This may perform 100 * recalculations of the axis velocity. 101 */ 102 void EndTouch(TimeStamp aTimestamp); 103 104 /** 105 * Notify this Axis that the gesture has ended forcefully. Useful for stopping 106 * flings when a user puts their finger down in the middle of one (i.e. to 107 * stop a previous touch including its fling so that a new one can take its 108 * place). 109 */ 110 void CancelGesture(); 111 112 /** 113 * Takes a requested displacement to the position of this axis, and adjusts it 114 * to account for overscroll (which might decrease the displacement; this is 115 * to prevent the viewport from overscrolling the page rect), and axis locking 116 * (which might prevent any displacement from happening). If overscroll 117 * ocurred, its amount is written to |aOverscrollAmountOut|. 118 * The |aDisplacementOut| parameter is set to the adjusted displacement, and 119 * the function returns true if and only if internal overscroll amounts were 120 * changed. 121 */ 122 bool AdjustDisplacement(ParentLayerCoord aDisplacement, 123 /* ParentLayerCoord */ float& aDisplacementOut, 124 /* ParentLayerCoord */ float& aOverscrollAmountOut, 125 bool aForceOverscroll = false); 126 127 /** 128 * Overscrolls this axis by the requested amount in the requested direction. 129 * The axis must be at the end of its scroll range in this direction. 130 */ 131 void OverscrollBy(ParentLayerCoord aOverscroll); 132 133 /** 134 * Return the amount of overscroll on this axis, in ParentLayer pixels. 135 * 136 * If this amount is nonzero, the relevant component of 137 * mAsyncPanZoomController->Metrics().mScrollOffset must be at its 138 * extreme allowed value in the relevant direction (that is, it must be at 139 * its maximum value if we are overscrolled at our composition length, and 140 * at its minimum value if we are overscrolled at the origin). 141 */ 142 ParentLayerCoord GetOverscroll() const; 143 144 /** 145 * Restore the amount by which this axis is overscrolled to the specified 146 * amount. This is for test-related use; overscrolling as a result of user 147 * input should happen via OverscrollBy(). 148 */ 149 void RestoreOverscroll(ParentLayerCoord aOverscroll); 150 151 /** 152 * Start an overscroll animation with the given initial velocity. 153 */ 154 void StartOverscrollAnimation(float aVelocity); 155 156 /** 157 * Sample the snap-back animation to relieve overscroll. 158 * |aDelta| is the time since the last sample. 159 */ 160 bool SampleOverscrollAnimation(const TimeDuration& aDelta); 161 162 /** 163 * Stop an overscroll animation. 164 */ 165 void EndOverscrollAnimation(); 166 167 /** 168 * Return whether this axis is overscrolled in either direction. 169 */ 170 bool IsOverscrolled() const; 171 172 /** 173 * Return true if this axis is overscrolled but its scroll offset 174 * has changed in a way that makes the oversrolled state no longer 175 * valid (for example, it is overscrolled at the top but the 176 * scroll offset is no longer zero). 177 */ 178 bool IsInInvalidOverscroll() const; 179 180 /** 181 * Clear any overscroll amount on this axis. 182 */ 183 void ClearOverscroll(); 184 185 /** 186 * Returns whether the overscroll animation is alive. 187 */ 188 bool IsOverscrollAnimationAlive() const; 189 190 /** 191 * Returns whether the overscroll animation is running. 192 * Note that unlike the above IsOverscrollAnimationAlive, this function 193 * returns false even if the animation is still there but is very close to 194 * the destination position and its velocity is quite low, i.e. it's time to 195 * finish. 196 */ 197 bool IsOverscrollAnimationRunning() const; 198 199 /** 200 * Gets the starting position of the touch supplied in StartTouch(). 201 */ 202 ParentLayerCoord PanStart() const; 203 204 /** 205 * Gets the distance between the starting position of the touch supplied in 206 * StartTouch() and the current touch from the last 207 * UpdateWithTouchAtDevicePoint(). 208 */ 209 ParentLayerCoord PanDistance() const; 210 211 /** 212 * Gets the distance between the starting position of the touch supplied in 213 * StartTouch() and the supplied position. 214 */ 215 ParentLayerCoord PanDistance(ParentLayerCoord aPos) const; 216 217 /** 218 * Returns true if the page has room to be scrolled along this axis. 219 */ 220 bool CanScroll() const; 221 222 /** 223 * Returns whether this axis can scroll any more in a particular direction. 224 */ 225 bool CanScroll(ParentLayerCoord aDelta) const; 226 227 /** 228 * Returns true if the page has room to be scrolled along this axis 229 * and this axis is not scroll-locked. 230 */ 231 bool CanScrollNow() const; 232 233 /** 234 * Clamp a point to the page's scrollable bounds. That is, a scroll 235 * destination to the returned point will not contain any overscroll. 236 */ 237 CSSCoord ClampOriginToScrollableRect(CSSCoord aOrigin) const; 238 SetAxisLocked(bool aAxisLocked)239 void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; } 240 241 /** 242 * Gets the raw velocity of this axis at this moment. 243 */ 244 float GetVelocity() const; 245 246 /** 247 * Sets the raw velocity of this axis at this moment. 248 * Intended to be called only when the axis "takes over" a velocity from 249 * another APZC, in which case there are no touch points available to call 250 * UpdateWithTouchAtDevicePoint. In other circumstances, 251 * UpdateWithTouchAtDevicePoint should be used and the velocity calculated 252 * there. 253 */ 254 void SetVelocity(float aVelocity); 255 256 /** 257 * If a displacement will overscroll the axis, this returns the amount and in 258 * what direction. 259 */ 260 ParentLayerCoord DisplacementWillOverscrollAmount( 261 ParentLayerCoord aDisplacement) const; 262 263 /** 264 * If a scale will overscroll the axis, this returns the amount and in what 265 * direction. 266 * 267 * |aFocus| is the point at which the scale is focused at. We will offset the 268 * scroll offset in such a way that it remains in the same place on the page 269 * relative. 270 * 271 * Note: Unlike most other functions in Axis, this functions operates in 272 * CSS coordinates so there is no confusion as to whether the 273 * ParentLayer coordinates it operates in are before or after the scale 274 * is applied. 275 */ 276 CSSCoord ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const; 277 278 /** 279 * Checks if an axis will overscroll in both directions by computing the 280 * content rect and checking that its height/width (depending on the axis) 281 * does not overextend past the viewport. 282 * 283 * This gets called by ScaleWillOverscroll(). 284 */ 285 bool ScaleWillOverscrollBothSides(float aScale) const; 286 287 /** 288 * Returns true if movement on this axis is locked. 289 */ 290 bool IsAxisLocked() const; 291 292 ParentLayerCoord GetOrigin() const; 293 ParentLayerCoord GetCompositionLength() const; 294 ParentLayerCoord GetPageStart() const; 295 ParentLayerCoord GetPageLength() const; 296 ParentLayerCoord GetCompositionEnd() const; 297 ParentLayerCoord GetPageEnd() const; 298 ParentLayerCoord GetScrollRangeEnd() const; 299 300 bool IsScrolledToStart() const; 301 bool IsScrolledToEnd() const; 302 GetPos()303 ParentLayerCoord GetPos() const { return mPos; } 304 305 bool OverscrollBehaviorAllowsHandoff() const; 306 bool OverscrollBehaviorAllowsOverscrollEffect() const; 307 308 virtual CSSToParentLayerScale GetAxisScale( 309 const CSSToParentLayerScale2D& aScale) const = 0; 310 virtual CSSCoord GetPointOffset(const CSSPoint& aPoint) const = 0; 311 virtual ParentLayerCoord GetPointOffset( 312 const ParentLayerPoint& aPoint) const = 0; 313 virtual ParentLayerCoord GetRectLength( 314 const ParentLayerRect& aRect) const = 0; 315 virtual ParentLayerCoord GetRectOffset( 316 const ParentLayerRect& aRect) const = 0; 317 virtual float GetTransformScale( 318 const AsyncTransformComponentMatrix& aMatrix) const = 0; 319 virtual ParentLayerCoord GetTransformTranslation( 320 const AsyncTransformComponentMatrix& aMatrix) const = 0; 321 virtual void PostScale(AsyncTransformComponentMatrix& aMatrix, 322 float aScale) const = 0; 323 virtual void PostTranslate(AsyncTransformComponentMatrix& aMatrix, 324 ParentLayerCoord aTranslation) const = 0; 325 326 virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0; 327 OpaqueApzcPointer()328 const void* OpaqueApzcPointer() const { return mAsyncPanZoomController; } 329 330 virtual const char* Name() const = 0; 331 332 // Convert a velocity from global inches/ms into ParentLayerCoords/ms. 333 float ToLocalVelocity(float aVelocityInchesPerMs) const; 334 335 protected: 336 // A position along the axis, used during input event processing to 337 // track velocities (and for touch gestures, to track the length of 338 // the gesture). For touch events, this represents the position of 339 // the finger (or in the case of two-finger scrolling, the midpoint 340 // of the two fingers). For pan gesture events, this represents an 341 // invented position corresponding to the mouse position at the start 342 // of the pan, plus deltas representing the displacement of the pan. 343 ParentLayerCoord mPos; 344 345 ParentLayerCoord mStartPos; 346 // The velocity can be accessed from multiple threads (e.g. APZ 347 // controller thread and APZ sampler thread), so needs to be 348 // protected by a mutex. 349 // Units: ParentLayerCoords per millisecond 350 mutable DataMutex<float> mVelocity; 351 bool mAxisLocked; // Whether movement on this axis is locked. 352 AsyncPanZoomController* mAsyncPanZoomController; 353 354 // The amount by which we are overscrolled; see GetOverscroll(). 355 ParentLayerCoord mOverscroll; 356 357 // The mass-spring-damper model for overscroll physics. 358 AxisPhysicsMSDModel mMSDModel; 359 360 // Used to track velocity over a series of input events and compute 361 // a resulting velocity to use for e.g. starting a fling animation. 362 // This member can only be accessed on the controller/UI thread. 363 UniquePtr<VelocityTracker> mVelocityTracker; 364 365 float DoGetVelocity() const; 366 void DoSetVelocity(float aVelocity); 367 368 const FrameMetrics& GetFrameMetrics() const; 369 const ScrollMetadata& GetScrollMetadata() const; 370 371 virtual OverscrollBehavior GetOverscrollBehavior() const = 0; 372 373 // Adjust a requested overscroll amount for resistance, yielding a smaller 374 // actual overscroll amount. 375 ParentLayerCoord ApplyResistance(ParentLayerCoord aOverscroll) const; 376 377 // Helper function for SampleOverscrollAnimation(). 378 void StepOverscrollAnimation(double aStepDurationMilliseconds); 379 }; 380 381 class AxisX : public Axis { 382 public: 383 explicit AxisX(AsyncPanZoomController* mAsyncPanZoomController); 384 CSSToParentLayerScale GetAxisScale( 385 const CSSToParentLayerScale2D& aScale) const override; 386 CSSCoord GetPointOffset(const CSSPoint& aPoint) const override; 387 ParentLayerCoord GetPointOffset( 388 const ParentLayerPoint& aPoint) const override; 389 ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override; 390 ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override; 391 float GetTransformScale( 392 const AsyncTransformComponentMatrix& aMatrix) const override; 393 ParentLayerCoord GetTransformTranslation( 394 const AsyncTransformComponentMatrix& aMatrix) const override; 395 void PostScale(AsyncTransformComponentMatrix& aMatrix, 396 float aScale) const override; 397 void PostTranslate(AsyncTransformComponentMatrix& aMatrix, 398 ParentLayerCoord aTranslation) const override; 399 ScreenPoint MakePoint(ScreenCoord aCoord) const override; 400 const char* Name() const override; 401 bool CanScrollTo(Side aSide) const; 402 SideBits ScrollableDirections() const; 403 404 private: 405 OverscrollBehavior GetOverscrollBehavior() const override; 406 }; 407 408 class AxisY : public Axis { 409 public: 410 explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController); 411 CSSCoord GetPointOffset(const CSSPoint& aPoint) const override; 412 ParentLayerCoord GetPointOffset( 413 const ParentLayerPoint& aPoint) const override; 414 CSSToParentLayerScale GetAxisScale( 415 const CSSToParentLayerScale2D& aScale) const override; 416 ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override; 417 ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override; 418 float GetTransformScale( 419 const AsyncTransformComponentMatrix& aMatrix) const override; 420 ParentLayerCoord GetTransformTranslation( 421 const AsyncTransformComponentMatrix& aMatrix) const override; 422 void PostScale(AsyncTransformComponentMatrix& aMatrix, 423 float aScale) const override; 424 void PostTranslate(AsyncTransformComponentMatrix& aMatrix, 425 ParentLayerCoord aTranslation) const override; 426 ScreenPoint MakePoint(ScreenCoord aCoord) const override; 427 const char* Name() const override; 428 bool CanScrollTo(Side aSide) const; 429 bool CanVerticalScrollWithDynamicToolbar() const; 430 SideBits ScrollableDirections() const; 431 SideBits ScrollableDirectionsWithDynamicToolbar( 432 const ScreenMargin& aFixedLayerMargins) const; 433 434 private: 435 OverscrollBehavior GetOverscrollBehavior() const override; 436 ParentLayerCoord GetCompositionLengthWithoutDynamicToolbar() const; 437 bool HasDynamicToolbar() const; 438 }; 439 440 } // namespace layers 441 } // namespace mozilla 442 443 #endif 444