LCOV - code coverage report
Current view: top level - gfx/layers/apz/src - AsyncPanZoomController.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 403 1842 21.9 %
Date: 2017-07-14 16:53:18 Functions: 43 168 25.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=8 et 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 <math.h>                       // for fabsf, fabs, atan2
       8             : #include <stdint.h>                     // for uint32_t, uint64_t
       9             : #include <sys/types.h>                  // for int32_t
      10             : #include <algorithm>                    // for max, min
      11             : #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
      12             : #include "Axis.h"                       // for AxisX, AxisY, Axis, etc
      13             : #include "CheckerboardEvent.h"          // for CheckerboardEvent
      14             : #include "Compositor.h"                 // for Compositor
      15             : #include "FrameMetrics.h"               // for FrameMetrics, etc
      16             : #include "GenericFlingAnimation.h"      // for GenericFlingAnimation
      17             : #include "GestureEventListener.h"       // for GestureEventListener
      18             : #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
      19             : #include "InputData.h"                  // for MultiTouchInput, etc
      20             : #include "InputBlockState.h"            // for InputBlockState, TouchBlockState
      21             : #include "InputQueue.h"                 // for InputQueue
      22             : #include "Overscroll.h"                 // for OverscrollAnimation
      23             : #include "OverscrollHandoffState.h"     // for OverscrollHandoffState
      24             : #include "Units.h"                      // for CSSRect, CSSPoint, etc
      25             : #include "UnitTransforms.h"             // for TransformTo
      26             : #include "base/message_loop.h"          // for MessageLoop
      27             : #include "base/task.h"                  // for NewRunnableMethod, etc
      28             : #include "gfxPrefs.h"                   // for gfxPrefs
      29             : #include "gfxTypes.h"                   // for gfxFloat
      30             : #include "LayersLogging.h"              // for print_stderr
      31             : #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
      32             : #include "mozilla/BasicEvents.h"        // for Modifiers, MODIFIER_*
      33             : #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
      34             : #include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
      35             : #include "mozilla/EventForwards.h"      // for nsEventStatus_*
      36             : #include "mozilla/EventStateManager.h"  // for EventStateManager
      37             : #include "mozilla/MouseEvents.h"        // for WidgetWheelEvent
      38             : #include "mozilla/Preferences.h"        // for Preferences
      39             : #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
      40             : #include "mozilla/RefPtr.h"             // for RefPtr
      41             : #include "mozilla/StaticPtr.h"          // for StaticAutoPtr
      42             : #include "mozilla/Telemetry.h"          // for Telemetry
      43             : #include "mozilla/TimeStamp.h"          // for TimeDuration, TimeStamp
      44             : #include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
      45             :              // note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
      46             : #include "mozilla/dom/Touch.h"          // for Touch
      47             : #include "mozilla/gfx/BasePoint.h"      // for BasePoint
      48             : #include "mozilla/gfx/BaseRect.h"       // for BaseRect
      49             : #include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
      50             : #include "mozilla/gfx/Rect.h"           // for RoundedIn
      51             : #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
      52             : #include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
      53             : #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread, etc
      54             : #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
      55             : #include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
      56             : #include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
      57             : #include "mozilla/layers/CompositorController.h" // for CompositorController
      58             : #include "mozilla/layers/DirectionUtils.h"   // for GetAxis{Start,End,Length,Scale}
      59             : #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
      60             : #include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
      61             : #include "mozilla/layers/ScrollInputMethods.h" // for ScrollInputMethod
      62             : #include "mozilla/mozalloc.h"           // for operator new, etc
      63             : #include "mozilla/Unused.h"             // for unused
      64             : #include "mozilla/FloatingPoint.h"      // for FuzzyEquals*
      65             : #include "nsAlgorithm.h"                // for clamped
      66             : #include "nsCOMPtr.h"                   // for already_AddRefed
      67             : #include "nsDebug.h"                    // for NS_WARNING
      68             : #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
      69             : #include "nsMathUtils.h"                // for NS_hypot
      70             : #include "nsPoint.h"                    // for nsIntPoint
      71             : #include "nsStyleConsts.h"
      72             : #include "nsStyleStruct.h"              // for nsTimingFunction
      73             : #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
      74             : #include "nsThreadUtils.h"              // for NS_IsMainThread
      75             : #include "nsViewportInfo.h"             // for kViewportMinScale, kViewportMaxScale
      76             : #include "prsystem.h"                   // for PR_GetPhysicalMemorySize
      77             : #include "SharedMemoryBasic.h"          // for SharedMemoryBasic
      78             : #include "ScrollSnap.h"                 // for ScrollSnapUtils
      79             : #include "WheelScrollAnimation.h"
      80             : #include "KeyboardScrollAnimation.h"
      81             : #if defined(MOZ_WIDGET_ANDROID)
      82             : #include "AndroidAPZ.h"
      83             : #include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
      84             : #endif // defined(MOZ_WIDGET_ANDROID)
      85             : 
      86             : #define ENABLE_APZC_LOGGING 0
      87             : // #define ENABLE_APZC_LOGGING 1
      88             : 
      89             : #if ENABLE_APZC_LOGGING
      90             : #  define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
      91             : #  define APZC_LOG_FM(fm, prefix, ...) \
      92             :     { std::stringstream ss; \
      93             :       ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
      94             :       AppendToString(ss, fm, ":", "", true); \
      95             :       APZC_LOG("%s\n", ss.str().c_str()); \
      96             :     }
      97             : #else
      98             : #  define APZC_LOG(...)
      99             : #  define APZC_LOG_FM(fm, prefix, ...)
     100             : #endif
     101             : 
     102             : namespace mozilla {
     103             : namespace layers {
     104             : 
     105             : typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
     106             : typedef GeckoContentController::APZStateChange APZStateChange;
     107             : typedef GeckoContentController::TapType TapType;
     108             : typedef mozilla::gfx::Point Point;
     109             : typedef mozilla::gfx::Matrix4x4 Matrix4x4;
     110             : using mozilla::gfx::PointTyped;
     111             : 
     112             : // Choose between platform-specific implementations.
     113             : #ifdef MOZ_WIDGET_ANDROID
     114             : typedef WidgetOverscrollEffect OverscrollEffect;
     115             : typedef AndroidSpecificState PlatformSpecificState;
     116             : typedef AndroidFlingAnimation FlingAnimation;
     117             : #else
     118             : typedef GenericOverscrollEffect OverscrollEffect;
     119             : typedef PlatformSpecificStateBase PlatformSpecificState;  // no extra state, just use the base class
     120             : typedef GenericFlingAnimation FlingAnimation;
     121             : #endif
     122             : 
     123             : /**
     124             :  * \page APZCPrefs APZ preferences
     125             :  *
     126             :  * The following prefs are used to control the behaviour of the APZC.
     127             :  * The default values are provided in gfxPrefs.h.
     128             :  *
     129             :  * \li\b apz.allow_checkerboarding
     130             :  * Pref that allows or disallows checkerboarding
     131             :  *
     132             :  * \li\b apz.allow_immediate_handoff
     133             :  * If set to true, scroll can be handed off from one APZC to another within
     134             :  * a single input block. If set to false, a single input block can only
     135             :  * scroll one APZC.
     136             :  *
     137             :  * \li\b apz.axis_lock.mode
     138             :  * The preferred axis locking style. See AxisLockMode for possible values.
     139             :  *
     140             :  * \li\b apz.axis_lock.lock_angle
     141             :  * Angle from axis within which we stay axis-locked.\n
     142             :  * Units: radians
     143             :  *
     144             :  * \li\b apz.axis_lock.breakout_threshold
     145             :  * Distance in inches the user must pan before axis lock can be broken.\n
     146             :  * Units: (real-world, i.e. screen) inches
     147             :  *
     148             :  * \li\b apz.axis_lock.breakout_angle
     149             :  * Angle at which axis lock can be broken.\n
     150             :  * Units: radians
     151             :  *
     152             :  * \li\b apz.axis_lock.direct_pan_angle
     153             :  * If the angle from an axis to the line drawn by a pan move is less than
     154             :  * this value, we can assume that panning can be done in the allowed direction
     155             :  * (horizontal or vertical).\n
     156             :  * Currently used only for touch-action css property stuff and was addded to
     157             :  * keep behaviour consistent with IE.\n
     158             :  * Units: radians
     159             :  *
     160             :  * \li\b apz.content_response_timeout
     161             :  * Amount of time before we timeout response from content. For example, if
     162             :  * content is being unruly/slow and we don't get a response back within this
     163             :  * time, we will just pretend that content did not preventDefault any touch
     164             :  * events we dispatched to it.\n
     165             :  * Units: milliseconds
     166             :  *
     167             :  * \li\b apz.danger_zone_x
     168             :  * \li\b apz.danger_zone_y
     169             :  * When drawing high-res tiles, we drop down to drawing low-res tiles
     170             :  * when we know we can't keep up with the scrolling. The way we determine
     171             :  * this is by checking if we are entering the "danger zone", which is the
     172             :  * boundary of the painted content. For example, if the painted content
     173             :  * goes from y=0...1000 and the visible portion is y=250...750 then
     174             :  * we're far from checkerboarding. If we get to y=490...990 though then we're
     175             :  * only 10 pixels away from showing checkerboarding so we are probably in
     176             :  * a state where we can't keep up with scrolling. The danger zone prefs specify
     177             :  * how wide this margin is; in the above example a y-axis danger zone of 10
     178             :  * pixels would make us drop to low-res at y=490...990.\n
     179             :  * This value is in layer pixels.
     180             :  *
     181             :  * \li\b apz.disable_for_scroll_linked_effects
     182             :  * Setting this pref to true will disable APZ scrolling on documents where
     183             :  * scroll-linked effects are detected. A scroll linked effect is detected if
     184             :  * positioning or transform properties are updated inside a scroll event
     185             :  * dispatch; we assume that such an update is in response to the scroll event
     186             :  * and is therefore a scroll-linked effect which will be laggy with APZ
     187             :  * scrolling.
     188             :  *
     189             :  * \li\b apz.displayport_expiry_ms
     190             :  * While a scrollable frame is scrolling async, we set a displayport on it
     191             :  * to make sure it is layerized. However this takes up memory, so once the
     192             :  * scrolling stops we want to remove the displayport. This pref controls how
     193             :  * long after scrolling stops the displayport is removed. A value of 0 will
     194             :  * disable the expiry behavior entirely.
     195             :  * Units: milliseconds
     196             :  *
     197             :  * \li\b apz.enlarge_displayport_when_clipped
     198             :  * Pref that enables enlarging of the displayport along one axis when the
     199             :  * generated displayport's size is beyond that of the scrollable rect on the
     200             :  * opposite axis.
     201             :  *
     202             :  * \li\b apz.fling_accel_interval_ms
     203             :  * The time that determines whether a second fling will be treated as
     204             :  * accelerated. If two flings are started within this interval, the second one
     205             :  * will be accelerated. Setting an interval of 0 means that acceleration will
     206             :  * be disabled.\n
     207             :  * Units: milliseconds
     208             :  *
     209             :  * \li\b apz.fling_accel_min_velocity
     210             :  * The minimum velocity of the second fling for it to be considered for fling
     211             :  * acceleration.
     212             :  * Units: screen pixels per milliseconds
     213             :  *
     214             :  * \li\b apz.fling_accel_base_mult
     215             :  * \li\b apz.fling_accel_supplemental_mult
     216             :  * When applying an acceleration on a fling, the new computed velocity is
     217             :  * (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
     218             :  * The base_mult and supplemental_mult multiplier values are controlled by
     219             :  * these prefs. Note that "old_velocity" here is the initial velocity of the
     220             :  * previous fling _after_ acceleration was applied to it (if applicable).
     221             :  *
     222             :  * \li\b apz.fling_curve_function_x1
     223             :  * \li\b apz.fling_curve_function_y1
     224             :  * \li\b apz.fling_curve_function_x2
     225             :  * \li\b apz.fling_curve_function_y2
     226             :  * \li\b apz.fling_curve_threshold_inches_per_ms
     227             :  * These five parameters define a Bezier curve function and threshold used to
     228             :  * increase the actual velocity relative to the user's finger velocity. When the
     229             :  * finger velocity is below the threshold (or if the threshold is not positive),
     230             :  * the velocity is used as-is. If the finger velocity exceeds the threshold
     231             :  * velocity, then the function defined by the curve is applied on the part of
     232             :  * the velocity that exceeds the threshold. Note that the upper bound of the
     233             :  * velocity is still specified by the \b apz.max_velocity_inches_per_ms pref, and
     234             :  * the function will smoothly curve the velocity from the threshold to the
     235             :  * max. In general the function parameters chosen should define an ease-out
     236             :  * curve in order to increase the velocity in this range, or an ease-in curve to
     237             :  * decrease the velocity. A straight-line curve is equivalent to disabling the
     238             :  * curve entirely by setting the threshold to -1. The max velocity pref must
     239             :  * also be set in order for the curving to take effect, as it defines the upper
     240             :  * bound of the velocity curve.\n
     241             :  * The points (x1, y1) and (x2, y2) used as the two intermediate control points
     242             :  * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
     243             :  * Some example values for these prefs can be found at\n
     244             :  * https://dxr.mozilla.org/mozilla-central/rev/70e05c6832e831374604ac3ce7433971368dffe0/layout/style/nsStyleStruct.cpp#2729
     245             :  *
     246             :  * \li\b apz.fling_friction
     247             :  * Amount of friction applied during flings. This is used in the following
     248             :  * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
     249             :  * for a new sample, v(t0) is the velocity at the previous sample, f is the
     250             :  * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
     251             :  * that has elapsed between the two samples.\n
     252             :  * NOTE: Not currently used in Android fling calculations.
     253             :  *
     254             :  * \li\b apz.fling_min_velocity_threshold
     255             :  * Minimum velocity for a fling to actually kick off. If the user pans and lifts
     256             :  * their finger such that the velocity is smaller than this amount, no fling
     257             :  * is initiated.\n
     258             :  * Units: screen pixels per millisecond
     259             :  *
     260             :  * \li\b apz.fling_stop_on_tap_threshold
     261             :  * When flinging, if the velocity is above this number, then a tap on the
     262             :  * screen will stop the fling without dispatching a tap to content. If the
     263             :  * velocity is below this threshold a tap will also be dispatched.
     264             :  * Note: when modifying this pref be sure to run the APZC gtests as some of
     265             :  * them depend on the value of this pref.\n
     266             :  * Units: screen pixels per millisecond
     267             :  *
     268             :  * \li\b apz.fling_stopped_threshold
     269             :  * When flinging, if the velocity goes below this number, we just stop the
     270             :  * animation completely. This is to prevent asymptotically approaching 0
     271             :  * velocity and rerendering unnecessarily.\n
     272             :  * Units: screen pixels per millisecond.\n
     273             :  * NOTE: Should not be set to anything
     274             :  * other than 0.0 for Android except for tests to disable flings.
     275             :  *
     276             :  * \li\b apz.frame_delay.enabled
     277             :  * If this is set to true, changes to the async scroll offset and async zoom
     278             :  * will not be immediately reflected in GetCurrentAsyncTransform() when called
     279             :  * with |AsyncTransformConsumer::eForCompositing|. Rather, the transform will
     280             :  * reflect the value of the async scroll offset and async zoom at the last time
     281             :  * SampleCompositedAsyncTransform() was called.
     282             :  *
     283             :  * \li\b apz.max_velocity_inches_per_ms
     284             :  * Maximum velocity.  Velocity will be capped at this value if a faster fling
     285             :  * occurs.  Negative values indicate unlimited velocity.\n
     286             :  * Units: (real-world, i.e. screen) inches per millisecond
     287             :  *
     288             :  * \li\b apz.max_velocity_queue_size
     289             :  * Maximum size of velocity queue. The queue contains last N velocity records.
     290             :  * On touch end we calculate the average velocity in order to compensate
     291             :  * touch/mouse drivers misbehaviour.
     292             :  *
     293             :  * \li\b apz.min_skate_speed
     294             :  * Minimum amount of speed along an axis before we switch to "skate" multipliers
     295             :  * rather than using the "stationary" multipliers.\n
     296             :  * Units: CSS pixels per millisecond
     297             :  *
     298             :  * \li\b apz.one_touch_pinch.enabled
     299             :  * Whether or not the "one-touch-pinch" gesture (for zooming with one finger)
     300             :  * is enabled or not.
     301             :  *
     302             :  * \li\b apz.overscroll.enabled
     303             :  * Pref that enables overscrolling. If this is disabled, excess scroll that
     304             :  * cannot be handed off is discarded.
     305             :  *
     306             :  * \li\b apz.overscroll.min_pan_distance_ratio
     307             :  * The minimum ratio of the pan distance along one axis to the pan distance
     308             :  * along the other axis needed to initiate overscroll along the first axis
     309             :  * during panning.
     310             :  *
     311             :  * \li\b apz.overscroll.stretch_factor
     312             :  * How much overscrolling can stretch content along an axis.
     313             :  * The maximum stretch along an axis is a factor of (1 + kStretchFactor).
     314             :  * (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
     315             :  * is 1, you can stretch at most by a factor of 2).
     316             :  *
     317             :  * \li\b apz.overscroll.spring_stiffness
     318             :  * The stiffness of the spring used in the physics model for the overscroll
     319             :  * animation.
     320             :  *
     321             :  * \li\b apz.overscroll.spring_friction
     322             :  * The friction of the spring used in the physics model for the overscroll
     323             :  * animation.
     324             :  * Even though a realistic physics model would dictate that this be the same
     325             :  * as \b apz.fling_friction, we allow it to be set to be something different,
     326             :  * because in practice we want flings to skate smoothly (low friction), while
     327             :  * we want the overscroll bounce-back to oscillate few times (high friction).
     328             :  *
     329             :  * \li\b apz.overscroll.stop_distance_threshold
     330             :  * \li\b apz.overscroll.stop_velocity_threshold
     331             :  * Thresholds for stopping the overscroll animation. When both the distance
     332             :  * and the velocity fall below their thresholds, we stop oscillating.\n
     333             :  * Units: screen pixels (for distance)
     334             :  *        screen pixels per millisecond (for velocity)
     335             :  *
     336             :  * \li\b apz.paint_skipping.enabled
     337             :  * When APZ is scrolling and sending repaint requests to the main thread, often
     338             :  * the main thread doesn't actually need to do a repaint. This pref allows the
     339             :  * main thread to skip doing those repaints in cases where it doesn't need to.
     340             :  *
     341             :  * \li\b apz.record_checkerboarding
     342             :  * Whether or not to record detailed info on checkerboarding events.
     343             :  *
     344             :  * \li\b apz.test.logging_enabled
     345             :  * Enable logging of APZ test data (see bug 961289).
     346             :  *
     347             :  * \li\b apz.touch_move_tolerance
     348             :  * See the description for apz.touch_start_tolerance below. This is a similar
     349             :  * threshold, except it is used to suppress touchmove events from being delivered
     350             :  * to content for NON-scrollable frames (or more precisely, for APZCs where
     351             :  * ArePointerEventsConsumable returns false).\n
     352             :  * Units: (real-world, i.e. screen) inches
     353             :  *
     354             :  * \li\b apz.touch_start_tolerance
     355             :  * Constant describing the tolerance in distance we use, multiplied by the
     356             :  * device DPI, before we start panning the screen. This is to prevent us from
     357             :  * accidentally processing taps as touch moves, and from very short/accidental
     358             :  * touches moving the screen. touchmove events are also not delivered to content
     359             :  * within this distance on scrollable frames.\n
     360             :  * Units: (real-world, i.e. screen) inches
     361             :  *
     362             :  * \li\b apz.velocity_bias
     363             :  * How much to adjust the displayport in the direction of scrolling. This value
     364             :  * is multiplied by the velocity and added to the displayport offset.
     365             :  *
     366             :  * \li\b apz.velocity_relevance_time_ms
     367             :  * When computing a fling velocity from the most recently stored velocity
     368             :  * information, only velocities within the most X milliseconds are used.
     369             :  * This pref controls the value of X.\n
     370             :  * Units: ms
     371             :  *
     372             :  * \li\b apz.x_skate_size_multiplier
     373             :  * \li\b apz.y_skate_size_multiplier
     374             :  * The multiplier we apply to the displayport size if it is skating (current
     375             :  * velocity is above \b apz.min_skate_speed). We prefer to increase the size of
     376             :  * the Y axis because it is more natural in the case that a user is reading a
     377             :  * page page that scrolls up/down. Note that one, both or neither of these may be
     378             :  * used at any instant.\n
     379             :  * In general we want \b apz.[xy]_skate_size_multiplier to be smaller than the corresponding
     380             :  * stationary size multiplier because when panning fast we would like to paint
     381             :  * less and get faster, more predictable paint times. When panning slowly we
     382             :  * can afford to paint more even though it's slower.
     383             :  *
     384             :  * \li\b apz.x_stationary_size_multiplier
     385             :  * \li\b apz.y_stationary_size_multiplier
     386             :  * The multiplier we apply to the displayport size if it is not skating (see
     387             :  * documentation for the skate size multipliers above).
     388             :  *
     389             :  * \li\b apz.x_skate_highmem_adjust
     390             :  * \li\b apz.y_skate_highmem_adjust
     391             :  * On high memory systems, we adjust the displayport during skating
     392             :  * to be larger so we can reduce checkerboarding.
     393             :  *
     394             :  * \li\b apz.zoom_animation_duration_ms
     395             :  * This controls how long the zoom-to-rect animation takes.\n
     396             :  * Units: ms
     397             :  *
     398             :  * \li\b apz.scale_repaint_delay_ms
     399             :  * How long to delay between repaint requests during a scale.
     400             :  * A negative number prevents repaint requests during a scale.\n
     401             :  * Units: ms
     402             :  *
     403             :  */
     404             : 
     405             : /**
     406             :  * Computed time function used for sampling frames of a zoom to animation.
     407             :  */
     408           3 : StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
     409             : 
     410             : /**
     411             :  * Computed time function used for curving up velocity when it gets high.
     412             :  */
     413             : StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
     414             : 
     415             : /**
     416             :  * The estimated duration of a paint for the purposes of calculating a new
     417             :  * displayport, in milliseconds.
     418             :  */
     419             : static const double kDefaultEstimatedPaintDurationMs = 50;
     420             : 
     421             : /**
     422             :  * Returns true if this is a high memory system and we can use
     423             :  * extra memory for a larger displayport to reduce checkerboarding.
     424             :  */
     425             : static bool gIsHighMemSystem = false;
     426           4 : static bool IsHighMemSystem()
     427             : {
     428           4 :   return gIsHighMemSystem;
     429             : }
     430             : 
     431             : /**
     432             :  * Is aAngle within the given threshold of the horizontal axis?
     433             :  * @param aAngle an angle in radians in the range [0, pi]
     434             :  * @param aThreshold an angle in radians in the range [0, pi/2]
     435             :  */
     436           0 : static bool IsCloseToHorizontal(float aAngle, float aThreshold)
     437             : {
     438           0 :   return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
     439             : }
     440             : 
     441             : // As above, but for the vertical axis.
     442           0 : static bool IsCloseToVertical(float aAngle, float aThreshold)
     443             : {
     444           0 :   return (fabs(aAngle - (M_PI / 2)) < aThreshold);
     445             : }
     446             : 
     447             : // Counter used to give each APZC a unique id
     448             : static uint32_t sAsyncPanZoomControllerCount = 0;
     449             : 
     450             : TimeStamp
     451           4 : AsyncPanZoomController::GetFrameTime() const
     452             : {
     453           4 :   APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
     454           4 :   return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
     455             : }
     456             : 
     457             : class MOZ_STACK_CLASS StateChangeNotificationBlocker {
     458             : public:
     459          40 :   explicit StateChangeNotificationBlocker(AsyncPanZoomController* aApzc)
     460          40 :     : mApzc(aApzc)
     461             :   {
     462          80 :     ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
     463          40 :     mInitialState = mApzc->mState;
     464          40 :     mApzc->mNotificationBlockers++;
     465          40 :   }
     466             : 
     467          40 :   ~StateChangeNotificationBlocker()
     468          40 :   {
     469             :     AsyncPanZoomController::PanZoomState newState;
     470             :     {
     471          80 :       ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
     472          40 :       mApzc->mNotificationBlockers--;
     473          40 :       newState = mApzc->mState;
     474             :     }
     475          40 :     mApzc->DispatchStateChangeNotification(mInitialState, newState);
     476          40 :   }
     477             : 
     478             : private:
     479             :   AsyncPanZoomController* mApzc;
     480             :   AsyncPanZoomController::PanZoomState mInitialState;
     481             : };
     482             : 
     483           0 : class ZoomAnimation: public AsyncPanZoomAnimation {
     484             : public:
     485           0 :   ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale2D aStartZoom,
     486             :                 CSSPoint aEndOffset, CSSToParentLayerScale2D aEndZoom)
     487           0 :     : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
     488             :     , mStartOffset(aStartOffset)
     489             :     , mStartZoom(aStartZoom)
     490             :     , mEndOffset(aEndOffset)
     491           0 :     , mEndZoom(aEndZoom)
     492           0 :   {}
     493             : 
     494           0 :   virtual bool DoSample(FrameMetrics& aFrameMetrics,
     495             :                         const TimeDuration& aDelta) override
     496             :   {
     497           0 :     mDuration += aDelta;
     498           0 :     double animPosition = mDuration / mTotalDuration;
     499             : 
     500           0 :     if (animPosition >= 1.0) {
     501           0 :       aFrameMetrics.SetZoom(mEndZoom);
     502           0 :       aFrameMetrics.SetScrollOffset(mEndOffset);
     503           0 :       return false;
     504             :     }
     505             : 
     506             :     // Sample the zoom at the current time point.  The sampled zoom
     507             :     // will affect the final computed resolution.
     508             :     float sampledPosition =
     509           0 :       gZoomAnimationFunction->GetValue(animPosition,
     510           0 :         ComputedTimingFunction::BeforeFlag::Unset);
     511             : 
     512             :     // We scale the scrollOffset linearly with sampledPosition, so the zoom
     513             :     // needs to scale inversely to match.
     514           0 :     aFrameMetrics.SetZoom(CSSToParentLayerScale2D(
     515           0 :       1 / (sampledPosition / mEndZoom.xScale + (1 - sampledPosition) / mStartZoom.xScale),
     516           0 :       1 / (sampledPosition / mEndZoom.yScale + (1 - sampledPosition) / mStartZoom.yScale)));
     517             : 
     518           0 :     aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
     519           0 :       mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
     520           0 :       mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
     521           0 :     )));
     522             : 
     523           0 :     return true;
     524             :   }
     525             : 
     526           0 :   virtual bool WantsRepaints() override
     527             :   {
     528           0 :     return false;
     529             :   }
     530             : 
     531             : private:
     532             :   TimeDuration mDuration;
     533             :   const TimeDuration mTotalDuration;
     534             : 
     535             :   // Old metrics from before we started a zoom animation. This is only valid
     536             :   // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
     537             :   // interpolate between the start and end frames. We only use the
     538             :   // |mViewportScrollOffset| and |mResolution| fields on this.
     539             :   CSSPoint mStartOffset;
     540             :   CSSToParentLayerScale2D mStartZoom;
     541             : 
     542             :   // Target metrics for a zoom to animation. This is only valid when we are in
     543             :   // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
     544             :   // |mResolution| fields on this.
     545             :   CSSPoint mEndOffset;
     546             :   CSSToParentLayerScale2D mEndZoom;
     547             : };
     548             : 
     549             : 
     550           0 : class SmoothScrollAnimation : public AsyncPanZoomAnimation {
     551             : public:
     552           0 :   SmoothScrollAnimation(AsyncPanZoomController& aApzc,
     553             :                         const nsPoint &aInitialPosition,
     554             :                         const nsPoint &aInitialVelocity,
     555             :                         const nsPoint& aDestination, double aSpringConstant,
     556             :                         double aDampingRatio)
     557           0 :    : mApzc(aApzc)
     558           0 :    , mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
     559             :                  aSpringConstant, aDampingRatio)
     560           0 :    , mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
     561           0 :                  aSpringConstant, aDampingRatio)
     562             :   {
     563           0 :   }
     564             : 
     565             :   /**
     566             :    * Advances a smooth scroll simulation based on the time passed in |aDelta|.
     567             :    * This should be called whenever sampling the content transform for this
     568             :    * frame. Returns true if the smooth scroll should be advanced by one frame,
     569             :    * or false if the smooth scroll has ended.
     570             :    */
     571           0 :   bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override {
     572             :     nsPoint oneParentLayerPixel =
     573           0 :       CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
     574           0 :     if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
     575           0 :         mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
     576             :       // Set the scroll offset to the exact destination. If we allow the scroll
     577             :       // offset to end up being a bit off from the destination, we can get
     578             :       // artefacts like "scroll to the next snap point in this direction"
     579             :       // scrolling to the snap point we're already supposed to be at.
     580             :       aFrameMetrics.SetScrollOffset(
     581           0 :           aFrameMetrics.CalculateScrollRange().ClampPoint(
     582           0 :               CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
     583           0 :                                              mYAxisModel.GetDestination()))));
     584           0 :       return false;
     585             :     }
     586             : 
     587           0 :     mXAxisModel.Simulate(aDelta);
     588           0 :     mYAxisModel.Simulate(aDelta);
     589             : 
     590           0 :     CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
     591           0 :                                                        mYAxisModel.GetPosition()));
     592           0 :     CSSPoint css_velocity = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetVelocity(),
     593           0 :                                                            mYAxisModel.GetVelocity()));
     594             : 
     595             :     // Convert from points/second to points/ms
     596           0 :     ParentLayerPoint velocity = ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
     597             : 
     598             :     // Keep the velocity updated for the Axis class so that any animations
     599             :     // chained off of the smooth scroll will inherit it.
     600           0 :     if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) {
     601           0 :       mApzc.mX.SetVelocity(0);
     602             :     } else {
     603           0 :       mApzc.mX.SetVelocity(velocity.x);
     604             :     }
     605           0 :     if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
     606           0 :       mApzc.mY.SetVelocity(0);
     607             :     } else {
     608           0 :       mApzc.mY.SetVelocity(velocity.y);
     609             :     }
     610             :     // If we overscroll, hand off to a fling animation that will complete the
     611             :     // spring back.
     612           0 :     CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
     613           0 :     ParentLayerPoint displacement = (position - aFrameMetrics.GetScrollOffset()) * zoom;
     614             : 
     615           0 :     ParentLayerPoint overscroll;
     616           0 :     ParentLayerPoint adjustedOffset;
     617           0 :     mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
     618           0 :     mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
     619             : 
     620           0 :     aFrameMetrics.ScrollBy(adjustedOffset / zoom);
     621             : 
     622             :     // The smooth scroll may have caused us to reach the end of our scroll range.
     623             :     // This can happen if either the layout.css.scroll-behavior.damping-ratio
     624             :     // preference is set to less than 1 (underdamped) or if a smooth scroll
     625             :     // inherits velocity from a fling gesture.
     626           0 :     if (!IsZero(overscroll)) {
     627             :       // Hand off a fling with the remaining momentum to the next APZC in the
     628             :       // overscroll handoff chain.
     629             : 
     630             :       // We may have reached the end of the scroll range along one axis but
     631             :       // not the other. In such a case we only want to hand off the relevant
     632             :       // component of the fling.
     633           0 :       if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
     634           0 :         velocity.x = 0;
     635           0 :       } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
     636           0 :         velocity.y = 0;
     637             :       }
     638             : 
     639             :       // To hand off the fling, we attempt to find a target APZC and start a new
     640             :       // fling with the same velocity on that APZC. For simplicity, the actual
     641             :       // overscroll of the current sample is discarded rather than being handed
     642             :       // off. The compositor should sample animations sufficiently frequently
     643             :       // that this is not noticeable. The target APZC is chosen by seeing if
     644             :       // there is an APZC further in the handoff chain which is pannable; if
     645             :       // there isn't, we take the new fling ourselves, entering an overscrolled
     646             :       // state.
     647             :       // Note: APZC is holding mMonitor, so directly calling
     648             :       // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
     649             :       // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
     650             :       // called after mMonitor is released.
     651           0 :       mDeferredTasks.AppendElement(NewRunnableMethod<ParentLayerPoint>(
     652             :         "layers::AsyncPanZoomController::HandleSmoothScrollOverscroll",
     653           0 :         &mApzc,
     654             :         &AsyncPanZoomController::HandleSmoothScrollOverscroll,
     655           0 :         velocity));
     656           0 :       return false;
     657             :     }
     658             : 
     659           0 :     return true;
     660             :   }
     661             : 
     662           0 :   void SetDestination(const nsPoint& aNewDestination) {
     663           0 :     mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
     664           0 :     mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
     665           0 :   }
     666             : 
     667           0 :   CSSPoint GetDestination() const {
     668             :     return CSSPoint::FromAppUnits(
     669           0 :         nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
     670             :   }
     671             : 
     672           0 :   SmoothScrollAnimation* AsSmoothScrollAnimation() override {
     673           0 :     return this;
     674             :   }
     675             : 
     676             : private:
     677             :   AsyncPanZoomController& mApzc;
     678             :   AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
     679             : };
     680             : 
     681             : /*static*/ void
     682           1 : AsyncPanZoomController::InitializeGlobalState()
     683             : {
     684             :   static bool sInitialized = false;
     685           1 :   if (sInitialized)
     686           0 :     return;
     687           1 :   sInitialized = true;
     688             : 
     689           1 :   MOZ_ASSERT(NS_IsMainThread());
     690             : 
     691             :   gZoomAnimationFunction = new ComputedTimingFunction(
     692           2 :     nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
     693           1 :   ClearOnShutdown(&gZoomAnimationFunction);
     694             :   gVelocityCurveFunction = new ComputedTimingFunction(
     695           2 :     nsTimingFunction(gfxPrefs::APZCurveFunctionX1(),
     696             :                      gfxPrefs::APZCurveFunctionY1(),
     697             :                      gfxPrefs::APZCurveFunctionX2(),
     698           2 :                      gfxPrefs::APZCurveFunctionY2()));
     699           1 :   ClearOnShutdown(&gVelocityCurveFunction);
     700             : 
     701           1 :   uint64_t sysmem = PR_GetPhysicalMemorySize();
     702           1 :   uint64_t threshold = 1LL << 32; // 4 GB in bytes
     703           1 :   gIsHighMemSystem = sysmem >= threshold;
     704             : }
     705             : 
     706           2 : AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
     707             :                                                APZCTreeManager* aTreeManager,
     708             :                                                const RefPtr<InputQueue>& aInputQueue,
     709             :                                                GeckoContentController* aGeckoContentController,
     710           2 :                                                GestureBehavior aGestures)
     711             :   :  mLayersId(aLayersId),
     712             :      mGeckoContentController(aGeckoContentController),
     713             :      mRefPtrMonitor("RefPtrMonitor"),
     714             :      // mTreeManager must be initialized before GetFrameTime() is called
     715             :      mTreeManager(aTreeManager),
     716           2 :      mFrameMetrics(mScrollMetadata.GetMetrics()),
     717             :      mMonitor("AsyncPanZoomController"),
     718           2 :      mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
     719             :      mX(this),
     720             :      mY(this),
     721             :      mPanDirRestricted(false),
     722             :      mZoomConstraints(false, false,
     723           4 :         mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1),
     724           4 :         mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)),
     725             :      mLastSampleTime(GetFrameTime()),
     726             :      mLastCheckerboardReport(GetFrameTime()),
     727           4 :      mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
     728             :      mState(NOTHING),
     729             :      mNotificationBlockers(0),
     730             :      mInputQueue(aInputQueue),
     731             :      mPinchPaintTimerSet(false),
     732           2 :      mAPZCId(sAsyncPanZoomControllerCount++),
     733             :      mSharedLock(nullptr),
     734             :      mAsyncTransformAppliedToContent(false),
     735          16 :      mCheckerboardEventLock("APZCBELock")
     736             : {
     737           2 :   if (aGestures == USE_GESTURE_DETECTOR) {
     738           2 :     mGestureEventListener = new GestureEventListener(this);
     739             :   }
     740           2 : }
     741             : 
     742           0 : AsyncPanZoomController::~AsyncPanZoomController()
     743             : {
     744           0 :   MOZ_ASSERT(IsDestroyed());
     745           0 : }
     746             : 
     747             : PlatformSpecificStateBase*
     748           0 : AsyncPanZoomController::GetPlatformSpecificState()
     749             : {
     750           0 :   if (!mPlatformSpecificState) {
     751           0 :     mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
     752             :   }
     753           0 :   return mPlatformSpecificState.get();
     754             : }
     755             : 
     756             : already_AddRefed<GeckoContentController>
     757          48 : AsyncPanZoomController::GetGeckoContentController() const {
     758          96 :   MonitorAutoLock lock(mRefPtrMonitor);
     759          96 :   RefPtr<GeckoContentController> controller = mGeckoContentController;
     760          96 :   return controller.forget();
     761             : }
     762             : 
     763             : already_AddRefed<GestureEventListener>
     764           0 : AsyncPanZoomController::GetGestureEventListener() const {
     765           0 :   MonitorAutoLock lock(mRefPtrMonitor);
     766           0 :   RefPtr<GestureEventListener> listener = mGestureEventListener;
     767           0 :   return listener.forget();
     768             : }
     769             : 
     770             : const RefPtr<InputQueue>&
     771           0 : AsyncPanZoomController::GetInputQueue() const {
     772           0 :   return mInputQueue;
     773             : }
     774             : 
     775             : void
     776           0 : AsyncPanZoomController::Destroy()
     777             : {
     778           0 :   APZThreadUtils::AssertOnCompositorThread();
     779             : 
     780           0 :   CancelAnimation(CancelAnimationFlags::ScrollSnap);
     781             : 
     782             :   { // scope the lock
     783           0 :     MonitorAutoLock lock(mRefPtrMonitor);
     784           0 :     mGeckoContentController = nullptr;
     785           0 :     mGestureEventListener = nullptr;
     786             :   }
     787           0 :   mParent = nullptr;
     788           0 :   mTreeManager = nullptr;
     789             : 
     790             :   // Only send the release message if the SharedFrameMetrics has been created.
     791           0 :   if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
     792           0 :     Unused << mMetricsSharingController->StopSharingMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
     793             :   }
     794             : 
     795             :   { // scope the lock
     796           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
     797           0 :     mSharedFrameMetricsBuffer = nullptr;
     798           0 :     delete mSharedLock;
     799           0 :     mSharedLock = nullptr;
     800             :   }
     801           0 : }
     802             : 
     803             : bool
     804          32 : AsyncPanZoomController::IsDestroyed() const
     805             : {
     806          32 :   return mTreeManager == nullptr;
     807             : }
     808             : 
     809             : /* static */ScreenCoord
     810           0 : AsyncPanZoomController::GetTouchStartTolerance()
     811             : {
     812           0 :   return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
     813             : }
     814             : 
     815           0 : /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
     816             : {
     817           0 :   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
     818             : }
     819             : 
     820             : bool
     821           0 : AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
     822           0 :   if (aTouchPoints == 0) {
     823             :     // Cant' do anything with zero touch points
     824           0 :     return false;
     825             :   }
     826             : 
     827             :   // This logic is simplified, erring on the side of returning true
     828             :   // if we're not sure. It's safer to pretend that we can consume the
     829             :   // event and then not be able to than vice-versa.
     830             :   // We could probably enhance this logic to determine things like "we're
     831             :   // not pannable, so we can only zoom in, and the zoom is already maxed
     832             :   // out, so we're not zoomable either" but no need for that at this point.
     833             : 
     834           0 :   bool pannable = aBlock->GetOverscrollHandoffChain()->CanBePanned(this);
     835           0 :   bool zoomable = mZoomConstraints.mAllowZoom;
     836             : 
     837           0 :   pannable &= (aBlock->TouchActionAllowsPanningX() || aBlock->TouchActionAllowsPanningY());
     838           0 :   zoomable &= (aBlock->TouchActionAllowsPinchZoom());
     839             : 
     840             :   // XXX once we fix bug 1031443, consumable should be assigned
     841             :   // pannable || zoomable if aTouchPoints > 1.
     842           0 :   bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
     843           0 :   if (!consumable) {
     844           0 :     return false;
     845             :   }
     846             : 
     847           0 :   return true;
     848             : }
     849             : 
     850           0 : nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
     851             :                                                       const AsyncDragMetrics& aDragMetrics,
     852             :                                                       CSSCoord aInitialThumbPos)
     853             : {
     854           0 :   if (!gfxPrefs::APZDragEnabled()) {
     855           0 :     return nsEventStatus_eIgnore;
     856             :   }
     857             : 
     858           0 :   if (!GetApzcTreeManager()) {
     859           0 :     return nsEventStatus_eConsumeNoDefault;
     860             :   }
     861             : 
     862           0 :   if (aEvent.mType == MouseInput::MouseType::MOUSE_UP) {
     863           0 :     ScrollSnap();
     864             :   }
     865             : 
     866           0 :   if (aEvent.mType != MouseInput::MouseType::MOUSE_MOVE) {
     867           0 :     return nsEventStatus_eConsumeNoDefault;
     868             :   }
     869             : 
     870             :   RefPtr<HitTestingTreeNode> node =
     871           0 :     GetApzcTreeManager()->FindScrollThumbNode(aDragMetrics);
     872           0 :   if (!node) {
     873           0 :     return nsEventStatus_eConsumeNoDefault;
     874             :   }
     875             : 
     876           0 :   const ScrollThumbData& thumbData = node->GetScrollThumbData();
     877             : 
     878             :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
     879           0 :       (uint32_t) ScrollInputMethod::ApzScrollbarDrag);
     880             : 
     881           0 :   bool isMouseAwayFromThumb = false;
     882           0 :   if (int snapMultiplier = gfxPrefs::SliderSnapMultiplier()) {
     883             :     // It's fine to ignore the async component of the thumb's transform,
     884             :     // because any async transform of the thumb will be in the direction of
     885             :     // scrolling, but here we're interested in the other direction.
     886             :     ParentLayerRect thumbRect =
     887           0 :         (node->GetTransform() * AsyncTransformMatrix()).TransformBounds(
     888           0 :               LayerRect(node->GetVisibleRegion().GetBounds()));
     889           0 :     ScrollDirection otherDirection = GetPerpendicularDirection(aDragMetrics.mDirection);
     890             :     ParentLayerCoord distance = GetAxisStart(otherDirection,
     891           0 :         thumbRect.DistanceTo(aEvent.mLocalOrigin));
     892           0 :     ParentLayerCoord thumbWidth = GetAxisLength(otherDirection, thumbRect);
     893             :     // Avoid triggering this condition spuriously when the thumb is
     894             :     // offscreen and its visible region is therefore empty.
     895           0 :     if (thumbWidth > 0 && thumbWidth * snapMultiplier < distance) {
     896           0 :       isMouseAwayFromThumb = true;
     897             :     }
     898             :   }
     899             : 
     900           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
     901           0 :   CSSCoord thumbPosition;
     902           0 :   if (isMouseAwayFromThumb) {
     903           0 :     thumbPosition = aInitialThumbPos;
     904             :   } else {
     905             :     thumbPosition = ConvertScrollbarPoint(aEvent.mLocalOrigin, thumbData) -
     906           0 :                     aDragMetrics.mScrollbarDragOffset;
     907             :   }
     908             : 
     909           0 :   CSSCoord maxThumbPos = thumbData.mScrollTrackLength;
     910           0 :   maxThumbPos -= thumbData.mThumbLength;
     911             : 
     912           0 :   float scrollPercent = thumbPosition / maxThumbPos;
     913             : 
     914             :   CSSCoord minScrollPosition =
     915           0 :     GetAxisStart(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect().TopLeft());
     916             :   CSSCoord maxScrollPosition =
     917           0 :     GetAxisStart(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect().BottomRight()) -
     918           0 :     GetAxisLength(aDragMetrics.mDirection, mFrameMetrics.CalculateCompositedRectInCssPixels());
     919           0 :   CSSCoord scrollPosition = minScrollPosition + (scrollPercent * (maxScrollPosition - minScrollPosition));
     920             : 
     921           0 :   scrollPosition = std::max(scrollPosition, minScrollPosition);
     922           0 :   scrollPosition = std::min(scrollPosition, maxScrollPosition);
     923             : 
     924           0 :   CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
     925           0 :   if (aDragMetrics.mDirection == ScrollDirection::HORIZONTAL) {
     926           0 :     scrollOffset.x = scrollPosition;
     927             :   } else {
     928           0 :     scrollOffset.y = scrollPosition;
     929             :   }
     930           0 :   mFrameMetrics.SetScrollOffset(scrollOffset);
     931           0 :   ScheduleCompositeAndMaybeRepaint();
     932           0 :   UpdateSharedCompositorFrameMetrics();
     933             : 
     934           0 :   return nsEventStatus_eConsumeNoDefault;
     935             : }
     936             : 
     937           0 : nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
     938             :                                                        const ScreenToParentLayerMatrix4x4& aTransformToApzc) {
     939           0 :   APZThreadUtils::AssertOnControllerThread();
     940             : 
     941           0 :   nsEventStatus rv = nsEventStatus_eIgnore;
     942             : 
     943           0 :   switch (aEvent.mInputType) {
     944             :   case MULTITOUCH_INPUT: {
     945           0 :     MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
     946           0 :     if (!multiTouchInput.TransformToLocal(aTransformToApzc)) {
     947           0 :       return rv;
     948             :     }
     949             : 
     950           0 :     RefPtr<GestureEventListener> listener = GetGestureEventListener();
     951           0 :     if (listener) {
     952           0 :       rv = listener->HandleInputEvent(multiTouchInput);
     953           0 :       if (rv == nsEventStatus_eConsumeNoDefault) {
     954           0 :         return rv;
     955             :       }
     956             :     }
     957             : 
     958           0 :     switch (multiTouchInput.mType) {
     959           0 :       case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
     960           0 :       case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
     961           0 :       case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
     962           0 :       case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
     963             :     }
     964           0 :     break;
     965             :   }
     966             :   case PANGESTURE_INPUT: {
     967           0 :     PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
     968           0 :     if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
     969           0 :       return rv;
     970             :     }
     971             : 
     972           0 :     switch (panGestureInput.mType) {
     973           0 :       case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
     974           0 :       case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
     975           0 :       case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
     976           0 :       case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
     977           0 :       case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
     978           0 :       case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
     979           0 :       case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
     980           0 :       case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
     981             :     }
     982           0 :     break;
     983             :   }
     984             :   case MOUSE_INPUT: {
     985           0 :     MouseInput mouseInput = aEvent.AsMouseInput();
     986           0 :     if (!mouseInput.TransformToLocal(aTransformToApzc)) {
     987           0 :       return rv;
     988             :     }
     989           0 :     break;
     990             :   }
     991             :   case SCROLLWHEEL_INPUT: {
     992           0 :     ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
     993           0 :     if (!scrollInput.TransformToLocal(aTransformToApzc)) {
     994           0 :       return rv;
     995             :     }
     996             : 
     997           0 :     rv = OnScrollWheel(scrollInput);
     998           0 :     break;
     999             :   }
    1000             :   case PINCHGESTURE_INPUT: {
    1001           0 :     PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
    1002           0 :     if (!pinchInput.TransformToLocal(aTransformToApzc)) {
    1003           0 :       return rv;
    1004             :     }
    1005             : 
    1006           0 :     rv = HandleGestureEvent(pinchInput);
    1007           0 :     break;
    1008             :   }
    1009             :   case TAPGESTURE_INPUT: {
    1010           0 :     TapGestureInput tapInput = aEvent.AsTapGestureInput();
    1011           0 :     if (!tapInput.TransformToLocal(aTransformToApzc)) {
    1012           0 :       return rv;
    1013             :     }
    1014             : 
    1015           0 :     rv = HandleGestureEvent(tapInput);
    1016           0 :     break;
    1017             :   }
    1018             :   case KEYBOARD_INPUT: {
    1019           0 :     KeyboardInput keyInput = aEvent.AsKeyboardInput();
    1020           0 :     rv = OnKeyboard(keyInput);
    1021           0 :     break;
    1022             :   }
    1023             :   }
    1024             : 
    1025           0 :   return rv;
    1026             : }
    1027             : 
    1028           0 : nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
    1029             : {
    1030           0 :   APZThreadUtils::AssertOnControllerThread();
    1031             : 
    1032           0 :   nsEventStatus rv = nsEventStatus_eIgnore;
    1033             : 
    1034           0 :   switch (aEvent.mInputType) {
    1035             :   case PINCHGESTURE_INPUT: {
    1036           0 :     const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
    1037           0 :     switch (pinchGestureInput.mType) {
    1038           0 :       case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
    1039           0 :       case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
    1040           0 :       case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
    1041             :     }
    1042           0 :     break;
    1043             :   }
    1044             :   case TAPGESTURE_INPUT: {
    1045           0 :     const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
    1046           0 :     switch (tapGestureInput.mType) {
    1047           0 :       case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
    1048           0 :       case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
    1049           0 :       case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
    1050           0 :       case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
    1051           0 :       case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
    1052           0 :       case TapGestureInput::TAPGESTURE_SECOND: rv = OnSecondTap(tapGestureInput); break;
    1053           0 :       case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
    1054             :     }
    1055           0 :     break;
    1056             :   }
    1057           0 :   default: MOZ_ASSERT_UNREACHABLE("Unhandled input event"); break;
    1058             :   }
    1059             : 
    1060           0 :   return rv;
    1061             : }
    1062             : 
    1063           0 : void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
    1064             : {
    1065           0 :   mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
    1066           0 : }
    1067             : 
    1068           0 : nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
    1069             :   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
    1070           0 :   mPanDirRestricted = false;
    1071           0 :   ParentLayerPoint point = GetFirstTouchPoint(aEvent);
    1072             : 
    1073           0 :   switch (mState) {
    1074             :     case FLING:
    1075             :     case ANIMATING_ZOOM:
    1076             :     case SMOOTH_SCROLL:
    1077             :     case OVERSCROLL_ANIMATION:
    1078             :     case WHEEL_SCROLL:
    1079             :     case KEYBOARD_SCROLL:
    1080             :     case PAN_MOMENTUM:
    1081           0 :       MOZ_ASSERT(GetCurrentTouchBlock());
    1082           0 :       GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
    1083             :       MOZ_FALLTHROUGH;
    1084             :     case NOTHING: {
    1085           0 :       mX.StartTouch(point.x, aEvent.mTime);
    1086           0 :       mY.StartTouch(point.y, aEvent.mTime);
    1087           0 :       if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1088           0 :         MOZ_ASSERT(GetCurrentTouchBlock());
    1089           0 :         controller->NotifyAPZStateChange(
    1090           0 :             GetGuid(), APZStateChange::eStartTouch,
    1091           0 :             GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
    1092             :       }
    1093           0 :       SetState(TOUCHING);
    1094           0 :       break;
    1095             :     }
    1096             :     case TOUCHING:
    1097             :     case PANNING:
    1098             :     case PANNING_LOCKED_X:
    1099             :     case PANNING_LOCKED_Y:
    1100             :     case PINCHING:
    1101           0 :       NS_WARNING("Received impossible touch in OnTouchStart");
    1102           0 :       break;
    1103             :   }
    1104             : 
    1105           0 :   return nsEventStatus_eConsumeNoDefault;
    1106             : }
    1107             : 
    1108           0 : nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
    1109             :   APZC_LOG("%p got a touch-move in state %d\n", this, mState);
    1110           0 :   switch (mState) {
    1111             :     case FLING:
    1112             :     case SMOOTH_SCROLL:
    1113             :     case NOTHING:
    1114             :     case ANIMATING_ZOOM:
    1115             :       // May happen if the user double-taps and drags without lifting after the
    1116             :       // second tap. Ignore the move if this happens.
    1117           0 :       return nsEventStatus_eIgnore;
    1118             : 
    1119             :     case TOUCHING: {
    1120           0 :       ScreenCoord panThreshold = GetTouchStartTolerance();
    1121           0 :       UpdateWithTouchAtDevicePoint(aEvent);
    1122             : 
    1123           0 :       if (PanDistance() < panThreshold) {
    1124           0 :         return nsEventStatus_eIgnore;
    1125             :       }
    1126             : 
    1127           0 :       ParentLayerPoint touchPoint = GetFirstTouchPoint(aEvent);
    1128             : 
    1129           0 :       MOZ_ASSERT(GetCurrentTouchBlock());
    1130           0 :       if (gfxPrefs::TouchActionEnabled() && GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
    1131             :         // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
    1132             :         // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
    1133             :         // status immediately to trigger cancel event further. It should happen independent of
    1134             :         // the parent type (whether it is scrolling or not).
    1135           0 :         StartPanning(touchPoint);
    1136           0 :         return nsEventStatus_eConsumeNoDefault;
    1137             :       }
    1138             : 
    1139           0 :       return StartPanning(touchPoint);
    1140             :     }
    1141             : 
    1142             :     case PANNING:
    1143             :     case PANNING_LOCKED_X:
    1144             :     case PANNING_LOCKED_Y:
    1145             :     case PAN_MOMENTUM:
    1146           0 :       TrackTouch(aEvent);
    1147           0 :       return nsEventStatus_eConsumeNoDefault;
    1148             : 
    1149             :     case PINCHING:
    1150             :       // The scale gesture listener should have handled this.
    1151           0 :       NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
    1152           0 :       return nsEventStatus_eIgnore;
    1153             : 
    1154             :     case WHEEL_SCROLL:
    1155             :     case KEYBOARD_SCROLL:
    1156             :     case OVERSCROLL_ANIMATION:
    1157             :       // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
    1158             :       // as touch blocks that begin in an overscrolled state cancel the
    1159             :       // animation. The same is true for wheel scroll animations.
    1160           0 :       NS_WARNING("Received impossible touch in OnTouchMove");
    1161           0 :       break;
    1162             :   }
    1163             : 
    1164           0 :   return nsEventStatus_eConsumeNoDefault;
    1165             : }
    1166             : 
    1167           0 : nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
    1168             :   APZC_LOG("%p got a touch-end in state %d\n", this, mState);
    1169           0 :   OnTouchEndOrCancel();
    1170             : 
    1171             :   // In case no touch behavior triggered previously we can avoid sending
    1172             :   // scroll events or requesting content repaint. This condition is added
    1173             :   // to make tests consistent - in case touch-action is NONE (and therefore
    1174             :   // no pans/zooms can be performed) we expected neither scroll or repaint
    1175             :   // events.
    1176           0 :   if (mState != NOTHING) {
    1177           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1178             :   }
    1179             : 
    1180           0 :   switch (mState) {
    1181             :   case FLING:
    1182             :     // Should never happen.
    1183           0 :     NS_WARNING("Received impossible touch end in OnTouchEnd.");
    1184             :     MOZ_FALLTHROUGH;
    1185             :   case ANIMATING_ZOOM:
    1186             :   case SMOOTH_SCROLL:
    1187             :   case NOTHING:
    1188             :     // May happen if the user double-taps and drags without lifting after the
    1189             :     // second tap. Ignore if this happens.
    1190           0 :     return nsEventStatus_eIgnore;
    1191             : 
    1192             :   case TOUCHING:
    1193             :     // We may have some velocity stored on the axis from move events
    1194             :     // that were not big enough to trigger scrolling. Clear that out.
    1195           0 :     mX.SetVelocity(0);
    1196           0 :     mY.SetVelocity(0);
    1197           0 :     MOZ_ASSERT(GetCurrentTouchBlock());
    1198             :     APZC_LOG("%p still has %u touch points active\n", this,
    1199             :         GetCurrentTouchBlock()->GetActiveTouchCount());
    1200             :     // In cases where the user is panning, then taps the second finger without
    1201             :     // entering a pinch, we will arrive here when the second finger is lifted.
    1202             :     // However the first finger is still down so we want to remain in state
    1203             :     // TOUCHING.
    1204           0 :     if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
    1205             :       // It's possible we may be overscrolled if the user tapped during a
    1206             :       // previous overscroll pan. Make sure to snap back in this situation.
    1207             :       // An ancestor APZC could be overscrolled instead of this APZC, so
    1208             :       // walk the handoff chain as well.
    1209           0 :       GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
    1210             :       // SnapBackOverscrolledApzc() will put any APZC it causes to snap back
    1211             :       // into the OVERSCROLL_ANIMATION state. If that's not us, since we're
    1212             :       // done TOUCHING enter the NOTHING state.
    1213           0 :       if (mState != OVERSCROLL_ANIMATION) {
    1214           0 :         SetState(NOTHING);
    1215             :       }
    1216             :     }
    1217           0 :     return nsEventStatus_eIgnore;
    1218             : 
    1219             :   case PANNING:
    1220             :   case PANNING_LOCKED_X:
    1221             :   case PANNING_LOCKED_Y:
    1222             :   case PAN_MOMENTUM:
    1223             :   {
    1224           0 :     MOZ_ASSERT(GetCurrentTouchBlock());
    1225           0 :     mX.EndTouch(aEvent.mTime);
    1226           0 :     mY.EndTouch(aEvent.mTime);
    1227           0 :     return HandleEndOfPan();
    1228             :   }
    1229             :   case PINCHING:
    1230           0 :     SetState(NOTHING);
    1231             :     // Scale gesture listener should have handled this.
    1232           0 :     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
    1233           0 :     return nsEventStatus_eIgnore;
    1234             : 
    1235             :   case WHEEL_SCROLL:
    1236             :   case KEYBOARD_SCROLL:
    1237             :   case OVERSCROLL_ANIMATION:
    1238             :     // Should not receive a touch-end in the OVERSCROLL_ANIMATION state
    1239             :     // as touch blocks that begin in an overscrolled state cancel the
    1240             :     // animation. The same is true for WHEEL_SCROLL.
    1241           0 :     NS_WARNING("Received impossible touch in OnTouchEnd");
    1242           0 :     break;
    1243             :   }
    1244             : 
    1245           0 :   return nsEventStatus_eConsumeNoDefault;
    1246             : }
    1247             : 
    1248           0 : nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
    1249             :   APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
    1250           0 :   OnTouchEndOrCancel();
    1251           0 :   CancelAnimationAndGestureState();
    1252           0 :   return nsEventStatus_eConsumeNoDefault;
    1253             : }
    1254             : 
    1255           0 : nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
    1256             :   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
    1257             : 
    1258           0 :   mPinchPaintTimerSet = false;
    1259             :   // Note that there may not be a touch block at this point, if we received the
    1260             :   // PinchGestureEvent directly from widget code without any touch events.
    1261           0 :   if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
    1262           0 :     return nsEventStatus_eIgnore;
    1263             :   }
    1264             : 
    1265             :   // If zooming is not allowed, this is a two-finger pan.
    1266             :   // Start tracking panning distance and velocity.
    1267           0 :   if (!mZoomConstraints.mAllowZoom) {
    1268           0 :     mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
    1269           0 :     mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
    1270             :   }
    1271             : 
    1272             :   // For platforms that don't support APZ zooming, dispatch a message to the
    1273             :   // content controller, it may want to do something else with this gesture.
    1274           0 :   if (!gfxPrefs::APZAllowZooming()) {
    1275           0 :     if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1276           0 :       controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
    1277             :     }
    1278             :   }
    1279             : 
    1280           0 :   SetState(PINCHING);
    1281           0 :   mX.SetVelocity(0);
    1282           0 :   mY.SetVelocity(0);
    1283           0 :   mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
    1284             : 
    1285           0 :   return nsEventStatus_eConsumeNoDefault;
    1286             : }
    1287             : 
    1288           0 : nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
    1289             :   APZC_LOG("%p got a scale in state %d\n", this, mState);
    1290             :  
    1291           0 :   if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
    1292           0 :     return nsEventStatus_eIgnore;
    1293             :   }
    1294             : 
    1295           0 :   if (mState != PINCHING) {
    1296           0 :     return nsEventStatus_eConsumeNoDefault;
    1297             :   }
    1298             : 
    1299             :   // If zooming is not allowed, this is a two-finger pan.
    1300             :   // Tracking panning distance and velocity.
    1301           0 :   if (!mZoomConstraints.mAllowZoom) {
    1302           0 :     mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x, 0, aEvent.mTime);
    1303           0 :     mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y, 0, aEvent.mTime);
    1304             :   }
    1305             : 
    1306           0 :   if (!gfxPrefs::APZAllowZooming()) {
    1307           0 :     if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1308           0 :       controller->NotifyPinchGesture(aEvent.mType, GetGuid(),
    1309           0 :           ViewAs<LayoutDevicePixel>(aEvent.mCurrentSpan - aEvent.mPreviousSpan,
    1310             :             PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
    1311           0 :           aEvent.modifiers);
    1312             :     }
    1313             :   }
    1314             : 
    1315             :   // Only the root APZC is zoomable, and the root APZC is not allowed to have
    1316             :   // different x and y scales. If it did, the calculations in this function
    1317             :   // would have to be adjusted (as e.g. it would no longer be valid to take
    1318             :   // the minimum or maximum of the ratios of the widths and heights of the
    1319             :   // page rect and the composition bounds).
    1320           0 :   MOZ_ASSERT(mFrameMetrics.IsRootContent());
    1321           0 :   MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
    1322             : 
    1323             :   {
    1324           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1325             : 
    1326           0 :     CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom().ToScaleFactor();
    1327           0 :     ParentLayerPoint focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
    1328           0 :     CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
    1329             : 
    1330           0 :     ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
    1331           0 :     mLastZoomFocus = focusPoint;
    1332             :     // If displacing by the change in focus point will take us off page bounds,
    1333             :     // then reduce the displacement such that it doesn't.
    1334           0 :     focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
    1335           0 :     focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
    1336           0 :     ScrollBy(focusChange / userZoom);
    1337             : 
    1338             :     // If the span is zero or close to it, we don't want to process this zoom
    1339             :     // change because we're going to get wonky numbers for the spanRatio. So
    1340             :     // let's bail out here. Note that we do this after the focus-change-scroll
    1341             :     // above, so that if we have a pinch with zero span but changing focus,
    1342             :     // such as generated by some Synaptics touchpads on Windows, we still
    1343             :     // scroll properly.
    1344           0 :     float prevSpan = aEvent.mPreviousSpan;
    1345           0 :     if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
    1346             :       // We might have done a nonzero ScrollBy above, so update metrics and
    1347             :       // repaint/recomposite
    1348           0 :       ScheduleCompositeAndMaybeRepaint();
    1349           0 :       UpdateSharedCompositorFrameMetrics();
    1350           0 :       return nsEventStatus_eConsumeNoDefault;
    1351             :     }
    1352           0 :     float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
    1353             : 
    1354             :     // When we zoom in with focus, we can zoom too much towards the boundaries
    1355             :     // that we actually go over them. These are the needed displacements along
    1356             :     // either axis such that we don't overscroll the boundaries when zooming.
    1357           0 :     CSSPoint neededDisplacement;
    1358             : 
    1359           0 :     CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
    1360           0 :     CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
    1361           0 :     realMinZoom.scale = std::max(realMinZoom.scale,
    1362           0 :                                  mFrameMetrics.GetCompositionBounds().width / mFrameMetrics.GetScrollableRect().width);
    1363           0 :     realMinZoom.scale = std::max(realMinZoom.scale,
    1364           0 :                                  mFrameMetrics.GetCompositionBounds().height / mFrameMetrics.GetScrollableRect().height);
    1365           0 :     if (realMaxZoom < realMinZoom) {
    1366           0 :       realMaxZoom = realMinZoom;
    1367             :     }
    1368             : 
    1369           0 :     bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
    1370           0 :                    (spanRatio < 1.0 && userZoom > realMinZoom);
    1371             : 
    1372           0 :     if (!mZoomConstraints.mAllowZoom) {
    1373           0 :       doScale = false;
    1374             :     }
    1375             : 
    1376           0 :     if (doScale) {
    1377           0 :       spanRatio = clamped(spanRatio,
    1378           0 :                           realMinZoom.scale / userZoom.scale,
    1379           0 :                           realMaxZoom.scale / userZoom.scale);
    1380             : 
    1381             :       // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
    1382             :       // up above we clamped it.
    1383           0 :       neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
    1384           0 :       neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
    1385             : 
    1386           0 :       ScaleWithFocus(spanRatio, cssFocusPoint);
    1387             : 
    1388           0 :       if (neededDisplacement != CSSPoint()) {
    1389           0 :         ScrollBy(neededDisplacement);
    1390             :       }
    1391             : 
    1392             :       // We don't want to redraw on every scale, so throttle it.
    1393           0 :       if (!mPinchPaintTimerSet) {
    1394           0 :         const int delay = gfxPrefs::APZScaleRepaintDelay();
    1395           0 :         if (delay >= 0) {
    1396           0 :           if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1397           0 :             mPinchPaintTimerSet = true;
    1398           0 :             controller->PostDelayedTask(
    1399           0 :               NewRunnableMethod(
    1400             :                 "layers::AsyncPanZoomController::"
    1401             :                 "DoDelayedRequestContentRepaint",
    1402             :                 this,
    1403             :                 &AsyncPanZoomController::DoDelayedRequestContentRepaint),
    1404           0 :               delay);
    1405             :           }
    1406             :         }
    1407             :       }
    1408             : 
    1409           0 :       UpdateSharedCompositorFrameMetrics();
    1410             : 
    1411             :     } else {
    1412             :       // Trigger a repaint request after scrolling.
    1413           0 :       RequestContentRepaint();
    1414             :     }
    1415             : 
    1416             :     // We did a ScrollBy call above even if we didn't do a scale, so we
    1417             :     // should composite for that.
    1418           0 :     ScheduleComposite();
    1419             :   }
    1420             : 
    1421           0 :   return nsEventStatus_eConsumeNoDefault;
    1422             : }
    1423             : 
    1424           0 : nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
    1425             :   APZC_LOG("%p got a scale-end in state %d\n", this, mState);
    1426             : 
    1427           0 :   mPinchPaintTimerSet = false;
    1428             : 
    1429           0 :   if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
    1430           0 :     return nsEventStatus_eIgnore;
    1431             :   }
    1432             : 
    1433           0 :   if (!gfxPrefs::APZAllowZooming()) {
    1434           0 :     if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1435           0 :       controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
    1436             :     }
    1437             :   }
    1438             : 
    1439             :   {
    1440           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1441           0 :     ScheduleComposite();
    1442           0 :     RequestContentRepaint();
    1443           0 :     UpdateSharedCompositorFrameMetrics();
    1444             :   }
    1445             : 
    1446             :   // Non-negative focus point would indicate that one finger is still down
    1447           0 :   if (aEvent.mLocalFocusPoint.x != -1 && aEvent.mLocalFocusPoint.y != -1) {
    1448           0 :     if (mZoomConstraints.mAllowZoom) {
    1449           0 :       mPanDirRestricted = false;
    1450           0 :       mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
    1451           0 :       mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
    1452           0 :       SetState(TOUCHING);
    1453             :     } else {
    1454             :       // If zooming isn't allowed, StartTouch() was already called
    1455             :       // in OnScaleBegin().
    1456           0 :       StartPanning(aEvent.mLocalFocusPoint);
    1457             :     }
    1458             :   } else {
    1459             :     // Otherwise, handle the fingers being lifted.
    1460           0 :     if (mZoomConstraints.mAllowZoom) {
    1461           0 :       ReentrantMonitorAutoEnter lock(mMonitor);
    1462             : 
    1463             :       // We can get into a situation where we are overscrolled at the end of a
    1464             :       // pinch if we go into overscroll with a two-finger pan, and then turn
    1465             :       // that into a pinch by increasing the span sufficiently. In such a case,
    1466             :       // there is no snap-back animation to get us out of overscroll, so we need
    1467             :       // to get out of it somehow.
    1468             :       // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
    1469             :       // further up in the handoff chain rather than on the current APZC, so
    1470             :       // we need to clear overscroll along the entire handoff chain.
    1471           0 :       if (HasReadyTouchBlock()) {
    1472           0 :         GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
    1473             :       } else {
    1474           0 :         ClearOverscroll();
    1475             :       }
    1476             :       // Along with clearing the overscroll, we also want to snap to the nearest
    1477             :       // snap point as appropriate.
    1478           0 :       ScrollSnap();
    1479             :     } else {
    1480             :       // when zoom is not allowed
    1481           0 :       mX.EndTouch(aEvent.mTime);
    1482           0 :       mY.EndTouch(aEvent.mTime);
    1483           0 :       if (mState == PINCHING) {
    1484             :         // still pinching
    1485           0 :         if (HasReadyTouchBlock()) {
    1486           0 :           return HandleEndOfPan();
    1487             :         }
    1488             :       }
    1489             :     }
    1490             :   }
    1491           0 :   return nsEventStatus_eConsumeNoDefault;
    1492             : }
    1493             : 
    1494           0 : nsEventStatus AsyncPanZoomController::HandleEndOfPan()
    1495             : {
    1496           0 :   MOZ_ASSERT(GetCurrentTouchBlock());
    1497           0 :   GetCurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
    1498           0 :   ParentLayerPoint flingVelocity = GetVelocityVector();
    1499             : 
    1500             :   // Clear our velocities; if DispatchFling() gives the fling to us,
    1501             :   // the fling velocity gets *added* to our existing velocity in
    1502             :   // AcceptFling().
    1503           0 :   mX.SetVelocity(0);
    1504           0 :   mY.SetVelocity(0);
    1505             :   // Clear our state so that we don't stay in the PANNING state
    1506             :   // if DispatchFling() gives the fling to somone else. However,
    1507             :   // don't send the state change notification until we've determined
    1508             :   // what our final state is to avoid notification churn.
    1509           0 :   StateChangeNotificationBlocker blocker(this);
    1510           0 :   SetState(NOTHING);
    1511             : 
    1512             :   APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
    1513             :       flingVelocity.Length().value, gfxPrefs::APZFlingMinVelocityThreshold());
    1514             : 
    1515           0 :   if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
    1516             :     // Relieve overscroll now if needed, since we will not transition to a fling
    1517             :     // animation and then an overscroll animation, and relieve it then.
    1518           0 :     GetCurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
    1519           0 :     return nsEventStatus_eConsumeNoDefault;
    1520             :   }
    1521             : 
    1522             :   // Make a local copy of the tree manager pointer and check that it's not
    1523             :   // null before calling DispatchFling(). This is necessary because Destroy(),
    1524             :   // which nulls out mTreeManager, could be called concurrently.
    1525           0 :   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
    1526             :     FlingHandoffState handoffState{flingVelocity,
    1527           0 :                                   GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
    1528             :                                   false /* not handoff */,
    1529           0 :                                   GetCurrentTouchBlock()->GetScrolledApzc()};
    1530           0 :     treeManagerLocal->DispatchFling(this, handoffState);
    1531             :   }
    1532           0 :   return nsEventStatus_eConsumeNoDefault;
    1533             : }
    1534             : 
    1535             : bool
    1536           0 : AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut)
    1537             : {
    1538           0 :   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
    1539             :     ScreenToScreenMatrix4x4 transformScreenToGecko =
    1540           0 :         treeManagerLocal->GetScreenToApzcTransform(this)
    1541           0 :       * treeManagerLocal->GetApzcToGeckoTransform(this);
    1542             : 
    1543             :     Maybe<ScreenIntPoint> layoutPoint = UntransformBy(
    1544           0 :         transformScreenToGecko, aPoint);
    1545           0 :     if (!layoutPoint) {
    1546           0 :       return false;
    1547             :     }
    1548             : 
    1549           0 :     *aOut = LayoutDevicePoint(ViewAs<LayoutDevicePixel>(*layoutPoint,
    1550             :                 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
    1551           0 :     return true;
    1552             :   }
    1553           0 :   return false;
    1554             : }
    1555             : 
    1556             : CSSCoord
    1557           0 : AsyncPanZoomController::ConvertScrollbarPoint(const ParentLayerPoint& aScrollbarPoint,
    1558             :                                               const ScrollThumbData& aThumbData) const
    1559             : {
    1560           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    1561             : 
    1562             :   // First, get it into the right coordinate space.
    1563           0 :   CSSPoint scrollbarPoint = aScrollbarPoint / mFrameMetrics.GetZoom();
    1564             :   // The scrollbar can be transformed with the frame but the pres shell
    1565             :   // resolution is only applied to the scroll frame.
    1566           0 :   scrollbarPoint = scrollbarPoint * mFrameMetrics.GetPresShellResolution();
    1567             : 
    1568             :   // Now, get it to be relative to the beginning of the scroll track.
    1569           0 :   CSSRect cssCompositionBound = mFrameMetrics.CalculateCompositedRectInCssPixels();
    1570           0 :   return GetAxisStart(aThumbData.mDirection, scrollbarPoint)
    1571           0 :       - GetAxisStart(aThumbData.mDirection, cssCompositionBound)
    1572           0 :       - aThumbData.mScrollTrackStart;
    1573             : }
    1574             : 
    1575             : static bool
    1576           0 : AllowsScrollingMoreThanOnePage(double aMultiplier)
    1577             : {
    1578             :   const int32_t kMinAllowPageScroll =
    1579           0 :     EventStateManager::MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
    1580           0 :   return Abs(aMultiplier) >= kMinAllowPageScroll;
    1581             : }
    1582             : 
    1583             : ParentLayerPoint
    1584           0 : AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) const
    1585             : {
    1586           0 :   ParentLayerSize scrollAmount;
    1587           0 :   ParentLayerSize pageScrollSize;
    1588             : 
    1589             :   {
    1590             :     // Grab the lock to access the frame metrics.
    1591           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1592           0 :     LayoutDeviceIntSize scrollAmountLD = mScrollMetadata.GetLineScrollAmount();
    1593           0 :     LayoutDeviceIntSize pageScrollSizeLD = mScrollMetadata.GetPageScrollAmount();
    1594           0 :     scrollAmount = scrollAmountLD /
    1595           0 :       mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
    1596           0 :     pageScrollSize = pageScrollSizeLD /
    1597           0 :       mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom();
    1598             :   }
    1599             : 
    1600           0 :   ParentLayerPoint delta;
    1601           0 :   switch (aEvent.mDeltaType) {
    1602             :     case ScrollWheelInput::SCROLLDELTA_LINE: {
    1603           0 :       delta.x = aEvent.mDeltaX * scrollAmount.width;
    1604           0 :       delta.y = aEvent.mDeltaY * scrollAmount.height;
    1605           0 :       break;
    1606             :     }
    1607             :     case ScrollWheelInput::SCROLLDELTA_PAGE: {
    1608           0 :       delta.x = aEvent.mDeltaX * pageScrollSize.width;
    1609           0 :       delta.y = aEvent.mDeltaY * pageScrollSize.height;
    1610           0 :       break;
    1611             :     }
    1612             :     case ScrollWheelInput::SCROLLDELTA_PIXEL: {
    1613           0 :       delta = ToParentLayerCoordinates(ScreenPoint(aEvent.mDeltaX, aEvent.mDeltaY), aEvent.mOrigin);
    1614           0 :       break;
    1615             :     }
    1616             :   }
    1617             : 
    1618             :   // Apply user-set multipliers.
    1619           0 :   delta.x *= aEvent.mUserDeltaMultiplierX;
    1620           0 :   delta.y *= aEvent.mUserDeltaMultiplierY;
    1621             : 
    1622             :   // For the conditions under which we allow system scroll overrides, see
    1623             :   // EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction
    1624             :   // and WheelTransaction::OverrideSystemScrollSpeed. Note that we do *not*
    1625             :   // restrict this to the root content, see bug 1217715 for discussion on this.
    1626           0 :   if (gfxPrefs::MouseWheelHasRootScrollDeltaOverride() &&
    1627           0 :       !aEvent.IsCustomizedByUserPrefs() &&
    1628           0 :       aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
    1629           0 :       aEvent.mAllowToOverrideSystemScrollSpeed) {
    1630           0 :     delta.x = WidgetWheelEvent::ComputeOverriddenDelta(delta.x, false);
    1631           0 :     delta.y = WidgetWheelEvent::ComputeOverriddenDelta(delta.y, true);
    1632             :   }
    1633             : 
    1634             :   // If this is a line scroll, and this event was part of a scroll series, then
    1635             :   // it might need extra acceleration. See WheelHandlingHelper.cpp.
    1636           0 :   if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
    1637           0 :       aEvent.mScrollSeriesNumber > 0)
    1638             :   {
    1639           0 :     int32_t start = gfxPrefs::MouseWheelAccelerationStart();
    1640           0 :     if (start >= 0 && aEvent.mScrollSeriesNumber >= uint32_t(start)) {
    1641           0 :       int32_t factor = gfxPrefs::MouseWheelAccelerationFactor();
    1642           0 :       if (factor > 0) {
    1643           0 :         delta.x = ComputeAcceleratedWheelDelta(delta.x, aEvent.mScrollSeriesNumber, factor);
    1644           0 :         delta.y = ComputeAcceleratedWheelDelta(delta.y, aEvent.mScrollSeriesNumber, factor);
    1645             :       }
    1646             :     }
    1647             :   }
    1648             : 
    1649             :   // We shouldn't scroll more than one page at once except when the
    1650             :   // user preference is large.
    1651           0 :   if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierX) &&
    1652           0 :       Abs(delta.x) > pageScrollSize.width) {
    1653           0 :     delta.x = (delta.x >= 0)
    1654           0 :               ? pageScrollSize.width
    1655           0 :               : -pageScrollSize.width;
    1656             :   }
    1657           0 :   if (!AllowsScrollingMoreThanOnePage(aEvent.mUserDeltaMultiplierY) &&
    1658           0 :       Abs(delta.y) > pageScrollSize.height) {
    1659           0 :     delta.y = (delta.y >= 0)
    1660           0 :               ? pageScrollSize.height
    1661           0 :               : -pageScrollSize.height;
    1662             :   }
    1663             : 
    1664           0 :   return delta;
    1665             : }
    1666             : 
    1667             : static
    1668           0 : void ReportKeyboardScrollAction(const KeyboardScrollAction& aAction)
    1669             : {
    1670             :   ScrollInputMethod scrollMethod;
    1671             : 
    1672           0 :   switch (aAction.mType) {
    1673             :     case KeyboardScrollAction::eScrollLine: {
    1674           0 :       scrollMethod = ScrollInputMethod::ApzScrollLine;
    1675           0 :       break;
    1676             :     }
    1677             :     case KeyboardScrollAction::eScrollCharacter: {
    1678           0 :       scrollMethod = ScrollInputMethod::ApzScrollCharacter;
    1679           0 :       break;
    1680             :     }
    1681             :     case KeyboardScrollAction::eScrollPage: {
    1682           0 :       scrollMethod = ScrollInputMethod::ApzScrollPage;
    1683           0 :       break;
    1684             :     }
    1685             :     case KeyboardScrollAction::eScrollComplete: {
    1686           0 :       scrollMethod = ScrollInputMethod::ApzCompleteScroll;
    1687           0 :       break;
    1688             :     }
    1689             :   }
    1690             : 
    1691           0 :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    1692           0 :       (uint32_t)scrollMethod);
    1693           0 : }
    1694             : 
    1695             : nsEventStatus
    1696           0 : AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent)
    1697             : {
    1698             :   // Report the type of scroll action to telemetry
    1699           0 :   ReportKeyboardScrollAction(aEvent.mAction);
    1700             : 
    1701             :   // Calculate the destination for this keyboard scroll action
    1702           0 :   CSSPoint destination = GetKeyboardDestination(aEvent.mAction);
    1703           0 :   nsIScrollableFrame::ScrollUnit scrollUnit = KeyboardScrollAction::GetScrollUnit(aEvent.mAction.mType);
    1704             : 
    1705             :   // The lock must be held across the entire update operation, so the
    1706             :   // compositor doesn't end the animation before we get a chance to
    1707             :   // update it.
    1708           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    1709             : 
    1710           0 :   if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, scrollUnit)) {
    1711             :     // If we're scroll snapping, use a smooth scroll animation to get
    1712             :     // the desired physics. Note that SmoothScrollTo() will re-use an
    1713             :     // existing smooth scroll animation if there is one.
    1714             :     APZC_LOG("%p keyboard scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str());
    1715           0 :     SmoothScrollTo(*snapPoint);
    1716           0 :     return nsEventStatus_eConsumeNoDefault;
    1717             :   }
    1718             : 
    1719             :   // Use a keyboard scroll animation to scroll, reusing an existing one if it exists
    1720           0 :   if (mState != KEYBOARD_SCROLL) {
    1721           0 :     CancelAnimation();
    1722           0 :     SetState(KEYBOARD_SCROLL);
    1723             : 
    1724           0 :     nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
    1725           0 :     StartAnimation(new KeyboardScrollAnimation(*this, initialPosition, aEvent.mAction.mType));
    1726             :   }
    1727             : 
    1728             :   // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
    1729             :   // appunits/second. We perform a cast to ParentLayerPoints/ms without a
    1730             :   // conversion so that the scroll duration isn't affected by zoom
    1731             :   nsPoint velocity =
    1732           0 :     CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
    1733             : 
    1734           0 :   KeyboardScrollAnimation* animation = mAnimation->AsKeyboardScrollAnimation();
    1735           0 :   MOZ_ASSERT(animation);
    1736             : 
    1737           0 :   animation->UpdateDestination(aEvent.mTimeStamp,
    1738           0 :                                CSSPixel::ToAppUnits(destination),
    1739           0 :                                nsSize(velocity.x, velocity.y));
    1740             : 
    1741           0 :   return nsEventStatus_eConsumeNoDefault;
    1742             : }
    1743             : 
    1744             : CSSPoint
    1745           0 : AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aAction) const
    1746             : {
    1747           0 :   CSSSize lineScrollSize;
    1748           0 :   CSSSize pageScrollSize;
    1749           0 :   CSSPoint scrollOffset;
    1750           0 :   CSSRect scrollRect;
    1751             : 
    1752             :   {
    1753             :     // Grab the lock to access the frame metrics.
    1754           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1755             : 
    1756           0 :     lineScrollSize = mScrollMetadata.GetLineScrollAmount() /
    1757           0 :       mFrameMetrics.GetDevPixelsPerCSSPixel();
    1758           0 :     pageScrollSize = mScrollMetadata.GetPageScrollAmount() /
    1759           0 :       mFrameMetrics.GetDevPixelsPerCSSPixel();
    1760             : 
    1761           0 :     if (mState == WHEEL_SCROLL) {
    1762           0 :       scrollOffset = mAnimation->AsWheelScrollAnimation()->GetDestination();
    1763           0 :     } else if (mState == SMOOTH_SCROLL) {
    1764           0 :       scrollOffset = mAnimation->AsSmoothScrollAnimation()->GetDestination();
    1765           0 :     } else if (mState == KEYBOARD_SCROLL) {
    1766           0 :       scrollOffset = mAnimation->AsKeyboardScrollAnimation()->GetDestination();
    1767             :     } else {
    1768           0 :       scrollOffset = mFrameMetrics.GetScrollOffset();
    1769             :     }
    1770             : 
    1771           0 :     scrollRect = mFrameMetrics.GetScrollableRect();
    1772             :   }
    1773             : 
    1774             :   // Calculate the scroll destination based off of the scroll type and direction
    1775           0 :   CSSPoint scrollDestination = scrollOffset;
    1776             : 
    1777           0 :   switch (aAction.mType) {
    1778             :     case KeyboardScrollAction::eScrollCharacter: {
    1779           0 :       int32_t scrollDistance = gfxPrefs::ToolkitHorizontalScrollDistance();
    1780             : 
    1781           0 :       if (aAction.mForward) {
    1782           0 :         scrollDestination.x += scrollDistance * lineScrollSize.width;
    1783             :       } else {
    1784           0 :         scrollDestination.x -= scrollDistance * lineScrollSize.width;
    1785             :       }
    1786           0 :       break;
    1787             :     }
    1788             :     case KeyboardScrollAction::eScrollLine: {
    1789           0 :       int32_t scrollDistance = gfxPrefs::ToolkitVerticalScrollDistance();
    1790             : 
    1791           0 :       if (aAction.mForward) {
    1792           0 :         scrollDestination.y += scrollDistance * lineScrollSize.height;
    1793             :       } else {
    1794           0 :         scrollDestination.y -= scrollDistance * lineScrollSize.height;
    1795             :       }
    1796           0 :       break;
    1797             :     }
    1798             :     case KeyboardScrollAction::eScrollPage: {
    1799           0 :       if (aAction.mForward) {
    1800           0 :         scrollDestination.y += pageScrollSize.height;
    1801             :       } else {
    1802           0 :         scrollDestination.y -= pageScrollSize.height;
    1803             :       }
    1804           0 :       break;
    1805             :     }
    1806             :     case KeyboardScrollAction::eScrollComplete: {
    1807           0 :       if (aAction.mForward) {
    1808           0 :         scrollDestination.y = scrollRect.YMost();
    1809             :       } else {
    1810           0 :         scrollDestination.y = scrollRect.y;
    1811             :       }
    1812           0 :       break;
    1813             :     }
    1814             :   }
    1815             : 
    1816           0 :   return scrollDestination;
    1817             : }
    1818             : 
    1819             : // Return whether or not the underlying layer can be scrolled on either axis.
    1820             : bool
    1821           0 : AsyncPanZoomController::CanScroll(const InputData& aEvent) const
    1822             : {
    1823           0 :   ParentLayerPoint delta;
    1824           0 :   if (aEvent.mInputType == SCROLLWHEEL_INPUT) {
    1825           0 :     delta = GetScrollWheelDelta(aEvent.AsScrollWheelInput());
    1826           0 :   } else if (aEvent.mInputType == PANGESTURE_INPUT) {
    1827           0 :     const PanGestureInput& panInput = aEvent.AsPanGestureInput();
    1828           0 :     delta = ToParentLayerCoordinates(panInput.UserMultipliedPanDisplacement(), panInput.mPanStartPoint);
    1829             :   }
    1830           0 :   if (!delta.x && !delta.y) {
    1831           0 :     return false;
    1832             :   }
    1833             : 
    1834           0 :   return CanScrollWithWheel(delta);
    1835             : }
    1836             : 
    1837             : bool
    1838           0 : AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const
    1839             : {
    1840           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    1841           0 :   if (mX.CanScroll(aDelta.x)) {
    1842           0 :     return true;
    1843             :   }
    1844           0 :   if (mY.CanScroll(aDelta.y) && mScrollMetadata.AllowVerticalScrollWithWheel()) {
    1845           0 :     return true;
    1846             :   }
    1847           0 :   return false;
    1848             : }
    1849             : 
    1850             : bool
    1851           0 : AsyncPanZoomController::CanScroll(ScrollDirection aDirection) const
    1852             : {
    1853           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    1854           0 :   switch (aDirection) {
    1855           0 :   case ScrollDirection::HORIZONTAL: return mX.CanScroll();
    1856           0 :   case ScrollDirection::VERTICAL:   return mY.CanScroll();
    1857             : 
    1858             :   case ScrollDirection::NONE:
    1859           0 :     MOZ_ASSERT_UNREACHABLE("Invalid value");
    1860             :     break;
    1861             :   }
    1862           0 :   return false;
    1863             : }
    1864             : 
    1865             : bool
    1866           0 : AsyncPanZoomController::AllowScrollHandoffInCurrentBlock() const
    1867             : {
    1868           0 :   bool result = mInputQueue->AllowScrollHandoff();
    1869           0 :   if (!gfxPrefs::APZAllowImmediateHandoff()) {
    1870           0 :     if (InputBlockState* currentBlock = GetCurrentInputBlock()) {
    1871             :       // Do not allow handoff beyond the first APZC to scroll.
    1872           0 :       if (currentBlock->GetScrolledApzc() == this) {
    1873           0 :         result = false;
    1874             :       }
    1875             :     }
    1876             :   }
    1877           0 :   return result;
    1878             : }
    1879             : 
    1880           0 : void AsyncPanZoomController::DoDelayedRequestContentRepaint()
    1881             : {
    1882           0 :   if (!IsDestroyed() && mPinchPaintTimerSet) {
    1883           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    1884           0 :     RequestContentRepaint();
    1885             :   }
    1886           0 :   mPinchPaintTimerSet = false;
    1887           0 : }
    1888             : 
    1889             : static ScrollInputMethod
    1890           0 : ScrollInputMethodForWheelDeltaType(ScrollWheelInput::ScrollDeltaType aDeltaType)
    1891             : {
    1892           0 :   switch (aDeltaType) {
    1893             :     case ScrollWheelInput::SCROLLDELTA_LINE: {
    1894           0 :       return ScrollInputMethod::ApzWheelLine;
    1895             :     }
    1896             :     case ScrollWheelInput::SCROLLDELTA_PAGE: {
    1897           0 :       return ScrollInputMethod::ApzWheelPage;
    1898             :     }
    1899             :     case ScrollWheelInput::SCROLLDELTA_PIXEL: {
    1900           0 :       return ScrollInputMethod::ApzWheelPixel;
    1901             :     }
    1902             :   }
    1903           0 :   MOZ_ASSERT_UNREACHABLE("Invalid value");
    1904             :   return ScrollInputMethod::ApzWheelLine;
    1905             : }
    1906             : 
    1907           0 : nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
    1908             : {
    1909           0 :   ParentLayerPoint delta = GetScrollWheelDelta(aEvent);
    1910             :   APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str());
    1911             : 
    1912           0 :   if ((delta.x || delta.y) && !CanScrollWithWheel(delta)) {
    1913             :     // We can't scroll this apz anymore, so we simply drop the event.
    1914           0 :     if (mInputQueue->GetActiveWheelTransaction() &&
    1915           0 :         gfxPrefs::MouseScrollTestingEnabled()) {
    1916           0 :       if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    1917           0 :         controller->NotifyMozMouseScrollEvent(
    1918           0 :           mFrameMetrics.GetScrollId(),
    1919           0 :           NS_LITERAL_STRING("MozMouseScrollFailed"));
    1920             :       }
    1921             :     }
    1922           0 :     return nsEventStatus_eConsumeNoDefault;
    1923             :   }
    1924             : 
    1925           0 :   if (delta.x == 0 && delta.y == 0) {
    1926             :     // Avoid spurious state changes and unnecessary work
    1927           0 :     return nsEventStatus_eIgnore;
    1928             :   }
    1929             : 
    1930           0 :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    1931           0 :       (uint32_t) ScrollInputMethodForWheelDeltaType(aEvent.mDeltaType));
    1932             : 
    1933             : 
    1934           0 :   switch (aEvent.mScrollMode) {
    1935             :     case ScrollWheelInput::SCROLLMODE_INSTANT: {
    1936             : 
    1937             :       // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
    1938             :       // next snap point. Check for this, and adjust the delta to take into
    1939             :       // account the snap point.
    1940           0 :       CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
    1941           0 :       MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
    1942             : 
    1943             :       ScreenPoint distance = ToScreenCoordinates(
    1944           0 :         ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
    1945             : 
    1946           0 :       CancelAnimation();
    1947             : 
    1948           0 :       MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
    1949             :       OverscrollHandoffState handoffState(
    1950           0 :           *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(),
    1951             :           distance,
    1952           0 :           ScrollSource::Wheel);
    1953           0 :       ParentLayerPoint startPoint = aEvent.mLocalOrigin;
    1954           0 :       ParentLayerPoint endPoint = aEvent.mLocalOrigin - delta;
    1955           0 :       CallDispatchScroll(startPoint, endPoint, handoffState);
    1956             : 
    1957           0 :       SetState(NOTHING);
    1958             : 
    1959             :       // The calls above handle their own locking; moreover,
    1960             :       // ToScreenCoordinates() and CallDispatchScroll() can grab the tree lock.
    1961           0 :       ReentrantMonitorAutoEnter lock(mMonitor);
    1962           0 :       RequestContentRepaint();
    1963             : 
    1964           0 :       break;
    1965             :     }
    1966             : 
    1967             :     case ScrollWheelInput::SCROLLMODE_SMOOTH: {
    1968             :       // The lock must be held across the entire update operation, so the
    1969             :       // compositor doesn't end the animation before we get a chance to
    1970             :       // update it.
    1971           0 :       ReentrantMonitorAutoEnter lock(mMonitor);
    1972             : 
    1973             :       // Perform scroll snapping if appropriate.
    1974           0 :       CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
    1975             :       // If we're already in a wheel scroll or smooth scroll animation,
    1976             :       // the delta is applied to its destination, not to the current
    1977             :       // scroll position. Take this into account when finding a snap point.
    1978           0 :       if (mState == WHEEL_SCROLL) {
    1979           0 :         startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
    1980           0 :       } else if (mState == SMOOTH_SCROLL) {
    1981           0 :         startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
    1982           0 :       } else if (mState == KEYBOARD_SCROLL) {
    1983           0 :         startPosition = mAnimation->AsKeyboardScrollAnimation()->GetDestination();
    1984             :       }
    1985           0 :       if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
    1986             :         // If we're scroll snapping, use a smooth scroll animation to get
    1987             :         // the desired physics. Note that SmoothScrollTo() will re-use an
    1988             :         // existing smooth scroll animation if there is one.
    1989             :         APZC_LOG("%p wheel scrolling to snap point %s\n", this, Stringify(startPosition).c_str());
    1990           0 :         SmoothScrollTo(startPosition);
    1991           0 :         break;
    1992             :       }
    1993             : 
    1994             :       // Otherwise, use a wheel scroll animation, also reusing one if possible.
    1995           0 :       if (mState != WHEEL_SCROLL) {
    1996           0 :         CancelAnimation();
    1997           0 :         SetState(WHEEL_SCROLL);
    1998             : 
    1999           0 :         nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
    2000             :         StartAnimation(new WheelScrollAnimation(
    2001           0 :           *this, initialPosition, aEvent.mDeltaType));
    2002             :       }
    2003             : 
    2004             :       nsPoint deltaInAppUnits =
    2005           0 :         CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
    2006             :       // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
    2007             :       // appunits/second. We perform a cast to ParentLayerPoints/ms without a
    2008             :       // conversion so that the scroll duration isn't affected by zoom
    2009             :       nsPoint velocity =
    2010           0 :         CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
    2011             : 
    2012           0 :       WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
    2013           0 :       animation->UpdateDelta(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
    2014           0 :       break;
    2015             :     }
    2016             :   }
    2017             : 
    2018           0 :   return nsEventStatus_eConsumeNoDefault;
    2019             : }
    2020             : 
    2021             : void
    2022           0 : AsyncPanZoomController::NotifyMozMouseScrollEvent(const nsString& aString) const
    2023             : {
    2024           0 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    2025           0 :   if (!controller) {
    2026           0 :     return;
    2027             :   }
    2028             : 
    2029           0 :   controller->NotifyMozMouseScrollEvent(mFrameMetrics.GetScrollId(), aString);
    2030             : }
    2031             : 
    2032           0 : nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
    2033             :   APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
    2034             : 
    2035           0 :   mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
    2036           0 :   mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
    2037           0 :   MOZ_ASSERT(GetCurrentPanGestureBlock());
    2038           0 :   GetCurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
    2039             : 
    2040           0 :   return nsEventStatus_eConsumeNoDefault;
    2041             : }
    2042             : 
    2043           0 : nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
    2044             :   APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
    2045             : 
    2046           0 :   mX.CancelGesture();
    2047           0 :   mY.CancelGesture();
    2048             : 
    2049           0 :   return nsEventStatus_eConsumeNoDefault;
    2050             : }
    2051             : 
    2052             : 
    2053           0 : nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
    2054             :   APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
    2055             : 
    2056           0 :   if (mState == SMOOTH_SCROLL) {
    2057             :     // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
    2058           0 :     CancelAnimation();
    2059             :   }
    2060             : 
    2061           0 :   mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
    2062           0 :   mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
    2063             : 
    2064           0 :   if (GetAxisLockMode() == FREE) {
    2065           0 :     SetState(PANNING);
    2066           0 :     return nsEventStatus_eConsumeNoDefault;
    2067             :   }
    2068             : 
    2069           0 :   float dx = aEvent.mPanDisplacement.x, dy = aEvent.mPanDisplacement.y;
    2070             : 
    2071           0 :   if (dx || dy) {
    2072           0 :     double angle = atan2(dy, dx); // range [-pi, pi]
    2073           0 :     angle = fabs(angle); // range [0, pi]
    2074           0 :     HandlePanning(angle);
    2075             :   } else {
    2076           0 :     SetState(PANNING);
    2077             :   }
    2078             : 
    2079             :   // Call into OnPan in order to process any delta included in this event.
    2080           0 :   OnPan(aEvent, true);
    2081             : 
    2082           0 :   return nsEventStatus_eConsumeNoDefault;
    2083             : }
    2084             : 
    2085           0 : nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
    2086             :   APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
    2087             : 
    2088           0 :   if (mState == SMOOTH_SCROLL) {
    2089           0 :     if (!aFingersOnTouchpad) {
    2090             :       // When a SMOOTH_SCROLL scroll is being processed on a frame, mouse
    2091             :       // wheel and trackpad momentum scroll position updates will not cancel the
    2092             :       // SMOOTH_SCROLL scroll animations, enabling scripts that depend on
    2093             :       // them to be responsive without forcing the user to wait for the momentum
    2094             :       // scrolling to completely stop.
    2095           0 :       return nsEventStatus_eConsumeNoDefault;
    2096             :     }
    2097             : 
    2098             :     // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
    2099           0 :     CancelAnimation();
    2100             :   }
    2101             : 
    2102           0 :   if (mState == NOTHING) {
    2103             :     // This event block was interrupted by something else. If the user's fingers
    2104             :     // are still on on the touchpad we want to resume scrolling, otherwise we
    2105             :     // ignore the rest of the scroll gesture.
    2106           0 :     if (!aFingersOnTouchpad) {
    2107           0 :       return nsEventStatus_eConsumeNoDefault;
    2108             :     }
    2109             :     // Resume / restart the pan.
    2110             :     // PanBegin will call back into this function with mState == PANNING.
    2111           0 :     return OnPanBegin(aEvent);
    2112             :   }
    2113             : 
    2114             :   // Note that there is a multiplier that applies onto the "physical" pan
    2115             :   // displacement (how much the user's fingers moved) that produces the "logical"
    2116             :   // pan displacement (how much the page should move). For some of the code
    2117             :   // below it makes more sense to use the physical displacement rather than
    2118             :   // the logical displacement, and vice-versa.
    2119           0 :   ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement;
    2120           0 :   ParentLayerPoint logicalPanDisplacement = aEvent.UserMultipliedLocalPanDisplacement();
    2121             : 
    2122             :   // We need to update the axis velocity in order to get a useful display port
    2123             :   // size and position. We need to do so even if this is a momentum pan (i.e.
    2124             :   // aFingersOnTouchpad == false); in that case the "with touch" part is not
    2125             :   // really appropriate, so we may want to rethink this at some point.
    2126           0 :   mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.x, logicalPanDisplacement.x, aEvent.mTime);
    2127           0 :   mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.y, logicalPanDisplacement.y, aEvent.mTime);
    2128             : 
    2129           0 :   HandlePanningUpdate(physicalPanDisplacement);
    2130             : 
    2131             :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    2132           0 :       (uint32_t) ScrollInputMethod::ApzPanGesture);
    2133             : 
    2134           0 :   ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y));
    2135           0 :   MOZ_ASSERT(GetCurrentPanGestureBlock());
    2136             :   OverscrollHandoffState handoffState(
    2137           0 :       *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(),
    2138             :       panDistance,
    2139           0 :       ScrollSource::Wheel);
    2140             : 
    2141             :   // Create fake "touch" positions that will result in the desired scroll motion.
    2142             :   // Note that the pan displacement describes the change in scroll position:
    2143             :   // positive displacement values mean that the scroll position increases.
    2144             :   // However, an increase in scroll position means that the scrolled contents
    2145             :   // are moved to the left / upwards. Since our simulated "touches" determine
    2146             :   // the motion of the scrolled contents, not of the scroll position, they need
    2147             :   // to move in the opposite direction of the pan displacement.
    2148           0 :   ParentLayerPoint startPoint = aEvent.mLocalPanStartPoint;
    2149           0 :   ParentLayerPoint endPoint = aEvent.mLocalPanStartPoint - logicalPanDisplacement;
    2150           0 :   CallDispatchScroll(startPoint, endPoint, handoffState);
    2151             : 
    2152           0 :   return nsEventStatus_eConsumeNoDefault;
    2153             : }
    2154             : 
    2155           0 : nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
    2156             :   APZC_LOG("%p got a pan-end in state %d\n", this, mState);
    2157             : 
    2158             :   // Call into OnPan in order to process any delta included in this event.
    2159           0 :   OnPan(aEvent, true);
    2160             : 
    2161           0 :   mX.EndTouch(aEvent.mTime);
    2162           0 :   mY.EndTouch(aEvent.mTime);
    2163             : 
    2164             :   // Drop any velocity on axes where we don't have room to scroll anyways
    2165             :   // (in this APZC, or an APZC further in the handoff chain).
    2166             :   // This ensures that we don't enlarge the display port unnecessarily.
    2167           0 :   MOZ_ASSERT(GetCurrentPanGestureBlock());
    2168             :   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
    2169           0 :     GetCurrentPanGestureBlock()->GetOverscrollHandoffChain();
    2170           0 :   if (!overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::HORIZONTAL)) {
    2171           0 :     mX.SetVelocity(0);
    2172             :   }
    2173           0 :   if (!overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::VERTICAL)) {
    2174           0 :     mY.SetVelocity(0);
    2175             :   }
    2176             : 
    2177           0 :   SetState(NOTHING);
    2178           0 :   RequestContentRepaint();
    2179             : 
    2180           0 :   if (!aEvent.mFollowedByMomentum) {
    2181           0 :     ScrollSnap();
    2182             :   }
    2183             : 
    2184           0 :   return nsEventStatus_eConsumeNoDefault;
    2185             : }
    2186             : 
    2187           0 : nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
    2188             :   APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
    2189             : 
    2190           0 :   if (mState == SMOOTH_SCROLL) {
    2191             :     // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
    2192           0 :     CancelAnimation();
    2193             :   }
    2194             : 
    2195           0 :   SetState(PAN_MOMENTUM);
    2196           0 :   ScrollSnapToDestination();
    2197             : 
    2198             :   // Call into OnPan in order to process any delta included in this event.
    2199           0 :   OnPan(aEvent, false);
    2200             : 
    2201           0 :   return nsEventStatus_eConsumeNoDefault;
    2202             : }
    2203             : 
    2204           0 : nsEventStatus AsyncPanZoomController::OnPanMomentumEnd(const PanGestureInput& aEvent) {
    2205             :   APZC_LOG("%p got a pan-momentumend in state %d\n", this, mState);
    2206             : 
    2207             :   // Call into OnPan in order to process any delta included in this event.
    2208           0 :   OnPan(aEvent, false);
    2209             : 
    2210             :   // We need to reset the velocity to zero. We don't really have a "touch"
    2211             :   // here because the touch has already ended long before the momentum
    2212             :   // animation started, but I guess it doesn't really matter for now.
    2213           0 :   mX.CancelGesture();
    2214           0 :   mY.CancelGesture();
    2215           0 :   SetState(NOTHING);
    2216             : 
    2217           0 :   RequestContentRepaint();
    2218             : 
    2219           0 :   return nsEventStatus_eConsumeNoDefault;
    2220             : }
    2221             : 
    2222           0 : nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
    2223             :   APZC_LOG("%p got a long-press in state %d\n", this, mState);
    2224           0 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    2225           0 :   if (controller) {
    2226           0 :     LayoutDevicePoint geckoScreenPoint;
    2227           0 :     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
    2228           0 :       TouchBlockState* touch = GetCurrentTouchBlock();
    2229           0 :       if (!touch) {
    2230             :         APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
    2231           0 :         return nsEventStatus_eIgnore;
    2232             :       }
    2233           0 :       if (touch->IsDuringFastFling()) {
    2234             :         APZC_LOG("%p dropping long-press because of fast fling\n", this);
    2235           0 :         return nsEventStatus_eIgnore;
    2236             :       }
    2237           0 :       uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
    2238           0 :       controller->HandleTap(TapType::eLongTap, geckoScreenPoint, aEvent.modifiers, GetGuid(), blockId);
    2239           0 :       return nsEventStatus_eConsumeNoDefault;
    2240             :     }
    2241             :   }
    2242           0 :   return nsEventStatus_eIgnore;
    2243             : }
    2244             : 
    2245           0 : nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
    2246             :   APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
    2247           0 :   return GenerateSingleTap(TapType::eLongTapUp, aEvent.mPoint, aEvent.modifiers);
    2248             : }
    2249             : 
    2250           0 : nsEventStatus AsyncPanZoomController::GenerateSingleTap(TapType aType,
    2251             :       const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
    2252           0 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    2253           0 :   if (controller) {
    2254           0 :     LayoutDevicePoint geckoScreenPoint;
    2255           0 :     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
    2256           0 :       TouchBlockState* touch = GetCurrentTouchBlock();
    2257             :       // |touch| may be null in the case where this function is
    2258             :       // invoked by GestureEventListener on a timeout. In that case we already
    2259             :       // verified that the single tap is allowed so we let it through.
    2260             :       // XXX there is a bug here that in such a case the touch block that
    2261             :       // generated this tap will not get its mSingleTapOccurred flag set.
    2262             :       // See https://bugzilla.mozilla.org/show_bug.cgi?id=1256344#c6
    2263           0 :       if (touch) {
    2264           0 :         if (touch->IsDuringFastFling()) {
    2265             :           APZC_LOG("%p dropping single-tap because it was during a fast-fling\n", this);
    2266           0 :           return nsEventStatus_eIgnore;
    2267             :         }
    2268           0 :         touch->SetSingleTapOccurred();
    2269             :       }
    2270             :       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
    2271             :       // calling controller->HandleTap directly might mean that content receives
    2272             :       // the single tap message before the corresponding touch-up. To avoid that we
    2273             :       // schedule the singletap message to run on the next spin of the event loop.
    2274             :       // See bug 965381 for the issue this was causing.
    2275             :       RefPtr<Runnable> runnable =
    2276             :         NewRunnableMethod<TapType,
    2277             :                           LayoutDevicePoint,
    2278             :                           mozilla::Modifiers,
    2279             :                           ScrollableLayerGuid,
    2280           0 :                           uint64_t>("layers::GeckoContentController::HandleTap",
    2281             :                                     controller,
    2282             :                                     &GeckoContentController::HandleTap,
    2283             :                                     aType,
    2284             :                                     geckoScreenPoint,
    2285             :                                     aModifiers,
    2286           0 :                                     GetGuid(),
    2287           0 :                                     touch ? touch->GetBlockId() : 0);
    2288             : 
    2289           0 :       controller->PostDelayedTask(runnable.forget(), 0);
    2290           0 :       return nsEventStatus_eConsumeNoDefault;
    2291             :     }
    2292             :   }
    2293           0 :   return nsEventStatus_eIgnore;
    2294             : }
    2295             : 
    2296           0 : void AsyncPanZoomController::OnTouchEndOrCancel() {
    2297           0 :   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    2298           0 :     MOZ_ASSERT(GetCurrentTouchBlock());
    2299           0 :     controller->NotifyAPZStateChange(
    2300           0 :         GetGuid(), APZStateChange::eEndTouch, GetCurrentTouchBlock()->SingleTapOccurred());
    2301             :   }
    2302           0 : }
    2303             : 
    2304           0 : nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
    2305             :   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
    2306             :   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
    2307             :   // sending event to content
    2308           0 :   MOZ_ASSERT(GetCurrentTouchBlock());
    2309           0 :   if (!(mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
    2310           0 :     return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
    2311             :   }
    2312           0 :   return nsEventStatus_eIgnore;
    2313             : }
    2314             : 
    2315           0 : nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
    2316             :   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
    2317           0 :   return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
    2318             : }
    2319             : 
    2320           0 : nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
    2321             :   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
    2322           0 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    2323           0 :   if (controller) {
    2324           0 :     MOZ_ASSERT(GetCurrentTouchBlock());
    2325           0 :     if (mZoomConstraints.mAllowDoubleTapZoom && GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
    2326           0 :       LayoutDevicePoint geckoScreenPoint;
    2327           0 :       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
    2328           0 :         controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
    2329           0 :             aEvent.modifiers, GetGuid(), GetCurrentTouchBlock()->GetBlockId());
    2330             :       }
    2331             :     }
    2332           0 :     return nsEventStatus_eConsumeNoDefault;
    2333             :   }
    2334           0 :   return nsEventStatus_eIgnore;
    2335             : }
    2336             : 
    2337           0 : nsEventStatus AsyncPanZoomController::OnSecondTap(const TapGestureInput& aEvent)
    2338             : {
    2339             :   APZC_LOG("%p got a second-tap in state %d\n", this, mState);
    2340           0 :   return GenerateSingleTap(TapType::eSecondTap, aEvent.mPoint, aEvent.modifiers);
    2341             : }
    2342             : 
    2343           0 : nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
    2344             :   APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
    2345             :   // XXX: Implement this.
    2346           0 :   return nsEventStatus_eIgnore;
    2347             : }
    2348             : 
    2349             : 
    2350           0 : ScreenToParentLayerMatrix4x4 AsyncPanZoomController::GetTransformToThis() const {
    2351           0 :   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
    2352           0 :     return treeManagerLocal->GetScreenToApzcTransform(this);
    2353             :   }
    2354           0 :   return ScreenToParentLayerMatrix4x4();
    2355             : }
    2356             : 
    2357           0 : ScreenPoint AsyncPanZoomController::ToScreenCoordinates(const ParentLayerPoint& aVector,
    2358             :                                                         const ParentLayerPoint& aAnchor) const {
    2359           0 :   return TransformVector(GetTransformToThis().Inverse(), aVector, aAnchor);
    2360             : }
    2361             : 
    2362             : // TODO: figure out a good way to check the w-coordinate is positive and return the result
    2363           0 : ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPoint& aVector,
    2364             :                                                                   const ScreenPoint& aAnchor) const {
    2365           0 :   return TransformVector(GetTransformToThis(), aVector, aAnchor);
    2366             : }
    2367             : 
    2368           0 : bool AsyncPanZoomController::Contains(const ScreenIntPoint& aPoint) const
    2369             : {
    2370           0 :   ScreenToParentLayerMatrix4x4 transformToThis = GetTransformToThis();
    2371           0 :   Maybe<ParentLayerIntPoint> point = UntransformBy(transformToThis, aPoint);
    2372           0 :   if (!point) {
    2373           0 :     return false;
    2374             :   }
    2375             : 
    2376           0 :   ParentLayerIntRect cb;
    2377             :   {
    2378           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    2379           0 :     GetFrameMetrics().GetCompositionBounds().ToIntRect(&cb);
    2380             :   }
    2381           0 :   return cb.Contains(*point);
    2382             : }
    2383             : 
    2384           0 : ScreenCoord AsyncPanZoomController::PanDistance() const {
    2385           0 :   ParentLayerPoint panVector;
    2386           0 :   ParentLayerPoint panStart;
    2387             :   {
    2388           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    2389           0 :     panVector = ParentLayerPoint(mX.PanDistance(), mY.PanDistance());
    2390           0 :     panStart = PanStart();
    2391             :   }
    2392           0 :   return ToScreenCoordinates(panVector, panStart).Length();
    2393             : }
    2394             : 
    2395           0 : ParentLayerPoint AsyncPanZoomController::PanStart() const {
    2396           0 :   return ParentLayerPoint(mX.PanStart(), mY.PanStart());
    2397             : }
    2398             : 
    2399           4 : const ParentLayerPoint AsyncPanZoomController::GetVelocityVector() const {
    2400           4 :   return ParentLayerPoint(mX.GetVelocity(), mY.GetVelocity());
    2401             : }
    2402             : 
    2403           0 : void AsyncPanZoomController::SetVelocityVector(const ParentLayerPoint& aVelocityVector) {
    2404           0 :   mX.SetVelocity(aVelocityVector.x);
    2405           0 :   mY.SetVelocity(aVelocityVector.y);
    2406           0 : }
    2407             : 
    2408           0 : void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
    2409             :   // Handling of cross sliding will need to be added in this method after touch-action released
    2410             :   // enabled by default.
    2411           0 :   MOZ_ASSERT(GetCurrentTouchBlock());
    2412             :   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
    2413           0 :     GetCurrentInputBlock()->GetOverscrollHandoffChain();
    2414           0 :   bool canScrollHorizontal = !mX.IsAxisLocked() &&
    2415           0 :     overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::HORIZONTAL);
    2416           0 :   bool canScrollVertical = !mY.IsAxisLocked() &&
    2417           0 :     overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::VERTICAL);
    2418           0 :   if (GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
    2419           0 :     if (canScrollHorizontal && canScrollVertical) {
    2420           0 :       if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
    2421           0 :         mY.SetAxisLocked(true);
    2422           0 :         SetState(PANNING_LOCKED_X);
    2423           0 :       } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
    2424           0 :         mX.SetAxisLocked(true);
    2425           0 :         SetState(PANNING_LOCKED_Y);
    2426             :       } else {
    2427           0 :         SetState(PANNING);
    2428             :       }
    2429           0 :     } else if (canScrollHorizontal || canScrollVertical) {
    2430           0 :       SetState(PANNING);
    2431             :     } else {
    2432           0 :       SetState(NOTHING);
    2433             :     }
    2434           0 :   } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningX()) {
    2435             :     // Using bigger angle for panning to keep behavior consistent
    2436             :     // with IE.
    2437           0 :     if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
    2438           0 :       mY.SetAxisLocked(true);
    2439           0 :       SetState(PANNING_LOCKED_X);
    2440           0 :       mPanDirRestricted = true;
    2441             :     } else {
    2442             :       // Don't treat these touches as pan/zoom movements since 'touch-action' value
    2443             :       // requires it.
    2444           0 :       SetState(NOTHING);
    2445             :     }
    2446           0 :   } else if (GetCurrentTouchBlock()->TouchActionAllowsPanningY()) {
    2447           0 :     if (IsCloseToVertical(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
    2448           0 :       mX.SetAxisLocked(true);
    2449           0 :       SetState(PANNING_LOCKED_Y);
    2450           0 :       mPanDirRestricted = true;
    2451             :     } else {
    2452           0 :       SetState(NOTHING);
    2453             :     }
    2454             :   } else {
    2455           0 :     SetState(NOTHING);
    2456             :   }
    2457           0 :   if (!IsInPanningState()) {
    2458             :     // If we didn't enter a panning state because touch-action disallowed it,
    2459             :     // make sure to clear any leftover velocity from the pre-threshold
    2460             :     // touchmoves.
    2461           0 :     mX.SetVelocity(0);
    2462           0 :     mY.SetVelocity(0);
    2463             :   }
    2464           0 : }
    2465             : 
    2466           0 : void AsyncPanZoomController::HandlePanning(double aAngle) {
    2467           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2468           0 :   MOZ_ASSERT(GetCurrentInputBlock());
    2469             :   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
    2470           0 :     GetCurrentInputBlock()->GetOverscrollHandoffChain();
    2471           0 :   bool canScrollHorizontal = !mX.IsAxisLocked() &&
    2472           0 :     overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::HORIZONTAL);
    2473           0 :   bool canScrollVertical = !mY.IsAxisLocked() &&
    2474           0 :     overscrollHandoffChain->CanScrollInDirection(this, ScrollDirection::VERTICAL);
    2475             : 
    2476           0 :   if (!canScrollHorizontal || !canScrollVertical) {
    2477           0 :     SetState(PANNING);
    2478           0 :   } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
    2479           0 :     mY.SetAxisLocked(true);
    2480           0 :     if (canScrollHorizontal) {
    2481           0 :       SetState(PANNING_LOCKED_X);
    2482             :     }
    2483           0 :   } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
    2484           0 :     mX.SetAxisLocked(true);
    2485           0 :     if (canScrollVertical) {
    2486           0 :       SetState(PANNING_LOCKED_Y);
    2487             :     }
    2488             :   } else {
    2489           0 :     SetState(PANNING);
    2490             :   }
    2491           0 : }
    2492             : 
    2493           0 : void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
    2494             :   // If we're axis-locked, check if the user is trying to break the lock
    2495           0 :   if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
    2496             : 
    2497           0 :     double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi]
    2498           0 :     angle = fabs(angle); // range [0, pi]
    2499             : 
    2500           0 :     float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
    2501             : 
    2502           0 :     if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
    2503           0 :       if (mState == PANNING_LOCKED_X) {
    2504           0 :         if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
    2505           0 :           mY.SetAxisLocked(false);
    2506           0 :           SetState(PANNING);
    2507             :         }
    2508           0 :       } else if (mState == PANNING_LOCKED_Y) {
    2509           0 :         if (!IsCloseToVertical(angle, gfxPrefs::APZAxisBreakoutAngle())) {
    2510           0 :           mX.SetAxisLocked(false);
    2511           0 :           SetState(PANNING);
    2512             :         }
    2513             :       }
    2514             :     }
    2515             :   }
    2516           0 : }
    2517             : 
    2518             : nsEventStatus
    2519           0 : AsyncPanZoomController::StartPanning(const ParentLayerPoint& aStartPoint) {
    2520           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2521             : 
    2522           0 :   float dx = mX.PanDistance(aStartPoint.x);
    2523           0 :   float dy = mY.PanDistance(aStartPoint.y);
    2524             : 
    2525           0 :   double angle = atan2(dy, dx); // range [-pi, pi]
    2526           0 :   angle = fabs(angle); // range [0, pi]
    2527             : 
    2528           0 :   if (gfxPrefs::TouchActionEnabled()) {
    2529           0 :     HandlePanningWithTouchAction(angle);
    2530             :   } else {
    2531           0 :     if (GetAxisLockMode() == FREE) {
    2532           0 :       SetState(PANNING);
    2533             :     } else {
    2534           0 :       HandlePanning(angle);
    2535             :     }
    2536             :   }
    2537             : 
    2538           0 :   if (IsInPanningState()) {
    2539           0 :     if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    2540           0 :       controller->NotifyAPZStateChange(GetGuid(), APZStateChange::eStartPanning);
    2541             :     }
    2542           0 :     return nsEventStatus_eConsumeNoDefault;
    2543             :   }
    2544             :   // Don't consume an event that didn't trigger a panning.
    2545           0 :   return nsEventStatus_eIgnore;
    2546             : }
    2547             : 
    2548           0 : void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
    2549           0 :   ParentLayerPoint point = GetFirstTouchPoint(aEvent);
    2550           0 :   mX.UpdateWithTouchAtDevicePoint(point.x, 0, aEvent.mTime);
    2551           0 :   mY.UpdateWithTouchAtDevicePoint(point.y, 0, aEvent.mTime);
    2552           0 : }
    2553             : 
    2554           0 : bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint,
    2555             :                                            ParentLayerPoint& aEndPoint,
    2556             :                                            OverscrollHandoffState& aOverscrollHandoffState) {
    2557             : 
    2558             :   // "start - end" rather than "end - start" because e.g. moving your finger
    2559             :   // down (*positive* direction along y axis) causes the vertical scroll offset
    2560             :   // to *decrease* as the page follows your finger.
    2561           0 :   ParentLayerPoint displacement = aStartPoint - aEndPoint;
    2562             : 
    2563           0 :   ParentLayerPoint overscroll;  // will be used outside monitor block
    2564             : 
    2565             :   // If the direction of panning is reversed within the same input block,
    2566             :   // a later event in the block could potentially scroll an APZC earlier
    2567             :   // in the handoff chain, than an earlier event in the block (because
    2568             :   // the earlier APZC was scrolled to its extent in the original direction).
    2569             :   // We want to disallow this.
    2570           0 :   bool scrollThisApzc = false;
    2571           0 :   if (InputBlockState* block = GetCurrentInputBlock()) {
    2572           0 :     scrollThisApzc = !block->GetScrolledApzc() || block->IsDownchainOfScrolledApzc(this);
    2573             :   }
    2574             : 
    2575           0 :   if (scrollThisApzc) {
    2576           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    2577             : 
    2578           0 :     ParentLayerPoint adjustedDisplacement;
    2579             :     bool forceVerticalOverscroll =
    2580           0 :       (aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
    2581           0 :        !mScrollMetadata.AllowVerticalScrollWithWheel());
    2582           0 :     bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y,
    2583           0 :                                           forceVerticalOverscroll);
    2584           0 :     bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
    2585             : 
    2586           0 :     if (xChanged || yChanged) {
    2587           0 :       ScheduleComposite();
    2588             :     }
    2589             : 
    2590           0 :     if (!IsZero(adjustedDisplacement)) {
    2591           0 :       ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
    2592           0 :       if (InputBlockState* block = GetCurrentInputBlock()) {
    2593             : #if defined(MOZ_WIDGET_ANDROID)
    2594             :         if (block->AsTouchBlock() && (block->GetScrolledApzc() != this) && IsRootContent()) {
    2595             :           if (APZCTreeManager* manager = GetApzcTreeManager()) {
    2596             :             AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
    2597             :             MOZ_ASSERT(animator);
    2598             :             animator->SetScrollingRootContent();
    2599             :           }
    2600             :         }
    2601             : #endif
    2602           0 :         block->SetScrolledApzc(this);
    2603             :       }
    2604           0 :       ScheduleCompositeAndMaybeRepaint();
    2605           0 :       UpdateSharedCompositorFrameMetrics();
    2606             :     }
    2607             : 
    2608             :     // Adjust the start point to reflect the consumed portion of the scroll.
    2609           0 :     aStartPoint = aEndPoint + overscroll;
    2610             :   } else {
    2611           0 :     overscroll = displacement;
    2612             :   }
    2613             : 
    2614             :   // If we consumed the entire displacement as a normal scroll, great.
    2615           0 :   if (IsZero(overscroll)) {
    2616           0 :     return true;
    2617             :   }
    2618             : 
    2619           0 :   if (AllowScrollHandoffInCurrentBlock()) {
    2620             :     // If there is overscroll, first try to hand it off to an APZC later
    2621             :     // in the handoff chain to consume (either as a normal scroll or as
    2622             :     // overscroll).
    2623             :     // Note: "+ overscroll" rather than "- overscroll" because "overscroll"
    2624             :     // is what's left of "displacement", and "displacement" is "start - end".
    2625           0 :     ++aOverscrollHandoffState.mChainIndex;
    2626           0 :     CallDispatchScroll(aStartPoint, aEndPoint, aOverscrollHandoffState);
    2627             : 
    2628           0 :     overscroll = aStartPoint - aEndPoint;
    2629           0 :     if (IsZero(overscroll)) {
    2630           0 :       return true;
    2631             :     }
    2632             :   }
    2633             : 
    2634             :   // If there is no APZC later in the handoff chain that accepted the
    2635             :   // overscroll, try to accept it ourselves. We only accept it if we
    2636             :   // are pannable.
    2637             :   APZC_LOG("%p taking overscroll during panning\n", this);
    2638           0 :   OverscrollForPanning(overscroll, aOverscrollHandoffState.mPanDistance);
    2639           0 :   aStartPoint = aEndPoint + overscroll;
    2640             : 
    2641           0 :   return IsZero(overscroll);
    2642             : }
    2643             : 
    2644           0 : void AsyncPanZoomController::OverscrollForPanning(ParentLayerPoint& aOverscroll,
    2645             :                                                   const ScreenPoint& aPanDistance) {
    2646             :   // Only allow entering overscroll along an axis if the pan distance along
    2647             :   // that axis is greater than the pan distance along the other axis by a
    2648             :   // configurable factor. If we are already overscrolled, don't check this.
    2649           0 :   if (!IsOverscrolled()) {
    2650           0 :     if (aPanDistance.x < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.y) {
    2651           0 :       aOverscroll.x = 0;
    2652             :     }
    2653           0 :     if (aPanDistance.y < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.x) {
    2654           0 :       aOverscroll.y = 0;
    2655             :     }
    2656             :   }
    2657             : 
    2658           0 :   OverscrollBy(aOverscroll);
    2659           0 : }
    2660             : 
    2661           0 : void AsyncPanZoomController::OverscrollBy(ParentLayerPoint& aOverscroll) {
    2662           0 :   if (!gfxPrefs::APZOverscrollEnabled()) {
    2663           0 :     return;
    2664             :   }
    2665             : 
    2666           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2667             :   // Do not go into overscroll in a direction in which we have no room to
    2668             :   // scroll to begin with.
    2669           0 :   bool xCanScroll = mX.CanScroll();
    2670           0 :   bool yCanScroll = mY.CanScroll();
    2671           0 :   bool xConsumed = FuzzyEqualsAdditive(aOverscroll.x, 0.0f, COORDINATE_EPSILON);
    2672           0 :   bool yConsumed = FuzzyEqualsAdditive(aOverscroll.y, 0.0f, COORDINATE_EPSILON);
    2673             : 
    2674           0 :   bool shouldOverscrollX = xCanScroll && !xConsumed;
    2675           0 :   bool shouldOverscrollY = yCanScroll && !yConsumed;
    2676             : 
    2677           0 :   mOverscrollEffect->ConsumeOverscroll(aOverscroll, shouldOverscrollX, shouldOverscrollY);
    2678             : }
    2679             : 
    2680           0 : RefPtr<const OverscrollHandoffChain> AsyncPanZoomController::BuildOverscrollHandoffChain() {
    2681           0 :   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
    2682           0 :     return treeManagerLocal->BuildOverscrollHandoffChain(this);
    2683             :   }
    2684             : 
    2685             :   // This APZC IsDestroyed(). To avoid callers having to special-case this
    2686             :   // scenario, just build a 1-element chain containing ourselves.
    2687           0 :   OverscrollHandoffChain* result = new OverscrollHandoffChain;
    2688           0 :   result->Add(this);
    2689           0 :   return result;
    2690             : }
    2691             : 
    2692           0 : void AsyncPanZoomController::AcceptFling(FlingHandoffState& aHandoffState) {
    2693           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2694             : 
    2695             :   // We may have a pre-existing velocity for whatever reason (for example,
    2696             :   // a previously handed off fling). We don't want to clobber that.
    2697             :   APZC_LOG("%p accepting fling with velocity %s\n", this,
    2698             :            Stringify(aHandoffState.mVelocity).c_str());
    2699           0 :   if (mX.CanScroll()) {
    2700           0 :     mX.SetVelocity(mX.GetVelocity() + aHandoffState.mVelocity.x);
    2701           0 :     aHandoffState.mVelocity.x = 0;
    2702             :   }
    2703           0 :   if (mY.CanScroll()) {
    2704           0 :     mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y);
    2705           0 :     aHandoffState.mVelocity.y = 0;
    2706             :   }
    2707             : 
    2708             :   // If there's a scroll snap point near the predicted fling destination,
    2709             :   // scroll there using a smooth scroll animation. Otherwise, start a
    2710             :   // fling animation.
    2711           0 :   ScrollSnapToDestination();
    2712           0 :   if (mState != SMOOTH_SCROLL) {
    2713           0 :     SetState(FLING);
    2714             :     FlingAnimation *fling = new FlingAnimation(*this,
    2715           0 :         GetPlatformSpecificState(),
    2716             :         aHandoffState.mChain,
    2717           0 :         aHandoffState.mIsHandoff,
    2718           0 :         aHandoffState.mScrolledApzc);
    2719           0 :     StartAnimation(fling);
    2720             :   }
    2721           0 : }
    2722             : 
    2723           0 : bool AsyncPanZoomController::AttemptFling(FlingHandoffState& aHandoffState) {
    2724             :   // If we are pannable, take over the fling ourselves.
    2725           0 :   if (IsPannable()) {
    2726           0 :     AcceptFling(aHandoffState);
    2727           0 :     return true;
    2728             :   }
    2729             : 
    2730           0 :   return false;
    2731             : }
    2732             : 
    2733           0 : void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
    2734             :                                                    const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
    2735             :                                                    const RefPtr<const AsyncPanZoomController>& aScrolledApzc) {
    2736           0 :   APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
    2737           0 :   if (treeManagerLocal) {
    2738             :     FlingHandoffState handoffState{aVelocity,
    2739             :                                    aOverscrollHandoffChain,
    2740             :                                    true /* handoff */,
    2741           0 :                                    aScrolledApzc};
    2742           0 :     treeManagerLocal->DispatchFling(this, handoffState);
    2743           0 :     if (!IsZero(handoffState.mVelocity) && IsPannable() && gfxPrefs::APZOverscrollEnabled()) {
    2744           0 :       mOverscrollEffect->HandleFlingOverscroll(handoffState.mVelocity);
    2745             :     }
    2746             :   }
    2747           0 : }
    2748             : 
    2749           0 : void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity) {
    2750             :   // We must call BuildOverscrollHandoffChain from this deferred callback
    2751             :   // function in order to avoid a deadlock when acquiring the tree lock.
    2752           0 :   HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr);
    2753           0 : }
    2754             : 
    2755           0 : void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) {
    2756           0 :   if (mState == SMOOTH_SCROLL && mAnimation) {
    2757             :     APZC_LOG("%p updating destination on existing animation\n", this);
    2758             :     RefPtr<SmoothScrollAnimation> animation(
    2759           0 :       static_cast<SmoothScrollAnimation*>(mAnimation.get()));
    2760           0 :     animation->SetDestination(CSSPoint::ToAppUnits(aDestination));
    2761             :   } else {
    2762           0 :     CancelAnimation();
    2763           0 :     SetState(SMOOTH_SCROLL);
    2764           0 :     nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
    2765             :     // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
    2766             :     // appunits/second. We perform a cast to ParentLayerPoints/ms without a
    2767             :     // conversion so that the scroll duration isn't affected by zoom
    2768           0 :     nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
    2769           0 :                                                             mY.GetVelocity())) * 1000.0f;
    2770           0 :     nsPoint destination = CSSPoint::ToAppUnits(aDestination);
    2771             : 
    2772             :     StartAnimation(new SmoothScrollAnimation(*this,
    2773             :                                              initialPosition, initialVelocity,
    2774             :                                              destination,
    2775           0 :                                              gfxPrefs::ScrollBehaviorSpringConstant(),
    2776           0 :                                              gfxPrefs::ScrollBehaviorDampingRatio()));
    2777             :   }
    2778           0 : }
    2779             : 
    2780           0 : void AsyncPanZoomController::StartOverscrollAnimation(const ParentLayerPoint& aVelocity) {
    2781           0 :   SetState(OVERSCROLL_ANIMATION);
    2782           0 :   StartAnimation(new OverscrollAnimation(*this, aVelocity));
    2783           0 : }
    2784             : 
    2785           0 : void AsyncPanZoomController::CallDispatchScroll(ParentLayerPoint& aStartPoint,
    2786             :                                                 ParentLayerPoint& aEndPoint,
    2787             :                                                 OverscrollHandoffState& aOverscrollHandoffState) {
    2788             :   // Make a local copy of the tree manager pointer and check if it's not
    2789             :   // null before calling DispatchScroll(). This is necessary because
    2790             :   // Destroy(), which nulls out mTreeManager, could be called concurrently.
    2791           0 :   APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
    2792           0 :   if (!treeManagerLocal) {
    2793           0 :     return;
    2794             :   }
    2795             :   treeManagerLocal->DispatchScroll(this,
    2796             :                                    aStartPoint, aEndPoint,
    2797           0 :                                    aOverscrollHandoffState);
    2798             : }
    2799             : 
    2800           0 : void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
    2801           0 :   ParentLayerPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
    2802           0 :   ParentLayerPoint touchPoint = GetFirstTouchPoint(aEvent);
    2803             : 
    2804             :   ScreenPoint panDistance = ToScreenCoordinates(
    2805           0 :       ParentLayerPoint(mX.PanDistance(touchPoint.x),
    2806             :                        mY.PanDistance(touchPoint.y)),
    2807           0 :       PanStart());
    2808           0 :   HandlePanningUpdate(panDistance);
    2809             : 
    2810           0 :   UpdateWithTouchAtDevicePoint(aEvent);
    2811             : 
    2812           0 :   if (prevTouchPoint != touchPoint) {
    2813             :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
    2814           0 :         (uint32_t) ScrollInputMethod::ApzTouch);
    2815           0 :     MOZ_ASSERT(GetCurrentTouchBlock());
    2816             :     OverscrollHandoffState handoffState(
    2817           0 :         *GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
    2818             :         panDistance,
    2819           0 :         ScrollSource::Touch);
    2820           0 :     CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
    2821             :   }
    2822           0 : }
    2823             : 
    2824           0 : ParentLayerPoint AsyncPanZoomController::GetFirstTouchPoint(const MultiTouchInput& aEvent) {
    2825           0 :   return ((SingleTouchData&)aEvent.mTouches[0]).mLocalScreenPoint;
    2826             : }
    2827             : 
    2828           0 : void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
    2829             : {
    2830           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2831           0 :   mAnimation = aAnimation;
    2832           0 :   mLastSampleTime = GetFrameTime();
    2833           0 :   ScheduleComposite();
    2834           0 : }
    2835             : 
    2836           2 : void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
    2837           4 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2838             :   APZC_LOG("%p running CancelAnimation(0x%x) in state %d\n", this, aFlags, mState);
    2839             : 
    2840           2 :   if ((aFlags & ExcludeWheel) && mState == WHEEL_SCROLL) {
    2841           0 :     return;
    2842             :   }
    2843             : 
    2844           2 :   SetState(NOTHING);
    2845           2 :   mAnimation = nullptr;
    2846             :   // Since there is no animation in progress now the axes should
    2847             :   // have no velocity either. If we are dropping the velocity from a non-zero
    2848             :   // value we should trigger a repaint as the displayport margins are dependent
    2849             :   // on the velocity and the last repaint request might not have good margins
    2850             :   // any more.
    2851           2 :   bool repaint = !IsZero(GetVelocityVector());
    2852           2 :   mX.SetVelocity(0);
    2853           2 :   mY.SetVelocity(0);
    2854           2 :   mX.SetAxisLocked(false);
    2855           2 :   mY.SetAxisLocked(false);
    2856             :   // Setting the state to nothing and cancelling the animation can
    2857             :   // preempt normal mechanisms for relieving overscroll, so we need to clear
    2858             :   // overscroll here.
    2859           2 :   if (!(aFlags & ExcludeOverscroll) && IsOverscrolled()) {
    2860           0 :     ClearOverscroll();
    2861           0 :     repaint = true;
    2862             :   }
    2863             :   // Similar to relieving overscroll, we also need to snap to any snap points
    2864             :   // if appropriate.
    2865           2 :   if (aFlags & CancelAnimationFlags::ScrollSnap) {
    2866           0 :     ScrollSnap();
    2867             :   }
    2868           2 :   if (repaint) {
    2869           0 :     RequestContentRepaint();
    2870           0 :     ScheduleComposite();
    2871           0 :     UpdateSharedCompositorFrameMetrics();
    2872             :   }
    2873             : }
    2874             : 
    2875           0 : void AsyncPanZoomController::ClearOverscroll() {
    2876           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2877           0 :   mX.ClearOverscroll();
    2878           0 :   mY.ClearOverscroll();
    2879           0 : }
    2880             : 
    2881           2 : void AsyncPanZoomController::SetCompositorController(CompositorController* aCompositorController)
    2882             : {
    2883           2 :   mCompositorController = aCompositorController;
    2884           2 : }
    2885             : 
    2886           2 : void AsyncPanZoomController::SetMetricsSharingController(MetricsSharingController* aMetricsSharingController)
    2887             : {
    2888           2 :   mMetricsSharingController = aMetricsSharingController;
    2889           2 : }
    2890             : 
    2891           0 : void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
    2892             : {
    2893           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    2894             :   CSSPoint adjustment =
    2895           0 :     ViewAs<ParentLayerPixel>(aShift, PixelCastJustification::ScreenIsParentLayerForRoot)
    2896           0 :     / mFrameMetrics.GetZoom();
    2897             :   APZC_LOG("%p adjusting scroll position by %s for surface shift\n",
    2898             :     this, Stringify(adjustment).c_str());
    2899           0 :   CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
    2900           0 :   scrollOffset.y = mY.ClampOriginToScrollableRect(scrollOffset.y + adjustment.y);
    2901           0 :   scrollOffset.x = mX.ClampOriginToScrollableRect(scrollOffset.x + adjustment.x);
    2902           0 :   mFrameMetrics.SetScrollOffset(scrollOffset);
    2903           0 :   RequestContentRepaint();
    2904           0 :   UpdateSharedCompositorFrameMetrics();
    2905           0 : }
    2906             : 
    2907           0 : void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
    2908           0 :   mFrameMetrics.ScrollBy(aOffset);
    2909           0 : }
    2910             : 
    2911           0 : void AsyncPanZoomController::ScaleWithFocus(float aScale,
    2912             :                                             const CSSPoint& aFocus) {
    2913           0 :   mFrameMetrics.ZoomBy(aScale);
    2914             :   // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
    2915             :   // at the same position on the screen before and after the change in zoom. The below code
    2916             :   // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
    2917             :   // in-depth explanation of how.
    2918           0 :   mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
    2919           0 : }
    2920             : 
    2921             : /**
    2922             :  * Enlarges the displayport along both axes based on the velocity.
    2923             :  */
    2924             : static CSSSize
    2925           2 : CalculateDisplayPortSize(const CSSSize& aCompositionSize,
    2926             :                          const CSSPoint& aVelocity)
    2927             : {
    2928           2 :   bool xIsStationarySpeed = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed();
    2929           2 :   bool yIsStationarySpeed = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed();
    2930             :   float xMultiplier = xIsStationarySpeed
    2931           2 :                         ? gfxPrefs::APZXStationarySizeMultiplier()
    2932           2 :                         : gfxPrefs::APZXSkateSizeMultiplier();
    2933             :   float yMultiplier = yIsStationarySpeed
    2934           2 :                         ? gfxPrefs::APZYStationarySizeMultiplier()
    2935           2 :                         : gfxPrefs::APZYSkateSizeMultiplier();
    2936             : 
    2937           2 :   if (IsHighMemSystem() && !xIsStationarySpeed) {
    2938           0 :     xMultiplier += gfxPrefs::APZXSkateHighMemAdjust();
    2939             :   }
    2940             : 
    2941           2 :   if (IsHighMemSystem() && !yIsStationarySpeed) {
    2942           0 :     yMultiplier += gfxPrefs::APZYSkateHighMemAdjust();
    2943             :   }
    2944             : 
    2945           2 :   return aCompositionSize * CSSSize(xMultiplier, yMultiplier);
    2946             : }
    2947             : 
    2948             : /**
    2949             :  * Ensures that the displayport is at least as large as the visible area
    2950             :  * inflated by the danger zone. If this is not the case then the
    2951             :  * "AboutToCheckerboard" function in TiledContentClient.cpp will return true
    2952             :  * even in the stable state.
    2953             :  */
    2954             : static CSSSize
    2955           2 : ExpandDisplayPortToDangerZone(const CSSSize& aDisplayPortSize,
    2956             :                               const FrameMetrics& aFrameMetrics)
    2957             : {
    2958           2 :   CSSSize dangerZone(0.0f, 0.0f);
    2959           8 :   if (aFrameMetrics.LayersPixelsPerCSSPixel().xScale != 0 &&
    2960           6 :       aFrameMetrics.LayersPixelsPerCSSPixel().yScale != 0) {
    2961           6 :     dangerZone = LayerSize(
    2962           2 :       gfxPrefs::APZDangerZoneX(),
    2963           6 :       gfxPrefs::APZDangerZoneY()) / aFrameMetrics.LayersPixelsPerCSSPixel();
    2964             :   }
    2965           2 :   const CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
    2966             : 
    2967             :   const float xSize = std::max(aDisplayPortSize.width,
    2968           2 :                                compositionSize.width + (2 * dangerZone.width));
    2969             : 
    2970             :   const float ySize = std::max(aDisplayPortSize.height,
    2971           2 :                                compositionSize.height + (2 * dangerZone.height));
    2972             : 
    2973           2 :   return CSSSize(xSize, ySize);
    2974             : }
    2975             : 
    2976             : /**
    2977             :  * Attempts to redistribute any area in the displayport that would get clipped
    2978             :  * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
    2979             :  * other axis, while maintaining total displayport area.
    2980             :  */
    2981             : static void
    2982           0 : RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
    2983             :                               const CSSRect& aScrollableRect)
    2984             : {
    2985             :   // As aDisplayPortSize.height * aDisplayPortSize.width does not change,
    2986             :   // we are just scaling by the ratio and its inverse.
    2987           0 :   if (aDisplayPortSize.height > aScrollableRect.height) {
    2988           0 :     aDisplayPortSize.width *= (aDisplayPortSize.height / aScrollableRect.height);
    2989           0 :     aDisplayPortSize.height = aScrollableRect.height;
    2990           0 :   } else if (aDisplayPortSize.width > aScrollableRect.width) {
    2991           0 :     aDisplayPortSize.height *= (aDisplayPortSize.width / aScrollableRect.width);
    2992           0 :     aDisplayPortSize.width = aScrollableRect.width;
    2993             :   }
    2994           0 : }
    2995             : 
    2996             : /* static */
    2997           2 : const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
    2998             :   const FrameMetrics& aFrameMetrics,
    2999             :   const ParentLayerPoint& aVelocity)
    3000             : {
    3001           2 :   if (aFrameMetrics.IsScrollInfoLayer()) {
    3002             :     // Don't compute margins. Since we can't asynchronously scroll this frame,
    3003             :     // we don't want to paint anything more than the composition bounds.
    3004           0 :     return ScreenMargin();
    3005             :   }
    3006             : 
    3007           2 :   CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
    3008           2 :   CSSPoint velocity;
    3009           2 :   if (aFrameMetrics.GetZoom() != CSSToParentLayerScale2D(0, 0)) {
    3010           2 :     velocity = aVelocity / aFrameMetrics.GetZoom();  // avoid division by zero
    3011             :   }
    3012           2 :   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
    3013             : 
    3014             :   // Calculate the displayport size based on how fast we're moving along each axis.
    3015           2 :   CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
    3016             : 
    3017           2 :   displayPortSize = ExpandDisplayPortToDangerZone(displayPortSize, aFrameMetrics);
    3018             : 
    3019           2 :   if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
    3020           0 :     RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
    3021             :   }
    3022             : 
    3023             :   // We calculate a "displayport" here which is relative to the scroll offset.
    3024             :   // Note that the scroll offset we have here in the APZ code may not be the
    3025             :   // same as the base rect that gets used on the layout side when the displayport
    3026             :   // margins are actually applied, so it is important to only consider the
    3027             :   // displayport as margins relative to a scroll offset rather than relative to
    3028             :   // something more unchanging like the scrollable rect origin.
    3029             : 
    3030             :   // Center the displayport based on its expansion over the composition size.
    3031           2 :   CSSRect displayPort((compositionSize.width - displayPortSize.width) / 2.0f,
    3032           2 :                       (compositionSize.height - displayPortSize.height) / 2.0f,
    3033           6 :                       displayPortSize.width, displayPortSize.height);
    3034             : 
    3035             :   // Offset the displayport, depending on how fast we're moving and the
    3036             :   // estimated time it takes to paint, to try to minimise checkerboarding.
    3037           2 :   float paintFactor = kDefaultEstimatedPaintDurationMs;
    3038           2 :   displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
    3039             : 
    3040             :   APZC_LOG_FM(aFrameMetrics,
    3041             :     "Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
    3042             :     displayPort.x, displayPort.y, displayPort.width, displayPort.height,
    3043             :     ToString(aVelocity).c_str(), paintFactor);
    3044             : 
    3045           2 :   CSSMargin cssMargins;
    3046           2 :   cssMargins.left = -displayPort.x;
    3047           2 :   cssMargins.top = -displayPort.y;
    3048           2 :   cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
    3049           2 :   cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
    3050             : 
    3051           2 :   return cssMargins * aFrameMetrics.DisplayportPixelsPerCSSPixel();
    3052             : }
    3053             : 
    3054           0 : void AsyncPanZoomController::ScheduleComposite() {
    3055           0 :   if (mCompositorController) {
    3056           0 :     mCompositorController->ScheduleRenderOnCompositorThread();
    3057             :   }
    3058           0 : }
    3059             : 
    3060           0 : void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
    3061           0 :   ScheduleComposite();
    3062           0 :   RequestContentRepaint();
    3063           0 : }
    3064             : 
    3065           0 : void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
    3066           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3067           0 :   RequestContentRepaint();
    3068           0 :   UpdateSharedCompositorFrameMetrics();
    3069           0 : }
    3070             : 
    3071           0 : void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
    3072             :   APZC_LOG("%p flushing repaint for new input block\n", this);
    3073             : 
    3074           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3075           0 :   RequestContentRepaint();
    3076           0 :   UpdateSharedCompositorFrameMetrics();
    3077           0 : }
    3078             : 
    3079           0 : bool AsyncPanZoomController::SnapBackIfOverscrolled() {
    3080           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3081             :   // It's possible that we're already in the middle of an overscroll
    3082             :   // animation - if so, don't start a new one.
    3083           0 :   if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) {
    3084             :     APZC_LOG("%p is overscrolled, starting snap-back\n", this);
    3085           0 :     StartOverscrollAnimation(ParentLayerPoint(0, 0));
    3086           0 :     return true;
    3087             :   }
    3088             :   // If we don't kick off an overscroll animation, we still need to ask the
    3089             :   // main thread to snap to any nearby snap points, assuming we haven't already
    3090             :   // done so when we started this fling
    3091           0 :   if (mState != FLING) {
    3092           0 :     ScrollSnap();
    3093             :   }
    3094           0 :   return false;
    3095             : }
    3096             : 
    3097           0 : bool AsyncPanZoomController::IsFlingingFast() const {
    3098           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3099           0 :   if (mState == FLING &&
    3100           0 :       GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
    3101             :     APZC_LOG("%p is moving fast\n", this);
    3102           0 :     return true;
    3103             :   }
    3104           0 :   return false;
    3105             : }
    3106             : 
    3107           0 : bool AsyncPanZoomController::IsPannable() const {
    3108           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3109           0 :   return mX.CanScroll() || mY.CanScroll();
    3110             : }
    3111             : 
    3112           0 : bool AsyncPanZoomController::IsScrollInfoLayer() const {
    3113           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3114           0 :   return mFrameMetrics.IsScrollInfoLayer();
    3115             : }
    3116             : 
    3117           0 : int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
    3118           0 :   RefPtr<GestureEventListener> listener = GetGestureEventListener();
    3119           0 :   return listener ? listener->GetLastTouchIdentifier() : -1;
    3120             : }
    3121             : 
    3122           4 : void AsyncPanZoomController::RequestContentRepaint(bool aUserAction) {
    3123             :   // Reinvoke this method on the repaint thread if it's not there already. It's
    3124             :   // important to do this before the call to CalculatePendingDisplayPort, so
    3125             :   // that CalculatePendingDisplayPort uses the most recent available version of
    3126             :   // mFrameMetrics, just before the paint request is dispatched to content.
    3127           6 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    3128           4 :   if (!controller) {
    3129           0 :     return;
    3130             :   }
    3131           4 :   if (!controller->IsRepaintThread()) {
    3132             :     // use the local variable to resolve the function overload.
    3133           2 :     auto func = static_cast<void (AsyncPanZoomController::*)(bool)>
    3134           2 :         (&AsyncPanZoomController::RequestContentRepaint);
    3135           6 :     controller->DispatchToRepaintThread(NewRunnableMethod<bool>(
    3136             :       "layers::AsyncPanZoomController::RequestContentRepaint",
    3137             :       this,
    3138             :       func,
    3139           4 :       aUserAction));
    3140           2 :     return;
    3141             :   }
    3142             : 
    3143           2 :   MOZ_ASSERT(controller->IsRepaintThread());
    3144             : 
    3145           4 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3146           2 :   ParentLayerPoint velocity = GetVelocityVector();
    3147           2 :   mFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(mFrameMetrics, velocity));
    3148           2 :   mFrameMetrics.SetUseDisplayPortMargins(true);
    3149           2 :   mFrameMetrics.SetPaintRequestTime(TimeStamp::Now());
    3150           2 :   mFrameMetrics.SetRepaintDrivenByUserAction(aUserAction);
    3151           2 :   RequestContentRepaint(mFrameMetrics, velocity);
    3152             : }
    3153             : 
    3154             : /*static*/ CSSRect
    3155           2 : GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
    3156             : {
    3157             :   // This computation is based on what happens in CalculatePendingDisplayPort. If that
    3158             :   // changes then this might need to change too
    3159           2 :   CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
    3160           4 :                    aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
    3161           2 :   baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
    3162           2 :   return baseRect;
    3163             : }
    3164             : 
    3165             : void
    3166           2 : AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
    3167             :                                               const ParentLayerPoint& aVelocity)
    3168             : {
    3169           4 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    3170           2 :   if (!controller) {
    3171           0 :     return;
    3172             :   }
    3173           2 :   MOZ_ASSERT(controller->IsRepaintThread());
    3174             : 
    3175             :   // If we're trying to paint what we already think is painted, discard this
    3176             :   // request since it's a pointless paint.
    3177           2 :   ScreenMargin marginDelta = (mLastPaintRequestMetrics.GetDisplayPortMargins()
    3178           4 :                            - aFrameMetrics.GetDisplayPortMargins());
    3179           5 :   if (fabsf(marginDelta.left) < EPSILON &&
    3180           2 :       fabsf(marginDelta.top) < EPSILON &&
    3181           2 :       fabsf(marginDelta.right) < EPSILON &&
    3182           2 :       fabsf(marginDelta.bottom) < EPSILON &&
    3183           2 :       fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
    3184           2 :             aFrameMetrics.GetScrollOffset().x) < EPSILON &&
    3185           2 :       fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
    3186           2 :             aFrameMetrics.GetScrollOffset().y) < EPSILON &&
    3187           2 :       aFrameMetrics.GetPresShellResolution() == mLastPaintRequestMetrics.GetPresShellResolution() &&
    3188           2 :       aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
    3189           2 :       fabsf(aFrameMetrics.GetViewport().width -
    3190           1 :             mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
    3191           0 :       fabsf(aFrameMetrics.GetViewport().height -
    3192           0 :             mLastPaintRequestMetrics.GetViewport().height) < EPSILON &&
    3193           0 :       aFrameMetrics.GetScrollGeneration() ==
    3194           2 :             mLastPaintRequestMetrics.GetScrollGeneration() &&
    3195           0 :       aFrameMetrics.GetScrollUpdateType() ==
    3196           0 :             mLastPaintRequestMetrics.GetScrollUpdateType()) {
    3197           0 :     return;
    3198             :   }
    3199             : 
    3200             :   APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
    3201             :   { // scope lock
    3202           4 :     MutexAutoLock lock(mCheckerboardEventLock);
    3203           2 :     if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
    3204           4 :       std::stringstream info;
    3205           2 :       info << " velocity " << aVelocity;
    3206           4 :       std::string str = info.str();
    3207           2 :       mCheckerboardEvent->UpdateRendertraceProperty(
    3208           4 :           CheckerboardEvent::RequestedDisplayPort, GetDisplayPortRect(aFrameMetrics),
    3209           2 :           str);
    3210             :     }
    3211             :   }
    3212             : 
    3213           2 :   MOZ_ASSERT(aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eNone ||
    3214             :              aFrameMetrics.GetScrollUpdateType() == FrameMetrics::eUserAction);
    3215           2 :   controller->RequestContentRepaint(aFrameMetrics);
    3216           2 :   mExpectedGeckoMetrics = aFrameMetrics;
    3217           2 :   mLastPaintRequestMetrics = aFrameMetrics;
    3218             : }
    3219             : 
    3220          40 : bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
    3221             :                                              nsTArray<RefPtr<Runnable>>* aOutDeferredTasks)
    3222             : {
    3223          40 :   APZThreadUtils::AssertOnCompositorThread();
    3224             : 
    3225             :   // This function may get called multiple with the same sample time, because
    3226             :   // there may be multiple layers with this APZC, and each layer invokes this
    3227             :   // function during composition. However we only want to do one animation step
    3228             :   // per composition so we need to deduplicate these calls first.
    3229          40 :   if (mLastSampleTime == aSampleTime) {
    3230           5 :     return false;
    3231             :   }
    3232             : 
    3233             :   // Sample the composited async transform once per composite. Note that we
    3234             :   // call this after the |mLastSampleTime == aSampleTime| check, to ensure
    3235             :   // it's only called once per APZC on each composite.
    3236          35 :   SampleCompositedAsyncTransform();
    3237             : 
    3238          35 :   TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime;
    3239          35 :   mLastSampleTime = aSampleTime;
    3240             : 
    3241          35 :   if (mAnimation) {
    3242           0 :     bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta);
    3243           0 :     bool wantsRepaints = mAnimation->WantsRepaints();
    3244           0 :     *aOutDeferredTasks = mAnimation->TakeDeferredTasks();
    3245           0 :     if (!continueAnimation) {
    3246           0 :       mAnimation = nullptr;
    3247           0 :       SetState(NOTHING);
    3248             :     }
    3249             :     // Request a repaint at the end of the animation in case something such as a
    3250             :     // call to NotifyLayersUpdated was invoked during the animation and Gecko's
    3251             :     // current state is some intermediate point of the animation.
    3252           0 :     if (!continueAnimation || wantsRepaints) {
    3253           0 :       RequestContentRepaint();
    3254             :     }
    3255           0 :     UpdateSharedCompositorFrameMetrics();
    3256           0 :     return true;
    3257             :   }
    3258          35 :   return false;
    3259             : }
    3260             : 
    3261             : AsyncTransformComponentMatrix
    3262          56 : AsyncPanZoomController::GetOverscrollTransform(AsyncTransformConsumer aMode) const
    3263             : {
    3264         112 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3265             : 
    3266          56 :   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
    3267           0 :     return AsyncTransformComponentMatrix();
    3268             :   }
    3269             : 
    3270          56 :   if (!IsOverscrolled()) {
    3271          56 :     return AsyncTransformComponentMatrix();
    3272             :   }
    3273             : 
    3274             :   // The overscroll effect is a simple translation by the overscroll offset.
    3275           0 :   ParentLayerPoint overscrollOffset(-mX.GetOverscroll(), -mY.GetOverscroll());
    3276           0 :   return AsyncTransformComponentMatrix()
    3277           0 :       .PostTranslate(overscrollOffset.x, overscrollOffset.y, 0);
    3278             : }
    3279             : 
    3280          40 : bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
    3281             : {
    3282          40 :   APZThreadUtils::AssertOnCompositorThread();
    3283             : 
    3284             :   // Don't send any state-change notifications until the end of the function,
    3285             :   // because we may go through some intermediate states while we finish
    3286             :   // animations and start new ones.
    3287          80 :   StateChangeNotificationBlocker blocker(this);
    3288             : 
    3289             :   // The eventual return value of this function. The compositor needs to know
    3290             :   // whether or not to advance by a frame as soon as it can. For example, if a
    3291             :   // fling is happening, it has to keep compositing so that the animation is
    3292             :   // smooth. If an animation frame is requested, it is the compositor's
    3293             :   // responsibility to schedule a composite.
    3294          40 :   mAsyncTransformAppliedToContent = false;
    3295          40 :   bool requestAnimationFrame = false;
    3296          80 :   nsTArray<RefPtr<Runnable>> deferredTasks;
    3297             : 
    3298             :   {
    3299          80 :     ReentrantMonitorAutoEnter lock(mMonitor);
    3300             : 
    3301          40 :     requestAnimationFrame = UpdateAnimation(aSampleTime, &deferredTasks);
    3302             : 
    3303             :     { // scope lock
    3304          80 :       MutexAutoLock lock(mCheckerboardEventLock);
    3305          40 :       if (mCheckerboardEvent) {
    3306         120 :         mCheckerboardEvent->UpdateRendertraceProperty(
    3307             :             CheckerboardEvent::UserVisible,
    3308          80 :             CSSRect(mFrameMetrics.GetScrollOffset(),
    3309         120 :                     mFrameMetrics.CalculateCompositedSizeInCssPixels()));
    3310             :       }
    3311             :     }
    3312             :   }
    3313             : 
    3314             :   // Execute any deferred tasks queued up by mAnimation's Sample() (called by
    3315             :   // UpdateAnimation()). This needs to be done after the monitor is released
    3316             :   // since the tasks are allowed to call APZCTreeManager methods which can grab
    3317             :   // the tree lock.
    3318          40 :   for (uint32_t i = 0; i < deferredTasks.Length(); ++i) {
    3319           0 :     deferredTasks[i]->Run();
    3320           0 :     deferredTasks[i] = nullptr;
    3321             :   }
    3322             : 
    3323             :   // One of the deferred tasks may have started a new animation. In this case,
    3324             :   // we want to ask the compositor to schedule a new composite.
    3325          40 :   requestAnimationFrame |= (mAnimation != nullptr);
    3326             : 
    3327          80 :   return requestAnimationFrame;
    3328             : }
    3329             : 
    3330             : ParentLayerPoint
    3331          26 : AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncTransformConsumer aMode) const
    3332             : {
    3333          52 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3334             : 
    3335          26 :   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
    3336           0 :     return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
    3337             :   }
    3338             : 
    3339          52 :   return (GetEffectiveScrollOffset(aMode) + mTestAsyncScrollOffset)
    3340          78 :       * GetEffectiveZoom(aMode) * mTestAsyncZoom.scale;
    3341             : }
    3342             : 
    3343             : CSSPoint
    3344           0 : AsyncPanZoomController::GetCurrentAsyncScrollOffsetInCssPixels(AsyncTransformConsumer aMode) const {
    3345           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3346             : 
    3347           0 :   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
    3348           0 :     return mLastContentPaintMetrics.GetScrollOffset();
    3349             :   }
    3350             : 
    3351           0 :   return GetEffectiveScrollOffset(aMode) + mTestAsyncScrollOffset;
    3352             : }
    3353             : 
    3354             : AsyncTransform
    3355          56 : AsyncPanZoomController::GetCurrentAsyncTransform(AsyncTransformConsumer aMode) const
    3356             : {
    3357         112 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3358             : 
    3359          56 :   if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) {
    3360           0 :     return AsyncTransform();
    3361             :   }
    3362             : 
    3363          56 :   CSSPoint lastPaintScrollOffset;
    3364          56 :   if (mLastContentPaintMetrics.IsScrollable()) {
    3365          56 :     lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
    3366             :   }
    3367             : 
    3368         112 :   CSSPoint currentScrollOffset = GetEffectiveScrollOffset(aMode) +
    3369         112 :     mTestAsyncScrollOffset;
    3370             : 
    3371             :   // If checkerboarding has been disallowed, clamp the scroll position to stay
    3372             :   // within rendered content.
    3373          56 :   if (!gfxPrefs::APZAllowCheckerboarding() &&
    3374           0 :       !mLastContentPaintMetrics.GetDisplayPort().IsEmpty()) {
    3375           0 :     CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
    3376             :     CSSPoint maxScrollOffset = lastPaintScrollOffset +
    3377           0 :       CSSPoint(mLastContentPaintMetrics.GetDisplayPort().XMost() - compositedSize.width,
    3378           0 :                mLastContentPaintMetrics.GetDisplayPort().YMost() - compositedSize.height);
    3379           0 :     CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.GetDisplayPort().TopLeft();
    3380             : 
    3381           0 :     if (minScrollOffset.x < maxScrollOffset.x) {
    3382           0 :       currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
    3383             :     }
    3384           0 :     if (minScrollOffset.y < maxScrollOffset.y) {
    3385           0 :       currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
    3386             :     }
    3387             :   }
    3388             : 
    3389          56 :   CSSToParentLayerScale2D effectiveZoom = GetEffectiveZoom(aMode);
    3390             : 
    3391         112 :   ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
    3392         112 :                                * effectiveZoom * mTestAsyncZoom.scale;
    3393             : 
    3394             :   LayerToParentLayerScale compositedAsyncZoom =
    3395          56 :       (effectiveZoom / mFrameMetrics.LayersPixelsPerCSSPixel()).ToScaleFactor();
    3396             :   return AsyncTransform(
    3397         112 :     LayerToParentLayerScale(compositedAsyncZoom.scale * mTestAsyncZoom.scale),
    3398         112 :     -translation);
    3399             : }
    3400             : 
    3401             : CSSPoint
    3402          82 : AsyncPanZoomController::GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const
    3403             : {
    3404          82 :   if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) {
    3405           0 :     return mCompositedScrollOffset;
    3406             :   }
    3407          82 :   return mFrameMetrics.GetScrollOffset();
    3408             : }
    3409             : 
    3410             : CSSToParentLayerScale2D
    3411          82 : AsyncPanZoomController::GetEffectiveZoom(AsyncTransformConsumer aMode) const
    3412             : {
    3413          82 :   if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) {
    3414           0 :     return mCompositedZoom;
    3415             :   }
    3416          82 :   return mFrameMetrics.GetZoom();
    3417             : }
    3418             : 
    3419             : void
    3420          35 : AsyncPanZoomController::SampleCompositedAsyncTransform()
    3421             : {
    3422          70 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3423          35 :   mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
    3424          35 :   mCompositedZoom = mFrameMetrics.GetZoom();
    3425          35 : }
    3426             : 
    3427             : AsyncTransformComponentMatrix
    3428          16 : AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncTransformConsumer aMode) const
    3429             : {
    3430          32 :   return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
    3431          48 :        * GetOverscrollTransform(aMode);
    3432             : }
    3433             : 
    3434           5 : Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
    3435          10 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3436             : 
    3437             :   LayerPoint scrollChange =
    3438          10 :     (mLastContentPaintMetrics.GetScrollOffset() - mExpectedGeckoMetrics.GetScrollOffset())
    3439          15 :     * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
    3440          10 :     * mLastContentPaintMetrics.GetCumulativeResolution();
    3441             : 
    3442             :   // We're interested in the async zoom change. Factor out the content scale
    3443             :   // that may change when dragging the window to a monitor with a different
    3444             :   // content scale.
    3445             :   LayoutDeviceToParentLayerScale2D lastContentZoom =
    3446           5 :     mLastContentPaintMetrics.GetZoom() / mLastContentPaintMetrics.GetDevPixelsPerCSSPixel();
    3447             :   LayoutDeviceToParentLayerScale2D lastDispatchedZoom =
    3448           5 :     mExpectedGeckoMetrics.GetZoom() / mExpectedGeckoMetrics.GetDevPixelsPerCSSPixel();
    3449           5 :   gfxSize zoomChange = lastContentZoom / lastDispatchedZoom;
    3450             : 
    3451          10 :   return Matrix4x4::Translation(scrollChange.x, scrollChange.y, 0).
    3452          15 :            PostScale(zoomChange.width, zoomChange.height, 1);
    3453             : }
    3454             : 
    3455             : uint32_t
    3456          35 : AsyncPanZoomController::GetCheckerboardMagnitude() const
    3457             : {
    3458          70 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3459             : 
    3460          35 :   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
    3461          35 :   CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
    3462          35 :   CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
    3463             : 
    3464          70 :   CSSIntRegion checkerboard;
    3465             :   // Round so as to minimize checkerboarding; if we're only showing fractional
    3466             :   // pixels of checkerboarding it's not really worth counting
    3467          35 :   checkerboard.Sub(RoundedIn(visible), RoundedOut(painted));
    3468          70 :   return checkerboard.Area();
    3469             : }
    3470             : 
    3471             : void
    3472          40 : AsyncPanZoomController::ReportCheckerboard(const TimeStamp& aSampleTime)
    3473             : {
    3474          40 :   if (mLastCheckerboardReport == aSampleTime) {
    3475             :     // This function will get called multiple times for each APZC on a single
    3476             :     // composite (once for each layer it is attached to). Only report the
    3477             :     // checkerboard once per composite though.
    3478           5 :     return;
    3479             :   }
    3480          35 :   mLastCheckerboardReport = aSampleTime;
    3481             : 
    3482          35 :   bool recordTrace = gfxPrefs::APZRecordCheckerboarding();
    3483          35 :   bool forTelemetry = Telemetry::CanRecordExtended();
    3484          35 :   uint32_t magnitude = GetCheckerboardMagnitude();
    3485             : 
    3486          70 :   MutexAutoLock lock(mCheckerboardEventLock);
    3487          35 :   if (!mCheckerboardEvent && (recordTrace || forTelemetry)) {
    3488           2 :     mCheckerboardEvent = MakeUnique<CheckerboardEvent>(recordTrace);
    3489             :   }
    3490          35 :   mPotentialCheckerboardTracker.InTransform(IsTransformingState(mState));
    3491          35 :   if (magnitude) {
    3492           0 :     mPotentialCheckerboardTracker.CheckerboardSeen();
    3493             :   }
    3494          35 :   UpdateCheckerboardEvent(lock, magnitude);
    3495             : }
    3496             : 
    3497             : void
    3498          35 : AsyncPanZoomController::UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
    3499             :                                                 uint32_t aMagnitude)
    3500             : {
    3501          35 :   if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(aMagnitude)) {
    3502             :     // This checkerboard event is done. Report some metrics to telemetry.
    3503           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_SEVERITY,
    3504           0 :       mCheckerboardEvent->GetSeverity());
    3505           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_PEAK,
    3506           0 :       mCheckerboardEvent->GetPeak());
    3507           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_DURATION,
    3508           0 :       (uint32_t)mCheckerboardEvent->GetDuration().ToMilliseconds());
    3509             : 
    3510           0 :     mPotentialCheckerboardTracker.CheckerboardDone();
    3511             : 
    3512           0 :     if (gfxPrefs::APZRecordCheckerboarding()) {
    3513             :       // if the pref is enabled, also send it to the storage class. it may be
    3514             :       // chosen for public display on about:checkerboard, the hall of fame for
    3515             :       // checkerboard events.
    3516           0 :       uint32_t severity = mCheckerboardEvent->GetSeverity();
    3517           0 :       std::string log = mCheckerboardEvent->GetLog();
    3518           0 :       CheckerboardEventStorage::Report(severity, log);
    3519             :     }
    3520           0 :     mCheckerboardEvent = nullptr;
    3521             :   }
    3522          35 : }
    3523             : 
    3524             : void
    3525           0 : AsyncPanZoomController::FlushActiveCheckerboardReport()
    3526             : {
    3527           0 :   MutexAutoLock lock(mCheckerboardEventLock);
    3528             :   // Pretend like we got a frame with 0 pixels checkerboarded. This will
    3529             :   // terminate the checkerboard event and flush it out
    3530           0 :   UpdateCheckerboardEvent(lock, 0);
    3531           0 : }
    3532             : 
    3533           0 : bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
    3534           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3535             : 
    3536           0 :   if (!gfxPrefs::APZAllowCheckerboarding() || mScrollMetadata.IsApzForceDisabled()) {
    3537           0 :     return false;
    3538             :   }
    3539             : 
    3540           0 :   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
    3541           0 :   CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
    3542           0 :   painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));   // fuzz for rounding error
    3543           0 :   CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
    3544           0 :   if (painted.Contains(visible)) {
    3545           0 :     return false;
    3546             :   }
    3547             :   APZC_LOG_FM(mFrameMetrics, "%p is currently checkerboarding (painted %s visble %s)",
    3548             :     this, Stringify(painted).c_str(), Stringify(visible).c_str());
    3549           0 :   return true;
    3550             : }
    3551             : 
    3552          34 : void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata,
    3553             :                                                  bool aIsFirstPaint,
    3554             :                                                  bool aThisLayerTreeUpdated)
    3555             : {
    3556          34 :   APZThreadUtils::AssertOnCompositorThread();
    3557             : 
    3558          41 :   ReentrantMonitorAutoEnter lock(mMonitor);
    3559          34 :   bool isDefault = mScrollMetadata.IsDefault();
    3560             : 
    3561          34 :   const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics();
    3562             : 
    3563          34 :   if ((aScrollMetadata == mLastContentPaintMetadata) && !isDefault) {
    3564             :     // No new information here, skip it.
    3565             :     APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
    3566          27 :     return;
    3567             :   }
    3568             : 
    3569             :   // If the mFrameMetrics scroll offset is different from the last scroll offset
    3570             :   // that the main-thread sent us, then we know that the user has been doing
    3571             :   // something that triggers a scroll. This check is the APZ equivalent of the
    3572             :   // check on the main-thread at
    3573             :   // https://hg.mozilla.org/mozilla-central/file/97a52326b06a/layout/generic/nsGfxScrollFrame.cpp#l4050
    3574             :   // There is code below (the use site of userScrolled) that prevents a restored-
    3575             :   // scroll-position update from overwriting a user scroll, again equivalent to
    3576             :   // how the main thread code does the same thing.
    3577           7 :   CSSPoint lastScrollOffset = mLastContentPaintMetadata.GetMetrics().GetScrollOffset();
    3578             :   bool userScrolled =
    3579          14 :     !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().x, lastScrollOffset.x) ||
    3580          14 :     !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().y, lastScrollOffset.y);
    3581             : 
    3582           7 :   if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) {
    3583           7 :     mLastContentPaintMetadata = aScrollMetadata;
    3584             :   }
    3585             : 
    3586           7 :   mScrollMetadata.SetScrollParentId(aScrollMetadata.GetScrollParentId());
    3587             :   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
    3588             :     this, aIsFirstPaint, aThisLayerTreeUpdated);
    3589             : 
    3590             :   { // scope lock
    3591          14 :     MutexAutoLock lock(mCheckerboardEventLock);
    3592           7 :     if (mCheckerboardEvent && mCheckerboardEvent->IsRecordingTrace()) {
    3593          10 :       std::string str;
    3594           5 :       if (aThisLayerTreeUpdated) {
    3595           5 :         if (!aLayerMetrics.GetPaintRequestTime().IsNull()) {
    3596             :           // Note that we might get the paint request time as non-null, but with
    3597             :           // aThisLayerTreeUpdated false. That can happen if we get a layer transaction
    3598             :           // from a different process right after we get the layer transaction with
    3599             :           // aThisLayerTreeUpdated == true. In this case we want to ignore the
    3600             :           // paint request time because it was already dumped in the previous layer
    3601             :           // transaction.
    3602           2 :           TimeDuration paintTime = TimeStamp::Now() - aLayerMetrics.GetPaintRequestTime();
    3603           4 :           std::stringstream info;
    3604           2 :           info << " painttime " << paintTime.ToMilliseconds();
    3605           2 :           str = info.str();
    3606             :         } else {
    3607             :           // This might be indicative of a wasted paint particularly if it happens
    3608             :           // during a checkerboard event.
    3609           3 :           str = " (this layertree updated)";
    3610             :         }
    3611             :       }
    3612          10 :       mCheckerboardEvent->UpdateRendertraceProperty(
    3613           5 :           CheckerboardEvent::Page, aLayerMetrics.GetScrollableRect());
    3614           5 :       mCheckerboardEvent->UpdateRendertraceProperty(
    3615             :           CheckerboardEvent::PaintedDisplayPort,
    3616          10 :           aLayerMetrics.GetDisplayPort() + aLayerMetrics.GetScrollOffset(),
    3617           5 :           str);
    3618           5 :       if (!aLayerMetrics.GetCriticalDisplayPort().IsEmpty()) {
    3619           0 :         mCheckerboardEvent->UpdateRendertraceProperty(
    3620             :             CheckerboardEvent::PaintedCriticalDisplayPort,
    3621           0 :             aLayerMetrics.GetCriticalDisplayPort() + aLayerMetrics.GetScrollOffset());
    3622             :       }
    3623             :     }
    3624             :   }
    3625             : 
    3626           7 :   bool needContentRepaint = false;
    3627           7 :   bool viewportUpdated = false;
    3628          11 :   if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().width, mFrameMetrics.GetCompositionBounds().width) &&
    3629           4 :       FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().height, mFrameMetrics.GetCompositionBounds().height)) {
    3630             :     // Remote content has sync'd up to the composition geometry
    3631             :     // change, so we can accept the viewport it's calculated.
    3632           7 :     if (mFrameMetrics.GetViewport().width != aLayerMetrics.GetViewport().width ||
    3633           3 :         mFrameMetrics.GetViewport().height != aLayerMetrics.GetViewport().height) {
    3634           1 :       needContentRepaint = true;
    3635           1 :       viewportUpdated = true;
    3636             :     }
    3637           4 :     mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
    3638             :   }
    3639             : 
    3640             :   // If the layers update was not triggered by our own repaint request, then
    3641             :   // we want to take the new scroll offset. Check the scroll generation as well
    3642             :   // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
    3643             :   // update message.
    3644           7 :   bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
    3645           7 :         && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
    3646             : 
    3647           7 :   if (scrollOffsetUpdated && userScrolled &&
    3648           0 :       aLayerMetrics.GetScrollUpdateType() == FrameMetrics::ScrollOffsetUpdateType::eRestore) {
    3649             :     APZC_LOG("%p dropping scroll update of type eRestore because of user scroll\n", this);
    3650           0 :     scrollOffsetUpdated = false;
    3651             :   }
    3652             : 
    3653           7 :   bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
    3654           7 :        && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
    3655             : 
    3656             :   // TODO if we're in a drag and scrollOffsetUpdated is set then we want to
    3657             :   // ignore it
    3658             : 
    3659             : #if defined(MOZ_WIDGET_ANDROID)
    3660             :   if (aLayerMetrics.IsRootContent()) {
    3661             :     if (APZCTreeManager* manager = GetApzcTreeManager()) {
    3662             :       AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
    3663             :       MOZ_ASSERT(animator);
    3664             :       animator->MaybeUpdateCompositionSizeAndRootFrameMetrics(aLayerMetrics);
    3665             :     }
    3666             :   }
    3667             : #endif
    3668             : 
    3669           7 :   if ((aIsFirstPaint && aThisLayerTreeUpdated) || isDefault) {
    3670             :     // Initialize our internal state to something sane when the content
    3671             :     // that was just painted is something we knew nothing about previously
    3672           2 :     CancelAnimation();
    3673             : 
    3674           2 :     mScrollMetadata = aScrollMetadata;
    3675           2 :     mExpectedGeckoMetrics = aLayerMetrics;
    3676           2 :     ShareCompositorFrameMetrics();
    3677             : 
    3678           2 :     mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
    3679           2 :     mCompositedZoom = mFrameMetrics.GetZoom();
    3680             : 
    3681           4 :     if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
    3682             :       // A non-zero display port margin here indicates a displayport has
    3683             :       // been set by a previous APZC for the content at this guid. The
    3684             :       // scrollable rect may have changed since then, making the margins
    3685             :       // wrong, so we need to calculate a new display port.
    3686             :       APZC_LOG("%p detected non-empty margins which probably need updating\n", this);
    3687           0 :       needContentRepaint = true;
    3688             :     }
    3689             :   } else {
    3690             :     // If we're not taking the aLayerMetrics wholesale we still need to pull
    3691             :     // in some things into our local mFrameMetrics because these things are
    3692             :     // determined by Gecko and our copy in mFrameMetrics may be stale.
    3693             : 
    3694          14 :     if (FuzzyEqualsAdditive(mFrameMetrics.GetCompositionBounds().width, aLayerMetrics.GetCompositionBounds().width) &&
    3695           9 :         mFrameMetrics.GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel() &&
    3696           4 :         !viewportUpdated) {
    3697             :       // Any change to the pres shell resolution was requested by APZ and is
    3698             :       // already included in our zoom; however, other components of the
    3699             :       // cumulative resolution (a parent document's pres-shell resolution, or
    3700             :       // the css-driven resolution) may have changed, and we need to update
    3701             :       // our zoom to reflect that. Note that we can't just take
    3702             :       // aLayerMetrics.mZoom because the APZ may have additional async zoom
    3703             :       // since the repaint request.
    3704           3 :       gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution()
    3705           6 :                                     / mFrameMetrics.GetCumulativeResolution();
    3706           3 :       float presShellResolutionChange = aLayerMetrics.GetPresShellResolution()
    3707           3 :                                       / mFrameMetrics.GetPresShellResolution();
    3708           3 :       if (presShellResolutionChange != 1.0f) {
    3709           0 :         needContentRepaint = true;
    3710             :       }
    3711           3 :       mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
    3712           3 :       mCompositedZoom.xScale *= (totalResolutionChange / presShellResolutionChange).width;
    3713           3 :       mCompositedZoom.yScale *= (totalResolutionChange / presShellResolutionChange).height;
    3714             :     } else {
    3715             :       // Take the new zoom as either device scale or composition width or
    3716             :       // viewport size got changed (e.g. due to orientation change, or content
    3717             :       // changing the meta-viewport tag).
    3718           2 :       mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
    3719           2 :       mCompositedZoom = aLayerMetrics.GetZoom();
    3720           2 :       mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel());
    3721             :     }
    3722           5 :     bool scrollableRectChanged = false;
    3723           5 :     if (!mFrameMetrics.GetScrollableRect().IsEqualEdges(aLayerMetrics.GetScrollableRect())) {
    3724           1 :       mFrameMetrics.SetScrollableRect(aLayerMetrics.GetScrollableRect());
    3725           1 :       needContentRepaint = true;
    3726           1 :       scrollableRectChanged = true;
    3727             :     }
    3728           5 :     mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
    3729           5 :     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
    3730           5 :     mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
    3731           5 :     mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
    3732           5 :     mScrollMetadata.SetHasScrollgrab(aScrollMetadata.GetHasScrollgrab());
    3733           5 :     mScrollMetadata.SetLineScrollAmount(aScrollMetadata.GetLineScrollAmount());
    3734           5 :     mScrollMetadata.SetPageScrollAmount(aScrollMetadata.GetPageScrollAmount());
    3735           5 :     mScrollMetadata.SetSnapInfo(ScrollSnapInfo(aScrollMetadata.GetSnapInfo()));
    3736             :     // The scroll clip can differ between layers associated a given scroll frame,
    3737             :     // so APZC (which keeps a single copy of ScrollMetadata per scroll frame)
    3738             :     // has no business using it.
    3739           5 :     mScrollMetadata.SetScrollClip(Nothing());
    3740           5 :     mScrollMetadata.SetIsLayersIdRoot(aScrollMetadata.IsLayersIdRoot());
    3741           5 :     mScrollMetadata.SetUsesContainerScrolling(aScrollMetadata.UsesContainerScrolling());
    3742           5 :     mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer());
    3743           5 :     mScrollMetadata.SetForceDisableApz(aScrollMetadata.IsApzForceDisabled());
    3744             : 
    3745           5 :     if (scrollOffsetUpdated) {
    3746             :       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
    3747             :         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
    3748             :         ToString(aLayerMetrics.GetScrollOffset()).c_str());
    3749             : 
    3750             :       // Send an acknowledgement with the new scroll generation so that any
    3751             :       // repaint requests later in this function go through.
    3752             :       // Because of the scroll generation update, any inflight paint requests are
    3753             :       // going to be ignored by layout, and so mExpectedGeckoMetrics
    3754             :       // becomes incorrect for the purposes of calculating the LD transform. To
    3755             :       // correct this we need to update mExpectedGeckoMetrics to be the
    3756             :       // last thing we know was painted by Gecko.
    3757           0 :       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
    3758           0 :       mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
    3759           0 :       mExpectedGeckoMetrics = aLayerMetrics;
    3760             : 
    3761             :       // Cancel the animation (which might also trigger a repaint request)
    3762             :       // after we update the scroll offset above. Otherwise we can be left
    3763             :       // in a state where things are out of sync.
    3764           0 :       CancelAnimation();
    3765             : 
    3766             :       // Since the scroll offset has changed, we need to recompute the
    3767             :       // displayport margins and send them to layout. Otherwise there might be
    3768             :       // scenarios where for example we scroll from the top of a page (where the
    3769             :       // top displayport margin is zero) to the bottom of a page, which will
    3770             :       // result in a displayport that doesn't extend upwards at all.
    3771             :       // Note that even if the CancelAnimation call above requested a repaint
    3772             :       // this is fine because we already have repaint request deduplication.
    3773           0 :       needContentRepaint = true;
    3774           5 :     } else if (scrollableRectChanged) {
    3775             :       // Even if we didn't accept a new scroll offset from content, the
    3776             :       // scrollable rect may have changed in a way that makes our local
    3777             :       // scroll offset out of bounds, so re-clamp it.
    3778           1 :       mFrameMetrics.SetScrollOffset(
    3779           2 :           mFrameMetrics.CalculateScrollRange().ClampPoint(
    3780           2 :               mFrameMetrics.GetScrollOffset()));
    3781             :     }
    3782             :   }
    3783             : 
    3784           7 :   if (smoothScrollRequested) {
    3785             :     // A smooth scroll has been requested for animation on the compositor
    3786             :     // thread.  This flag will be reset by the main thread when it receives
    3787             :     // the scroll update acknowledgement.
    3788             : 
    3789             :     APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this,
    3790             :       Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
    3791             :       Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(),
    3792             :       mState);
    3793             : 
    3794             :     // See comment on the similar code in the |if (scrollOffsetUpdated)| block
    3795             :     // above.
    3796           0 :     mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
    3797           0 :     needContentRepaint = true;
    3798           0 :     mExpectedGeckoMetrics = aLayerMetrics;
    3799             : 
    3800           0 :     SmoothScrollTo(mFrameMetrics.GetSmoothScrollOffset());
    3801             :   }
    3802             : 
    3803           7 :   if (needContentRepaint) {
    3804             :     // This repaint request is not driven by a user action on the APZ side
    3805           2 :     RequestContentRepaint(false);
    3806             :   }
    3807           7 :   UpdateSharedCompositorFrameMetrics();
    3808             : }
    3809             : 
    3810           0 : const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const {
    3811           0 :   mMonitor.AssertCurrentThreadIn();
    3812           0 :   return mFrameMetrics;
    3813             : }
    3814             : 
    3815          36 : APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
    3816          36 :   mMonitor.AssertNotCurrentThreadIn();
    3817          36 :   return mTreeManager;
    3818             : }
    3819             : 
    3820           0 : void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
    3821           0 :   if (!aRect.IsFinite()) {
    3822           0 :     NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...");
    3823           0 :     return;
    3824           0 :   } else if (aRect.IsEmpty() && (aFlags & DISABLE_ZOOM_OUT)) {
    3825             :     // Double-tap-to-zooming uses an empty rect to mean "zoom out".
    3826             :     // If zooming out is disabled, an empty rect is nonsensical
    3827             :     // and will produce undesirable scrolling.
    3828           0 :     NS_WARNING("ZoomToRect got called with an empty rect and zoom out disabled; ignoring...");
    3829           0 :     return;
    3830             :   }
    3831             : 
    3832             :   // Only the root APZC is zoomable, and the root APZC is not allowed to have
    3833             :   // different x and y scales. If it did, the calculations in this function
    3834             :   // would have to be adjusted (as e.g. it would no longer be valid to take
    3835             :   // the minimum or maximum of the ratios of the widths and heights of the
    3836             :   // page rect and the composition bounds).
    3837           0 :   MOZ_ASSERT(mFrameMetrics.IsRootContent());
    3838           0 :   MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
    3839             : 
    3840           0 :   SetState(ANIMATING_ZOOM);
    3841             : 
    3842             :   {
    3843           0 :     ReentrantMonitorAutoEnter lock(mMonitor);
    3844             : 
    3845           0 :     ParentLayerRect compositionBounds = mFrameMetrics.GetCompositionBounds();
    3846           0 :     CSSRect cssPageRect = mFrameMetrics.GetScrollableRect();
    3847           0 :     CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
    3848           0 :     CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoom().ToScaleFactor();
    3849           0 :     CSSToParentLayerScale targetZoom;
    3850             : 
    3851             :     // The minimum zoom to prevent over-zoom-out.
    3852             :     // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
    3853             :     // then the CSS content rect, in layers pixels, will be smaller than the
    3854             :     // composition bounds. If this happens, we can't fill the target composited
    3855             :     // area with this frame.
    3856             :     CSSToParentLayerScale localMinZoom(std::max(mZoomConstraints.mMinZoom.scale,
    3857           0 :                                        std::max(compositionBounds.width / cssPageRect.width,
    3858           0 :                                                 compositionBounds.height / cssPageRect.height)));
    3859           0 :     CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom;
    3860             : 
    3861           0 :     if (!aRect.IsEmpty()) {
    3862             :       // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
    3863           0 :       aRect = aRect.Intersect(cssPageRect);
    3864           0 :       targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
    3865           0 :                                                   compositionBounds.height / aRect.height));
    3866             :     }
    3867             : 
    3868             :     // 1. If the rect is empty, the content-side logic for handling a double-tap
    3869             :     //    requested that we zoom out.
    3870             :     // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
    3871             :     // 3. currentZoom is equal to localMinZoom and user still double-tapping it
    3872             :     // Treat these three cases as a request to zoom out as much as possible.
    3873             :     bool zoomOut;
    3874           0 :     if (aFlags & DISABLE_ZOOM_OUT) {
    3875           0 :       zoomOut = false;
    3876             :     } else {
    3877           0 :       zoomOut = aRect.IsEmpty() ||
    3878           0 :         (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
    3879           0 :         (currentZoom == localMinZoom && targetZoom <= localMinZoom);
    3880             :     }
    3881             : 
    3882           0 :     if (zoomOut) {
    3883           0 :       CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
    3884           0 :       float y = scrollOffset.y;
    3885             :       float newHeight =
    3886           0 :         cssPageRect.width * (compositedSize.height / compositedSize.width);
    3887           0 :       float dh = compositedSize.height - newHeight;
    3888             : 
    3889           0 :       aRect = CSSRect(0.0f,
    3890           0 :                       y + dh/2,
    3891             :                       cssPageRect.width,
    3892             :                       newHeight);
    3893           0 :       aRect = aRect.Intersect(cssPageRect);
    3894           0 :       targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
    3895           0 :                                                   compositionBounds.height / aRect.height));
    3896             :     }
    3897             : 
    3898           0 :     targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
    3899           0 :     FrameMetrics endZoomToMetrics = mFrameMetrics;
    3900           0 :     if (aFlags & PAN_INTO_VIEW_ONLY) {
    3901           0 :       targetZoom = currentZoom;
    3902           0 :     } else if(aFlags & ONLY_ZOOM_TO_DEFAULT_SCALE) {
    3903             :       CSSToParentLayerScale zoomAtDefaultScale =
    3904           0 :         mFrameMetrics.GetDevPixelsPerCSSPixel() * LayoutDeviceToParentLayerScale(1.0);
    3905           0 :       if (targetZoom.scale > zoomAtDefaultScale.scale) {
    3906             :         // Only change the zoom if we are less than the default zoom
    3907           0 :         if (currentZoom.scale < zoomAtDefaultScale.scale) {
    3908           0 :           targetZoom = zoomAtDefaultScale;
    3909             :         } else {
    3910           0 :           targetZoom = currentZoom;
    3911             :         }
    3912             :       }
    3913             :     }
    3914           0 :     endZoomToMetrics.SetZoom(CSSToParentLayerScale2D(targetZoom));
    3915             : 
    3916             :     // Adjust the zoomToRect to a sensible position to prevent overscrolling.
    3917           0 :     CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
    3918             : 
    3919             :     // Vertically center the zoomed element in the screen.
    3920           0 :     if (!zoomOut && (sizeAfterZoom.height > aRect.height)) {
    3921           0 :       aRect.y -= (sizeAfterZoom.height - aRect.height) * 0.5f;
    3922           0 :       if (aRect.y < 0.0f) {
    3923           0 :         aRect.y = 0.0f;
    3924             :       }
    3925             :     }
    3926             : 
    3927             :     // If either of these conditions are met, the page will be
    3928             :     // overscrolled after zoomed
    3929           0 :     if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
    3930           0 :       aRect.y = cssPageRect.height - sizeAfterZoom.height;
    3931           0 :       aRect.y = aRect.y > 0 ? aRect.y : 0;
    3932             :     }
    3933           0 :     if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
    3934           0 :       aRect.x = cssPageRect.width - sizeAfterZoom.width;
    3935           0 :       aRect.x = aRect.x > 0 ? aRect.x : 0;
    3936             :     }
    3937             : 
    3938           0 :     endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
    3939             : 
    3940           0 :     StartAnimation(new ZoomAnimation(
    3941           0 :         mFrameMetrics.GetScrollOffset(),
    3942           0 :         mFrameMetrics.GetZoom(),
    3943           0 :         endZoomToMetrics.GetScrollOffset(),
    3944           0 :         endZoomToMetrics.GetZoom()));
    3945             : 
    3946             :     // Schedule a repaint now, so the new displayport will be painted before the
    3947             :     // animation finishes.
    3948           0 :     ParentLayerPoint velocity(0, 0);
    3949             :     endZoomToMetrics.SetDisplayPortMargins(
    3950           0 :       CalculatePendingDisplayPort(endZoomToMetrics, velocity));
    3951           0 :     endZoomToMetrics.SetUseDisplayPortMargins(true);
    3952           0 :     endZoomToMetrics.SetPaintRequestTime(TimeStamp::Now());
    3953           0 :     endZoomToMetrics.SetRepaintDrivenByUserAction(true);
    3954             : 
    3955           0 :     RefPtr<GeckoContentController> controller = GetGeckoContentController();
    3956           0 :     if (!controller) {
    3957           0 :       return;
    3958             :     }
    3959           0 :     if (controller->IsRepaintThread()) {
    3960           0 :       RequestContentRepaint(endZoomToMetrics, velocity);
    3961             :     } else {
    3962             :       // use a local var to resolve the function overload
    3963           0 :       auto func = static_cast<void (AsyncPanZoomController::*)(const FrameMetrics&, const ParentLayerPoint&)>
    3964           0 :           (&AsyncPanZoomController::RequestContentRepaint);
    3965           0 :       controller->DispatchToRepaintThread(
    3966           0 :         NewRunnableMethod<FrameMetrics, ParentLayerPoint>(
    3967             :           "layers::AsyncPanZoomController::ZoomToRect",
    3968             :           this,
    3969             :           func,
    3970             :           endZoomToMetrics,
    3971           0 :           velocity));
    3972             :     }
    3973             :   }
    3974             : }
    3975             : 
    3976             : InputBlockState*
    3977           0 : AsyncPanZoomController::GetCurrentInputBlock() const
    3978             : {
    3979           0 :   return GetInputQueue()->GetCurrentBlock();
    3980             : }
    3981             : 
    3982             : TouchBlockState*
    3983           0 : AsyncPanZoomController::GetCurrentTouchBlock() const
    3984             : {
    3985           0 :   return GetInputQueue()->GetCurrentTouchBlock();
    3986             : }
    3987             : 
    3988             : PanGestureBlockState*
    3989           0 : AsyncPanZoomController::GetCurrentPanGestureBlock() const
    3990             : {
    3991           0 :   return GetInputQueue()->GetCurrentPanGestureBlock();
    3992             : }
    3993             : 
    3994             : void
    3995           0 : AsyncPanZoomController::ResetTouchInputState()
    3996             : {
    3997           0 :   MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
    3998           0 :   RefPtr<GestureEventListener> listener = GetGestureEventListener();
    3999           0 :   if (listener) {
    4000           0 :     listener->HandleInputEvent(cancel);
    4001             :   }
    4002           0 :   CancelAnimationAndGestureState();
    4003             :   // Clear overscroll along the entire handoff chain, in case an APZC
    4004             :   // later in the chain is overscrolled.
    4005           0 :   if (TouchBlockState* block = GetCurrentTouchBlock()) {
    4006           0 :     block->GetOverscrollHandoffChain()->ClearOverscroll();
    4007             :   }
    4008           0 : }
    4009             : 
    4010             : void
    4011           0 : AsyncPanZoomController::CancelAnimationAndGestureState()
    4012             : {
    4013           0 :   mX.CancelGesture();
    4014           0 :   mY.CancelGesture();
    4015           0 :   CancelAnimation(CancelAnimationFlags::ScrollSnap);
    4016           0 : }
    4017             : 
    4018             : bool
    4019           0 : AsyncPanZoomController::HasReadyTouchBlock() const
    4020             : {
    4021           0 :   return GetInputQueue()->HasReadyTouchBlock();
    4022             : }
    4023             : 
    4024           2 : void AsyncPanZoomController::SetState(PanZoomState aNewState)
    4025             : {
    4026             :   PanZoomState oldState;
    4027             : 
    4028             :   // Intentional scoping for mutex
    4029             :   {
    4030           4 :     ReentrantMonitorAutoEnter lock(mMonitor);
    4031             :     APZC_LOG("%p changing from state %d to %d\n", this, mState, aNewState);
    4032           2 :     oldState = mState;
    4033           2 :     mState = aNewState;
    4034             :   }
    4035             : 
    4036           2 :   DispatchStateChangeNotification(oldState, aNewState);
    4037           2 : }
    4038             : 
    4039          42 : void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldState,
    4040             :                                                              PanZoomState aNewState)
    4041             : {
    4042             :   { // scope the lock
    4043          84 :     ReentrantMonitorAutoEnter lock(mMonitor);
    4044          42 :     if (mNotificationBlockers > 0) {
    4045           0 :       return;
    4046             :     }
    4047             :   }
    4048             : 
    4049          84 :   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
    4050          42 :     if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
    4051           0 :       controller->NotifyAPZStateChange(
    4052           0 :           GetGuid(), APZStateChange::eTransformBegin);
    4053             : #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
    4054             :       // Let the compositor know about scroll state changes so it can manage
    4055             :       // windowed plugins.
    4056           0 :       if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
    4057           0 :         mCompositorController->ScheduleHideAllPluginWindows();
    4058             :       }
    4059             : #endif
    4060          42 :     } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
    4061             : #if defined(MOZ_WIDGET_ANDROID)
    4062             :       // The Android UI thread only shows overlay UI elements when the content is not being
    4063             :       // panned or zoomed and it is in a steady state. So the FrameMetrics only need to be
    4064             :       // updated when the transform ends.
    4065             :       if (APZCTreeManager* manager = GetApzcTreeManager()) {
    4066             :         AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
    4067             :         MOZ_ASSERT(animator);
    4068             :         animator->UpdateRootFrameMetrics(mFrameMetrics);
    4069             :       }
    4070             : #endif
    4071             : 
    4072           0 :       controller->NotifyAPZStateChange(
    4073           0 :           GetGuid(), APZStateChange::eTransformEnd);
    4074             : #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
    4075           0 :       if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
    4076           0 :         mCompositorController->ScheduleShowAllPluginWindows();
    4077             :       }
    4078             : #endif
    4079             :     }
    4080             :   }
    4081             : }
    4082             : 
    4083         161 : bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
    4084         161 :   return !(aState == NOTHING || aState == TOUCHING);
    4085             : }
    4086             : 
    4087           0 : bool AsyncPanZoomController::IsInPanningState() const {
    4088           0 :   return (mState == PANNING || mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y);
    4089             : }
    4090             : 
    4091           1 : void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
    4092             :   APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
    4093             :     aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
    4094           1 :   if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
    4095           0 :     NS_WARNING("APZC received zoom constraints with NaN values; dropping...");
    4096           0 :     return;
    4097             :   }
    4098             : 
    4099           1 :   CSSToParentLayerScale min = mFrameMetrics.GetDevPixelsPerCSSPixel()
    4100           2 :     * kViewportMinScale / ParentLayerToScreenScale(1);
    4101           1 :   CSSToParentLayerScale max = mFrameMetrics.GetDevPixelsPerCSSPixel()
    4102           2 :     * kViewportMaxScale / ParentLayerToScreenScale(1);
    4103             : 
    4104             :   // inf float values and other bad cases should be sanitized by the code below.
    4105           1 :   mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
    4106           1 :   mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
    4107           1 :   mZoomConstraints.mMinZoom = (min > aConstraints.mMinZoom ? min : aConstraints.mMinZoom);
    4108           1 :   mZoomConstraints.mMaxZoom = (max > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : max);
    4109           1 :   if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
    4110           0 :     mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
    4111             :   }
    4112             : }
    4113             : 
    4114             : ZoomConstraints
    4115           0 : AsyncPanZoomController::GetZoomConstraints() const
    4116             : {
    4117           0 :   return mZoomConstraints;
    4118             : }
    4119             : 
    4120             : 
    4121           0 : void AsyncPanZoomController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
    4122           0 :   APZThreadUtils::AssertOnControllerThread();
    4123           0 :   RefPtr<Runnable> task = aTask;
    4124           0 :   RefPtr<GeckoContentController> controller = GetGeckoContentController();
    4125           0 :   if (controller) {
    4126           0 :     controller->PostDelayedTask(task.forget(), aDelayMs);
    4127             :   }
    4128             :   // If there is no controller, that means this APZC has been destroyed, and
    4129             :   // we probably don't need to run the task. It will get destroyed when the
    4130             :   // RefPtr goes out of scope.
    4131           0 : }
    4132             : 
    4133          99 : bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
    4134             : {
    4135          99 :   return aGuid == GetGuid();
    4136             : }
    4137             : 
    4138          32 : bool AsyncPanZoomController::HasTreeManager(const APZCTreeManager* aTreeManager) const
    4139             : {
    4140          32 :   return GetApzcTreeManager() == aTreeManager;
    4141             : }
    4142             : 
    4143           5 : void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const
    4144             : {
    4145           5 :   if (aGuidOut) {
    4146           5 :     *aGuidOut = GetGuid();
    4147             :   }
    4148           5 : }
    4149             : 
    4150         143 : ScrollableLayerGuid AsyncPanZoomController::GetGuid() const
    4151             : {
    4152         143 :   return ScrollableLayerGuid(mLayersId, mFrameMetrics);
    4153             : }
    4154             : 
    4155           7 : void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
    4156             : {
    4157           7 :   mMonitor.AssertCurrentThreadIn();
    4158             : 
    4159           7 :   FrameMetrics* frame = mSharedFrameMetricsBuffer ?
    4160          14 :       static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
    4161             : 
    4162           7 :   if (frame && mSharedLock && gfxPrefs::ProgressivePaint()) {
    4163           0 :     mSharedLock->Lock();
    4164           0 :     *frame = mFrameMetrics;
    4165           0 :     mSharedLock->Unlock();
    4166             :   }
    4167           7 : }
    4168             : 
    4169           2 : void AsyncPanZoomController::ShareCompositorFrameMetrics()
    4170             : {
    4171           2 :   APZThreadUtils::AssertOnCompositorThread();
    4172             : 
    4173             :   // Only create the shared memory buffer if it hasn't already been created,
    4174             :   // we are using progressive tile painting, and we have a
    4175             :   // controller to pass the shared memory back to the content process/thread.
    4176           2 :   if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
    4177             : 
    4178             :     // Create shared memory and initialize it with the current FrameMetrics value
    4179           0 :     mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
    4180           0 :     FrameMetrics* frame = nullptr;
    4181           0 :     mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
    4182           0 :     mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
    4183           0 :     frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
    4184             : 
    4185           0 :     if (frame) {
    4186             : 
    4187             :       { // scope the monitor, only needed to copy the FrameMetrics.
    4188           0 :         ReentrantMonitorAutoEnter lock(mMonitor);
    4189           0 :         *frame = mFrameMetrics;
    4190             :       }
    4191             : 
    4192             :       // Get the process id of the content process
    4193           0 :       base::ProcessId otherPid = mMetricsSharingController->RemotePid();
    4194           0 :       ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
    4195             : 
    4196             :       // Get the shared memory handle to share with the content process
    4197           0 :       mSharedFrameMetricsBuffer->ShareToProcess(otherPid, &mem);
    4198             : 
    4199             :       // Get the cross process mutex handle to share with the content process
    4200           0 :       mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
    4201           0 :       CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(otherPid);
    4202             : 
    4203             :       // Send the shared memory handle and cross process handle to the content
    4204             :       // process by an asynchronous ipc call. Include the APZC unique ID
    4205             :       // so the content process know which APZC sent this shared FrameMetrics.
    4206           0 :       if (!mMetricsSharingController->StartSharingMetrics(mem, handle, mLayersId, mAPZCId)) {
    4207             :         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
    4208             :       }
    4209             :     }
    4210             :   }
    4211           2 : }
    4212             : 
    4213           0 : Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
    4214             :     const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) {
    4215           0 :   mMonitor.AssertCurrentThreadIn();
    4216             :   APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str());
    4217           0 :   CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
    4218           0 :   if (Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
    4219             :           mScrollMetadata.GetSnapInfo(),
    4220             :           aUnit,
    4221           0 :           CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()),
    4222           0 :           CSSRect::ToAppUnits(scrollRange),
    4223           0 :           CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()),
    4224           0 :           CSSPoint::ToAppUnits(aDestination))) {
    4225           0 :     CSSPoint cssSnapPoint = CSSPoint::FromAppUnits(snapPoint.ref());
    4226             :     // GetSnapPointForDestination() can produce a destination that's outside
    4227             :     // of the scroll frame's scroll range. Clamp it here (this matches the
    4228             :     // behaviour of the main-thread code path, which clamps it in
    4229             :     // nsGfxScrollFrame::ScrollTo()).
    4230           0 :     return Some(scrollRange.ClampPoint(cssSnapPoint));
    4231             :   }
    4232           0 :   return Nothing();
    4233             : }
    4234             : 
    4235           0 : void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
    4236           0 :   if (Maybe<CSSPoint> snapPoint =
    4237           0 :         FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) {
    4238           0 :     if (*snapPoint != mFrameMetrics.GetScrollOffset()) {
    4239             :       APZC_LOG("%p smooth scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str());
    4240           0 :       SmoothScrollTo(*snapPoint);
    4241             :     }
    4242             :   }
    4243           0 : }
    4244             : 
    4245           0 : void AsyncPanZoomController::ScrollSnap() {
    4246           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    4247           0 :   ScrollSnapNear(mFrameMetrics.GetScrollOffset());
    4248           0 : }
    4249             : 
    4250           0 : void AsyncPanZoomController::ScrollSnapToDestination() {
    4251           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    4252             : 
    4253           0 :   float friction = gfxPrefs::APZFlingFriction();
    4254           0 :   ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity());
    4255           0 :   ParentLayerPoint predictedDelta;
    4256             :   // "-velocity / log(1.0 - friction)" is the integral of the deceleration
    4257             :   // curve modeled for flings in the "Axis" class.
    4258           0 :   if (velocity.x != 0.0f) {
    4259           0 :     predictedDelta.x = -velocity.x / log(1.0 - friction);
    4260             :   }
    4261           0 :   if (velocity.y != 0.0f) {
    4262           0 :     predictedDelta.y = -velocity.y / log(1.0 - friction);
    4263             :   }
    4264           0 :   CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom();
    4265             : 
    4266             :   // If the fling will overscroll, don't scroll snap, because then the user
    4267             :   // user would not see any overscroll animation.
    4268           0 :   bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) ||
    4269           0 :                                                   (velocity.y * mY.GetOverscroll() >= 0));
    4270           0 :   if (!flingWillOverscroll) {
    4271             :     APZC_LOG("%p fling snapping.  friction: %f velocity: %f, %f "
    4272             :              "predictedDelta: %f, %f position: %f, %f "
    4273             :              "predictedDestination: %f, %f\n",
    4274             :              this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
    4275             :              (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
    4276             :              (float)mFrameMetrics.GetScrollOffset().y,
    4277             :              (float)predictedDestination.x, (float)predictedDestination.y);
    4278             : 
    4279           0 :     ScrollSnapNear(predictedDestination);
    4280             :   }
    4281           0 : }
    4282             : 
    4283           0 : bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
    4284             :     const ScrollWheelInput& aEvent,
    4285             :     ParentLayerPoint& aDelta,
    4286             :     CSSPoint& aStartPosition)
    4287             : {
    4288             :   // Don't scroll snap for pixel scrolls. This matches the main thread
    4289             :   // behaviour in EventStateManager::DoScrollText().
    4290           0 :   if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
    4291           0 :     return false;
    4292             :   }
    4293             : 
    4294           0 :   ReentrantMonitorAutoEnter lock(mMonitor);
    4295           0 :   CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom();
    4296           0 :   CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint(
    4297           0 :       aStartPosition + (aDelta / zoom));
    4298             :   nsIScrollableFrame::ScrollUnit unit =
    4299           0 :       ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType);
    4300             : 
    4301           0 :   if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, unit)) {
    4302           0 :     aDelta = (*snapPoint - aStartPosition) * zoom;
    4303           0 :     aStartPosition = *snapPoint;
    4304           0 :     return true;
    4305             :   }
    4306           0 :   return false;
    4307             : }
    4308             : 
    4309             : } // namespace layers
    4310             : } // namespace mozilla

Generated by: LCOV version 1.13