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 #include "Axis.h"
8 
9 #include <math.h>     // for fabsf, pow, powf
10 #include <algorithm>  // for max
11 
12 #include "APZCTreeManager.h"                // for APZCTreeManager
13 #include "AsyncPanZoomController.h"         // for AsyncPanZoomController
14 #include "FrameMetrics.h"                   // for FrameMetrics
15 #include "SimpleVelocityTracker.h"          // for FrameMetrics
16 #include "mozilla/Attributes.h"             // for final
17 #include "mozilla/Preferences.h"            // for Preferences
18 #include "mozilla/gfx/Rect.h"               // for RoundedIn
19 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread
20 #include "mozilla/mozalloc.h"               // for operator new
21 #include "mozilla/FloatingPoint.h"          // for FuzzyEqualsAdditive
22 #include "nsMathUtils.h"                    // for NS_lround
23 #include "nsPrintfCString.h"                // for nsPrintfCString
24 #include "nsThreadUtils.h"                  // for NS_DispatchToMainThread, etc
25 #include "nscore.h"                         // for NS_IMETHOD
26 
27 static mozilla::LazyLogModule sApzAxsLog("apz.axis");
28 #define AXIS_LOG(...) MOZ_LOG(sApzAxsLog, LogLevel::Debug, (__VA_ARGS__))
29 
30 namespace mozilla {
31 namespace layers {
32 
FuzzyEqualsCoordinate(float aValue1,float aValue2)33 bool FuzzyEqualsCoordinate(float aValue1, float aValue2) {
34   return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON) ||
35          FuzzyEqualsMultiplicative(aValue1, aValue2);
36 }
37 
Axis(AsyncPanZoomController * aAsyncPanZoomController)38 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
39     : mPos(0),
40       mVelocity(0.0f, "Axis::mVelocity"),
41       mAxisLocked(false),
42       mAsyncPanZoomController(aAsyncPanZoomController),
43       mOverscroll(0),
44       mMSDModel(0.0, 0.0, 0.0, StaticPrefs::apz_overscroll_spring_stiffness(),
45                 StaticPrefs::apz_overscroll_damping()),
46       mVelocityTracker(mAsyncPanZoomController->GetPlatformSpecificState()
47                            ->CreateVelocityTracker(this)) {}
48 
ToLocalVelocity(float aVelocityInchesPerMs) const49 float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const {
50   ScreenPoint velocity =
51       MakePoint(aVelocityInchesPerMs * mAsyncPanZoomController->GetDPI());
52   // Use ToScreenCoordinates() to convert a point rather than a vector by
53   // treating the point as a vector, and using (0, 0) as the anchor.
54   ScreenPoint panStart = mAsyncPanZoomController->ToScreenCoordinates(
55       mAsyncPanZoomController->PanStart(), ParentLayerPoint());
56   ParentLayerPoint localVelocity =
57       mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
58   return localVelocity.Length();
59 }
60 
UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,TimeStamp aTimestamp)61 void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,
62                                         TimeStamp aTimestamp) {
63   // mVelocityTracker is controller-thread only
64   APZThreadUtils::AssertOnControllerThread();
65 
66   mPos = aPos;
67 
68   AXIS_LOG("%p|%s got position %f\n", mAsyncPanZoomController, Name(),
69            mPos.value);
70   if (Maybe<float> newVelocity =
71           mVelocityTracker->AddPosition(aPos, aTimestamp)) {
72     DoSetVelocity(mAxisLocked ? 0 : *newVelocity);
73     AXIS_LOG("%p|%s velocity from tracker is %f%s\n", mAsyncPanZoomController,
74              Name(), *newVelocity,
75              mAxisLocked ? ", but we are axis locked" : "");
76   }
77 }
78 
StartTouch(ParentLayerCoord aPos,TimeStamp aTimestamp)79 void Axis::StartTouch(ParentLayerCoord aPos, TimeStamp aTimestamp) {
80   mStartPos = aPos;
81   mPos = aPos;
82   mVelocityTracker->StartTracking(aPos, aTimestamp);
83   mAxisLocked = false;
84 }
85 
AdjustDisplacement(ParentLayerCoord aDisplacement,float & aDisplacementOut,float & aOverscrollAmountOut,bool aForceOverscroll)86 bool Axis::AdjustDisplacement(
87     ParentLayerCoord aDisplacement,
88     /* ParentLayerCoord */ float& aDisplacementOut,
89     /* ParentLayerCoord */ float& aOverscrollAmountOut,
90     bool aForceOverscroll /* = false */) {
91   if (mAxisLocked) {
92     aOverscrollAmountOut = 0;
93     aDisplacementOut = 0;
94     return false;
95   }
96   if (aForceOverscroll) {
97     aOverscrollAmountOut = aDisplacement;
98     aDisplacementOut = 0;
99     return false;
100   }
101 
102   ParentLayerCoord displacement = aDisplacement;
103 
104   // First consume any overscroll in the opposite direction along this axis.
105   ParentLayerCoord consumedOverscroll = 0;
106   if (mOverscroll > 0 && aDisplacement < 0) {
107     consumedOverscroll = std::min(mOverscroll, -aDisplacement);
108   } else if (mOverscroll < 0 && aDisplacement > 0) {
109     consumedOverscroll = 0.f - std::min(-mOverscroll, aDisplacement);
110   }
111   mOverscroll -= consumedOverscroll;
112   displacement += consumedOverscroll;
113 
114   if (consumedOverscroll != 0.0f) {
115     AXIS_LOG("%p|%s changed overscroll amount to %f\n", mAsyncPanZoomController,
116              Name(), mOverscroll.value);
117   }
118 
119   // Split the requested displacement into an allowed displacement that does
120   // not overscroll, and an overscroll amount.
121   aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
122   if (aOverscrollAmountOut != 0.0f) {
123     // No need to have a velocity along this axis anymore; it won't take us
124     // anywhere, so we're just spinning needlessly.
125     AXIS_LOG("%p|%s has overscrolled, clearing velocity\n",
126              mAsyncPanZoomController, Name());
127     DoSetVelocity(0.0f);
128     displacement -= aOverscrollAmountOut;
129   }
130   aDisplacementOut = displacement;
131   return fabsf(consumedOverscroll) > EPSILON;
132 }
133 
ApplyResistance(ParentLayerCoord aRequestedOverscroll) const134 ParentLayerCoord Axis::ApplyResistance(
135     ParentLayerCoord aRequestedOverscroll) const {
136   // 'resistanceFactor' is a value between 0 and 1/16, which:
137   //   - tends to 1/16 as the existing overscroll tends to 0
138   //   - tends to 0 as the existing overscroll tends to the composition length
139   // The actual overscroll is the requested overscroll multiplied by this
140   // factor.
141   float resistanceFactor =
142       (1 - fabsf(GetOverscroll()) / GetCompositionLength()) / 16;
143   float result = resistanceFactor < 0 ? ParentLayerCoord(0)
144                                       : aRequestedOverscroll * resistanceFactor;
145   result = clamped(result, -8.0f, 8.0f);
146   return result;
147 }
148 
OverscrollBy(ParentLayerCoord aOverscroll)149 void Axis::OverscrollBy(ParentLayerCoord aOverscroll) {
150   MOZ_ASSERT(CanScroll());
151   // We can get some spurious calls to OverscrollBy() with near-zero values
152   // due to rounding error. Ignore those (they might trip the asserts below.)
153   if (FuzzyEqualsAdditive(aOverscroll.value, 0.0f, COORDINATE_EPSILON)) {
154     return;
155   }
156   EndOverscrollAnimation();
157   aOverscroll = ApplyResistance(aOverscroll);
158   if (aOverscroll > 0) {
159 #ifdef DEBUG
160     if (!IsScrolledToEnd()) {
161       nsPrintfCString message(
162           "composition end (%f) is not equal (within error) to page end (%f)\n",
163           GetCompositionEnd().value, GetPageEnd().value);
164       NS_ASSERTION(false, message.get());
165       MOZ_CRASH("GFX: Overscroll issue > 0");
166     }
167 #endif
168     MOZ_ASSERT(mOverscroll >= 0);
169   } else if (aOverscroll < 0) {
170 #ifdef DEBUG
171     if (!IsScrolledToStart()) {
172       nsPrintfCString message(
173           "composition origin (%f) is not equal (within error) to page origin "
174           "(%f)\n",
175           GetOrigin().value, GetPageStart().value);
176       NS_ASSERTION(false, message.get());
177       MOZ_CRASH("GFX: Overscroll issue < 0");
178     }
179 #endif
180     MOZ_ASSERT(mOverscroll <= 0);
181   }
182   mOverscroll += aOverscroll;
183 
184   AXIS_LOG("%p|%s changed overscroll amount to %f\n", mAsyncPanZoomController,
185            Name(), mOverscroll.value);
186 }
187 
GetOverscroll() const188 ParentLayerCoord Axis::GetOverscroll() const { return mOverscroll; }
189 
RestoreOverscroll(ParentLayerCoord aOverscroll)190 void Axis::RestoreOverscroll(ParentLayerCoord aOverscroll) {
191   mOverscroll = aOverscroll;
192 }
193 
StartOverscrollAnimation(float aVelocity)194 void Axis::StartOverscrollAnimation(float aVelocity) {
195   const float maxVelocity = StaticPrefs::apz_overscroll_max_velocity();
196   aVelocity = clamped(aVelocity / 2.0f, -maxVelocity, maxVelocity);
197   SetVelocity(aVelocity);
198   mMSDModel.SetPosition(mOverscroll);
199   // Convert velocity from ParentLayerCoords/millisecond to
200   // ParentLayerCoords/second.
201   mMSDModel.SetVelocity(DoGetVelocity() * 1000.0);
202 
203   AXIS_LOG(
204       "%p|%s beginning overscroll animation with amount %f and velocity %f\n",
205       mAsyncPanZoomController, Name(), mOverscroll.value, DoGetVelocity());
206 }
207 
EndOverscrollAnimation()208 void Axis::EndOverscrollAnimation() {
209   mMSDModel.SetPosition(0.0);
210   mMSDModel.SetVelocity(0.0);
211 }
212 
SampleOverscrollAnimation(const TimeDuration & aDelta)213 bool Axis::SampleOverscrollAnimation(const TimeDuration& aDelta) {
214   mMSDModel.Simulate(aDelta);
215   mOverscroll = mMSDModel.GetPosition();
216 
217   AXIS_LOG("%p|%s changed overscroll amount to %f\n", mAsyncPanZoomController,
218            Name(), mOverscroll.value);
219 
220   if (mMSDModel.IsFinished(1.0)) {
221     // "Jump" to the at-rest state. The jump shouldn't be noticeable as the
222     // velocity and overscroll are already low.
223     AXIS_LOG("%p|%s oscillation dropped below threshold, going to rest\n",
224              mAsyncPanZoomController, Name());
225     ClearOverscroll();
226     DoSetVelocity(0);
227     return false;
228   }
229 
230   // Otherwise, continue the animation.
231   return true;
232 }
233 
IsOverscrollAnimationRunning() const234 bool Axis::IsOverscrollAnimationRunning() const {
235   return !mMSDModel.IsFinished(1.0);
236 }
237 
IsOverscrollAnimationAlive() const238 bool Axis::IsOverscrollAnimationAlive() const {
239   // Unlike IsOverscrollAnimationRunning, check the position and the velocity to
240   // be sure that the animation has started but hasn't yet finished.
241   return mMSDModel.GetPosition() != 0.0 || mMSDModel.GetVelocity() != 0.0;
242 }
243 
IsOverscrolled() const244 bool Axis::IsOverscrolled() const { return mOverscroll != 0.f; }
245 
IsScrolledToStart() const246 bool Axis::IsScrolledToStart() const {
247   return FuzzyEqualsCoordinate(GetOrigin().value, GetPageStart().value);
248 }
249 
IsScrolledToEnd() const250 bool Axis::IsScrolledToEnd() const {
251   return FuzzyEqualsCoordinate(GetCompositionEnd().value, GetPageEnd().value);
252 }
253 
IsInInvalidOverscroll() const254 bool Axis::IsInInvalidOverscroll() const {
255   if (mOverscroll > 0) {
256     return !IsScrolledToEnd();
257   } else if (mOverscroll < 0) {
258     return !IsScrolledToStart();
259   }
260   return false;
261 }
262 
ClearOverscroll()263 void Axis::ClearOverscroll() {
264   EndOverscrollAnimation();
265   mOverscroll = 0;
266 }
267 
PanStart() const268 ParentLayerCoord Axis::PanStart() const { return mStartPos; }
269 
PanDistance() const270 ParentLayerCoord Axis::PanDistance() const { return fabs(mPos - mStartPos); }
271 
PanDistance(ParentLayerCoord aPos) const272 ParentLayerCoord Axis::PanDistance(ParentLayerCoord aPos) const {
273   return fabs(aPos - mStartPos);
274 }
275 
EndTouch(TimeStamp aTimestamp)276 void Axis::EndTouch(TimeStamp aTimestamp) {
277   // mVelocityQueue is controller-thread only
278   APZThreadUtils::AssertOnControllerThread();
279 
280   // If the velocity tracker wasn't able to compute a velocity, zero out
281   // the velocity to make sure we don't get a fling based on some old and
282   // no-longer-relevant value of mVelocity. Also if the axis is locked then
283   // just reset the velocity to 0 since we don't need any velocity to carry
284   // into the fling.
285   if (mAxisLocked) {
286     DoSetVelocity(0);
287   } else if (Maybe<float> velocity =
288                  mVelocityTracker->ComputeVelocity(aTimestamp)) {
289     DoSetVelocity(*velocity);
290   } else {
291     DoSetVelocity(0);
292   }
293   mAxisLocked = false;
294   AXIS_LOG("%p|%s ending touch, computed velocity %f\n",
295            mAsyncPanZoomController, Name(), DoGetVelocity());
296 }
297 
CancelGesture()298 void Axis::CancelGesture() {
299   // mVelocityQueue is controller-thread only
300   APZThreadUtils::AssertOnControllerThread();
301 
302   AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n",
303            mAsyncPanZoomController, Name());
304   DoSetVelocity(0.0f);
305   mVelocityTracker->Clear();
306 }
307 
CanScroll() const308 bool Axis::CanScroll() const {
309   return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
310 }
311 
CanScroll(ParentLayerCoord aDelta) const312 bool Axis::CanScroll(ParentLayerCoord aDelta) const {
313   if (!CanScroll() || mAxisLocked) {
314     return false;
315   }
316 
317   return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) >
318          COORDINATE_EPSILON;
319 }
320 
ClampOriginToScrollableRect(CSSCoord aOrigin) const321 CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const {
322   CSSToParentLayerScale zoom = GetAxisScale(GetFrameMetrics().GetZoom());
323   ParentLayerCoord origin = aOrigin * zoom;
324   ParentLayerCoord result;
325   if (origin < GetPageStart()) {
326     result = GetPageStart();
327   } else if (origin + GetCompositionLength() > GetPageEnd()) {
328     result = GetPageEnd() - GetCompositionLength();
329   } else {
330     return aOrigin;
331   }
332   if (zoom == CSSToParentLayerScale(0)) {
333     return aOrigin;
334   }
335   return result / zoom;
336 }
337 
CanScrollNow() const338 bool Axis::CanScrollNow() const { return !mAxisLocked && CanScroll(); }
339 
DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const340 ParentLayerCoord Axis::DisplacementWillOverscrollAmount(
341     ParentLayerCoord aDisplacement) const {
342   ParentLayerCoord newOrigin = GetOrigin() + aDisplacement;
343   ParentLayerCoord newCompositionEnd = GetCompositionEnd() + aDisplacement;
344   // If the current pan plus a displacement takes the window to the left of or
345   // above the current page rect.
346   bool minus = newOrigin < GetPageStart();
347   // If the current pan plus a displacement takes the window to the right of or
348   // below the current page rect.
349   bool plus = newCompositionEnd > GetPageEnd();
350   if (minus && plus) {
351     // Don't handle overscrolled in both directions; a displacement can't cause
352     // this, it must have already been zoomed out too far.
353     return 0;
354   }
355   if (minus) {
356     return newOrigin - GetPageStart();
357   }
358   if (plus) {
359     return newCompositionEnd - GetPageEnd();
360   }
361   return 0;
362 }
363 
ScaleWillOverscrollAmount(float aScale,CSSCoord aFocus) const364 CSSCoord Axis::ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const {
365   // Internally, do computations in ParentLayer coordinates *before* the scale
366   // is applied.
367   CSSToParentLayerScale zoom = GetAxisScale(GetFrameMetrics().GetZoom());
368   ParentLayerCoord focus = aFocus * zoom;
369   ParentLayerCoord originAfterScale = (GetOrigin() + focus) - (focus / aScale);
370 
371   bool both = ScaleWillOverscrollBothSides(aScale);
372   bool minus = GetPageStart() - originAfterScale > COORDINATE_EPSILON;
373   bool plus =
374       (originAfterScale + (GetCompositionLength() / aScale)) - GetPageEnd() >
375       COORDINATE_EPSILON;
376 
377   if ((minus && plus) || both) {
378     // If we ever reach here it's a bug in the client code.
379     MOZ_ASSERT(false,
380                "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount");
381     return 0;
382   }
383   if (minus && zoom != CSSToParentLayerScale(0)) {
384     return (originAfterScale - GetPageStart()) / zoom;
385   }
386   if (plus && zoom != CSSToParentLayerScale(0)) {
387     return (originAfterScale + (GetCompositionLength() / aScale) -
388             GetPageEnd()) /
389            zoom;
390   }
391   return 0;
392 }
393 
IsAxisLocked() const394 bool Axis::IsAxisLocked() const { return mAxisLocked; }
395 
GetVelocity() const396 float Axis::GetVelocity() const { return mAxisLocked ? 0 : DoGetVelocity(); }
397 
SetVelocity(float aVelocity)398 void Axis::SetVelocity(float aVelocity) {
399   AXIS_LOG("%p|%s direct-setting velocity to %f\n", mAsyncPanZoomController,
400            Name(), aVelocity);
401   DoSetVelocity(aVelocity);
402 }
403 
GetCompositionEnd() const404 ParentLayerCoord Axis::GetCompositionEnd() const {
405   return GetOrigin() + GetCompositionLength();
406 }
407 
GetPageEnd() const408 ParentLayerCoord Axis::GetPageEnd() const {
409   return GetPageStart() + GetPageLength();
410 }
411 
GetScrollRangeEnd() const412 ParentLayerCoord Axis::GetScrollRangeEnd() const {
413   return GetPageEnd() - GetCompositionLength();
414 }
415 
GetOrigin() const416 ParentLayerCoord Axis::GetOrigin() const {
417   ParentLayerPoint origin =
418       GetFrameMetrics().GetVisualScrollOffset() * GetFrameMetrics().GetZoom();
419   return GetPointOffset(origin);
420 }
421 
GetCompositionLength() const422 ParentLayerCoord Axis::GetCompositionLength() const {
423   return GetRectLength(GetFrameMetrics().GetCompositionBounds());
424 }
425 
GetPageStart() const426 ParentLayerCoord Axis::GetPageStart() const {
427   ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() *
428                              GetFrameMetrics().GetZoom();
429   return GetRectOffset(pageRect);
430 }
431 
GetPageLength() const432 ParentLayerCoord Axis::GetPageLength() const {
433   ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() *
434                              GetFrameMetrics().GetZoom();
435   return GetRectLength(pageRect);
436 }
437 
ScaleWillOverscrollBothSides(float aScale) const438 bool Axis::ScaleWillOverscrollBothSides(float aScale) const {
439   const FrameMetrics& metrics = GetFrameMetrics();
440   ParentLayerRect screenCompositionBounds =
441       metrics.GetCompositionBounds() / ParentLayerToParentLayerScale(aScale);
442   return GetRectLength(screenCompositionBounds) - GetPageLength() >
443          COORDINATE_EPSILON;
444 }
445 
DoGetVelocity() const446 float Axis::DoGetVelocity() const {
447   auto velocity = mVelocity.Lock();
448   return velocity.ref();
449 }
DoSetVelocity(float aVelocity)450 void Axis::DoSetVelocity(float aVelocity) {
451   auto velocity = mVelocity.Lock();
452   velocity.ref() = aVelocity;
453 }
454 
GetFrameMetrics() const455 const FrameMetrics& Axis::GetFrameMetrics() const {
456   return mAsyncPanZoomController->GetFrameMetrics();
457 }
458 
GetScrollMetadata() const459 const ScrollMetadata& Axis::GetScrollMetadata() const {
460   return mAsyncPanZoomController->GetScrollMetadata();
461 }
462 
OverscrollBehaviorAllowsHandoff() const463 bool Axis::OverscrollBehaviorAllowsHandoff() const {
464   // Scroll handoff is a "non-local" overscroll behavior, so it's allowed
465   // with "auto" and disallowed with "contain" and "none".
466   return GetOverscrollBehavior() == OverscrollBehavior::Auto;
467 }
468 
OverscrollBehaviorAllowsOverscrollEffect() const469 bool Axis::OverscrollBehaviorAllowsOverscrollEffect() const {
470   // An overscroll effect is a "local" overscroll behavior, so it's allowed
471   // with "auto" and "contain" and disallowed with "none".
472   return GetOverscrollBehavior() != OverscrollBehavior::None;
473 }
474 
AxisX(AsyncPanZoomController * aAsyncPanZoomController)475 AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
476     : Axis(aAsyncPanZoomController) {}
477 
GetPointOffset(const CSSPoint & aPoint) const478 CSSCoord AxisX::GetPointOffset(const CSSPoint& aPoint) const {
479   return aPoint.x;
480 }
481 
GetPointOffset(const ParentLayerPoint & aPoint) const482 ParentLayerCoord AxisX::GetPointOffset(const ParentLayerPoint& aPoint) const {
483   return aPoint.x;
484 }
485 
GetAxisScale(const CSSToParentLayerScale2D & aScale) const486 CSSToParentLayerScale AxisX::GetAxisScale(
487     const CSSToParentLayerScale2D& aScale) const {
488   return CSSToParentLayerScale(aScale.xScale);
489 }
490 
GetRectLength(const ParentLayerRect & aRect) const491 ParentLayerCoord AxisX::GetRectLength(const ParentLayerRect& aRect) const {
492   return aRect.Width();
493 }
494 
GetRectOffset(const ParentLayerRect & aRect) const495 ParentLayerCoord AxisX::GetRectOffset(const ParentLayerRect& aRect) const {
496   return aRect.X();
497 }
498 
GetTransformScale(const AsyncTransformComponentMatrix & aMatrix) const499 float AxisX::GetTransformScale(
500     const AsyncTransformComponentMatrix& aMatrix) const {
501   return aMatrix._11;
502 }
503 
GetTransformTranslation(const AsyncTransformComponentMatrix & aMatrix) const504 ParentLayerCoord AxisX::GetTransformTranslation(
505     const AsyncTransformComponentMatrix& aMatrix) const {
506   return aMatrix._41;
507 }
508 
PostScale(AsyncTransformComponentMatrix & aMatrix,float aScale) const509 void AxisX::PostScale(AsyncTransformComponentMatrix& aMatrix,
510                       float aScale) const {
511   aMatrix.PostScale(aScale, 1.f, 1.f);
512 }
513 
PostTranslate(AsyncTransformComponentMatrix & aMatrix,ParentLayerCoord aTranslation) const514 void AxisX::PostTranslate(AsyncTransformComponentMatrix& aMatrix,
515                           ParentLayerCoord aTranslation) const {
516   aMatrix.PostTranslate(aTranslation, 0, 0);
517 }
518 
MakePoint(ScreenCoord aCoord) const519 ScreenPoint AxisX::MakePoint(ScreenCoord aCoord) const {
520   return ScreenPoint(aCoord, 0);
521 }
522 
Name() const523 const char* AxisX::Name() const { return "X"; }
524 
CanScrollTo(Side aSide) const525 bool AxisX::CanScrollTo(Side aSide) const {
526   switch (aSide) {
527     case eSideLeft:
528       return CanScroll(-COORDINATE_EPSILON * 2);
529     case eSideRight:
530       return CanScroll(COORDINATE_EPSILON * 2);
531     default:
532       MOZ_ASSERT_UNREACHABLE("aSide is out of valid values");
533       return false;
534   }
535 }
536 
ScrollableDirections() const537 SideBits AxisX::ScrollableDirections() const {
538   SideBits directions = SideBits::eNone;
539 
540   if (CanScrollTo(eSideLeft)) {
541     directions |= SideBits::eLeft;
542   }
543   if (CanScrollTo(eSideRight)) {
544     directions |= SideBits::eRight;
545   }
546 
547   return directions;
548 }
549 
GetOverscrollBehavior() const550 OverscrollBehavior AxisX::GetOverscrollBehavior() const {
551   return GetScrollMetadata().GetOverscrollBehavior().mBehaviorX;
552 }
553 
AxisY(AsyncPanZoomController * aAsyncPanZoomController)554 AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
555     : Axis(aAsyncPanZoomController) {}
556 
GetPointOffset(const CSSPoint & aPoint) const557 CSSCoord AxisY::GetPointOffset(const CSSPoint& aPoint) const {
558   return aPoint.y;
559 }
560 
GetPointOffset(const ParentLayerPoint & aPoint) const561 ParentLayerCoord AxisY::GetPointOffset(const ParentLayerPoint& aPoint) const {
562   return aPoint.y;
563 }
564 
GetAxisScale(const CSSToParentLayerScale2D & aScale) const565 CSSToParentLayerScale AxisY::GetAxisScale(
566     const CSSToParentLayerScale2D& aScale) const {
567   return CSSToParentLayerScale(aScale.yScale);
568 }
569 
GetRectLength(const ParentLayerRect & aRect) const570 ParentLayerCoord AxisY::GetRectLength(const ParentLayerRect& aRect) const {
571   return aRect.Height();
572 }
573 
GetRectOffset(const ParentLayerRect & aRect) const574 ParentLayerCoord AxisY::GetRectOffset(const ParentLayerRect& aRect) const {
575   return aRect.Y();
576 }
577 
GetTransformScale(const AsyncTransformComponentMatrix & aMatrix) const578 float AxisY::GetTransformScale(
579     const AsyncTransformComponentMatrix& aMatrix) const {
580   return aMatrix._22;
581 }
582 
GetTransformTranslation(const AsyncTransformComponentMatrix & aMatrix) const583 ParentLayerCoord AxisY::GetTransformTranslation(
584     const AsyncTransformComponentMatrix& aMatrix) const {
585   return aMatrix._42;
586 }
587 
PostScale(AsyncTransformComponentMatrix & aMatrix,float aScale) const588 void AxisY::PostScale(AsyncTransformComponentMatrix& aMatrix,
589                       float aScale) const {
590   aMatrix.PostScale(1.f, aScale, 1.f);
591 }
592 
PostTranslate(AsyncTransformComponentMatrix & aMatrix,ParentLayerCoord aTranslation) const593 void AxisY::PostTranslate(AsyncTransformComponentMatrix& aMatrix,
594                           ParentLayerCoord aTranslation) const {
595   aMatrix.PostTranslate(0, aTranslation, 0);
596 }
597 
MakePoint(ScreenCoord aCoord) const598 ScreenPoint AxisY::MakePoint(ScreenCoord aCoord) const {
599   return ScreenPoint(0, aCoord);
600 }
601 
Name() const602 const char* AxisY::Name() const { return "Y"; }
603 
CanScrollTo(Side aSide) const604 bool AxisY::CanScrollTo(Side aSide) const {
605   switch (aSide) {
606     case eSideTop:
607       return CanScroll(-COORDINATE_EPSILON * 2);
608     case eSideBottom:
609       return CanScroll(COORDINATE_EPSILON * 2);
610     default:
611       MOZ_ASSERT_UNREACHABLE("aSide is out of valid values");
612       return false;
613   }
614 }
615 
ScrollableDirections() const616 SideBits AxisY::ScrollableDirections() const {
617   SideBits directions = SideBits::eNone;
618 
619   if (CanScrollTo(eSideTop)) {
620     directions |= SideBits::eTop;
621   }
622   if (CanScrollTo(eSideBottom)) {
623     directions |= SideBits::eBottom;
624   }
625 
626   return directions;
627 }
628 
HasDynamicToolbar() const629 bool AxisY::HasDynamicToolbar() const {
630   return GetCompositionLengthWithoutDynamicToolbar() != ParentLayerCoord(0);
631 }
632 
ScrollableDirectionsWithDynamicToolbar(const ScreenMargin & aFixedLayerMargins) const633 SideBits AxisY::ScrollableDirectionsWithDynamicToolbar(
634     const ScreenMargin& aFixedLayerMargins) const {
635   MOZ_ASSERT(mAsyncPanZoomController->IsRootContent());
636 
637   SideBits directions = ScrollableDirections();
638 
639   if (HasDynamicToolbar()) {
640     ScreenCoord toolbarHeight = ViewAs<ScreenPixel>(
641         GetCompositionLength() - GetCompositionLengthWithoutDynamicToolbar(),
642         PixelCastJustification::ScreenIsParentLayerForRoot);
643 
644     if (fabs(aFixedLayerMargins.bottom) > COORDINATE_EPSILON) {
645       directions |= SideBits::eTop;
646     }
647     if (toolbarHeight + aFixedLayerMargins.bottom > COORDINATE_EPSILON) {
648       directions |= SideBits::eBottom;
649     }
650   }
651 
652   return directions;
653 }
654 
CanVerticalScrollWithDynamicToolbar() const655 bool AxisY::CanVerticalScrollWithDynamicToolbar() const {
656   return !HasDynamicToolbar()
657              ? CanScroll()
658              : GetPageLength() - GetCompositionLengthWithoutDynamicToolbar() >
659                    COORDINATE_EPSILON;
660 }
661 
GetOverscrollBehavior() const662 OverscrollBehavior AxisY::GetOverscrollBehavior() const {
663   return GetScrollMetadata().GetOverscrollBehavior().mBehaviorY;
664 }
665 
GetCompositionLengthWithoutDynamicToolbar() const666 ParentLayerCoord AxisY::GetCompositionLengthWithoutDynamicToolbar() const {
667   return GetFrameMetrics().GetCompositionSizeWithoutDynamicToolbar().Height();
668 }
669 
670 }  // namespace layers
671 }  // namespace mozilla
672