gfx/layers/apz/src/AsyncPanZoomController.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2209 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set sw=2 ts=8 et tw=80 : */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include <math.h>                       // for fabsf, fabs, atan2
    1.11 +#include <stdint.h>                     // for uint32_t, uint64_t
    1.12 +#include <sys/types.h>                  // for int32_t
    1.13 +#include <algorithm>                    // for max, min
    1.14 +#include "AnimationCommon.h"            // for ComputedTimingFunction
    1.15 +#include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
    1.16 +#include "CompositorParent.h"           // for CompositorParent
    1.17 +#include "FrameMetrics.h"               // for FrameMetrics, etc
    1.18 +#include "GestureEventListener.h"       // for GestureEventListener
    1.19 +#include "InputData.h"                  // for MultiTouchInput, etc
    1.20 +#include "Units.h"                      // for CSSRect, CSSPoint, etc
    1.21 +#include "UnitTransforms.h"             // for TransformTo
    1.22 +#include "base/message_loop.h"          // for MessageLoop
    1.23 +#include "base/task.h"                  // for NewRunnableMethod, etc
    1.24 +#include "base/tracked.h"               // for FROM_HERE
    1.25 +#include "gfxPrefs.h"                   // for gfxPrefs
    1.26 +#include "gfxTypes.h"                   // for gfxFloat
    1.27 +#include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
    1.28 +#include "mozilla/BasicEvents.h"        // for Modifiers, MODIFIER_*
    1.29 +#include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
    1.30 +#include "mozilla/Constants.h"          // for M_PI
    1.31 +#include "mozilla/EventForwards.h"      // for nsEventStatus_*
    1.32 +#include "mozilla/Preferences.h"        // for Preferences
    1.33 +#include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
    1.34 +#include "mozilla/StaticPtr.h"          // for StaticAutoPtr
    1.35 +#include "mozilla/TimeStamp.h"          // for TimeDuration, TimeStamp
    1.36 +#include "mozilla/dom/Touch.h"          // for Touch
    1.37 +#include "mozilla/gfx/BasePoint.h"      // for BasePoint
    1.38 +#include "mozilla/gfx/BaseRect.h"       // for BaseRect
    1.39 +#include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
    1.40 +#include "mozilla/gfx/Rect.h"           // for RoundedIn
    1.41 +#include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
    1.42 +#include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
    1.43 +#include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
    1.44 +#include "mozilla/layers/Axis.h"        // for AxisX, AxisY, Axis, etc
    1.45 +#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
    1.46 +#include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
    1.47 +#include "mozilla/layers/TaskThrottler.h"  // for TaskThrottler
    1.48 +#include "mozilla/mozalloc.h"           // for operator new, etc
    1.49 +#include "mozilla/unused.h"             // for unused
    1.50 +#include "mozilla/FloatingPoint.h"      // for FuzzyEqualsMultiplicative
    1.51 +#include "nsAlgorithm.h"                // for clamped
    1.52 +#include "nsAutoPtr.h"                  // for nsRefPtr
    1.53 +#include "nsCOMPtr.h"                   // for already_AddRefed
    1.54 +#include "nsDebug.h"                    // for NS_WARNING
    1.55 +#include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
    1.56 +#include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
    1.57 +#include "nsMathUtils.h"                // for NS_hypot
    1.58 +#include "nsPoint.h"                    // for nsIntPoint
    1.59 +#include "nsStyleConsts.h"
    1.60 +#include "nsStyleStruct.h"              // for nsTimingFunction
    1.61 +#include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
    1.62 +#include "nsThreadUtils.h"              // for NS_IsMainThread
    1.63 +#include "SharedMemoryBasic.h"          // for SharedMemoryBasic
    1.64 +
    1.65 +// #define APZC_ENABLE_RENDERTRACE
    1.66 +
    1.67 +#define APZC_LOG(...)
    1.68 +// #define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
    1.69 +#define APZC_LOG_FM(fm, prefix, ...) \
    1.70 +  APZC_LOG(prefix ":" \
    1.71 +           " i=(%ld %lld) cb=(%d %d %d %d) rcs=(%.3f %.3f) dp=(%.3f %.3f %.3f %.3f) dpm=(%.3f %.3f %.3f %.3f) um=%d " \
    1.72 +           "v=(%.3f %.3f %.3f %.3f) s=(%.3f %.3f) sr=(%.3f %.3f %.3f %.3f) z=(%.3f %.3f %.3f %.3f) u=(%d %lu)\n", \
    1.73 +           __VA_ARGS__, \
    1.74 +           fm.mPresShellId, fm.GetScrollId(), \
    1.75 +           fm.mCompositionBounds.x, fm.mCompositionBounds.y, fm.mCompositionBounds.width, fm.mCompositionBounds.height, \
    1.76 +           fm.GetRootCompositionSize().width, fm.GetRootCompositionSize().height, \
    1.77 +           fm.mDisplayPort.x, fm.mDisplayPort.y, fm.mDisplayPort.width, fm.mDisplayPort.height, \
    1.78 +           fm.GetDisplayPortMargins().top, fm.GetDisplayPortMargins().right, fm.GetDisplayPortMargins().bottom, fm.GetDisplayPortMargins().left, \
    1.79 +           fm.GetUseDisplayPortMargins() ? 1 : 0, \
    1.80 +           fm.mViewport.x, fm.mViewport.y, fm.mViewport.width, fm.mViewport.height, \
    1.81 +           fm.GetScrollOffset().x, fm.GetScrollOffset().y, \
    1.82 +           fm.mScrollableRect.x, fm.mScrollableRect.y, fm.mScrollableRect.width, fm.mScrollableRect.height, \
    1.83 +           fm.mDevPixelsPerCSSPixel.scale, fm.mResolution.scale, fm.mCumulativeResolution.scale, fm.GetZoom().scale, \
    1.84 +           fm.GetScrollOffsetUpdated(), fm.GetScrollGeneration()); \
    1.85 +
    1.86 +// Static helper functions
    1.87 +namespace {
    1.88 +
    1.89 +int32_t
    1.90 +WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
    1.91 +{
    1.92 +  int32_t result = 0;
    1.93 +  if (aModifiers & mozilla::MODIFIER_SHIFT) {
    1.94 +    result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
    1.95 +  }
    1.96 +  if (aModifiers & mozilla::MODIFIER_CONTROL) {
    1.97 +    result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
    1.98 +  }
    1.99 +  if (aModifiers & mozilla::MODIFIER_ALT) {
   1.100 +    result |= nsIDOMWindowUtils::MODIFIER_ALT;
   1.101 +  }
   1.102 +  if (aModifiers & mozilla::MODIFIER_META) {
   1.103 +    result |= nsIDOMWindowUtils::MODIFIER_META;
   1.104 +  }
   1.105 +  if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
   1.106 +    result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
   1.107 +  }
   1.108 +  if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
   1.109 +    result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
   1.110 +  }
   1.111 +  if (aModifiers & mozilla::MODIFIER_FN) {
   1.112 +    result |= nsIDOMWindowUtils::MODIFIER_FN;
   1.113 +  }
   1.114 +  if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
   1.115 +    result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
   1.116 +  }
   1.117 +  if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
   1.118 +    result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
   1.119 +  }
   1.120 +  if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
   1.121 +    result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
   1.122 +  }
   1.123 +  if (aModifiers & mozilla::MODIFIER_OS) {
   1.124 +    result |= nsIDOMWindowUtils::MODIFIER_OS;
   1.125 +  }
   1.126 +  return result;
   1.127 +}
   1.128 +
   1.129 +}
   1.130 +
   1.131 +using namespace mozilla::css;
   1.132 +
   1.133 +namespace mozilla {
   1.134 +namespace layers {
   1.135 +
   1.136 +typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
   1.137 +typedef GeckoContentController::APZStateChange APZStateChange;
   1.138 +
   1.139 +/*
   1.140 + * The following prefs are used to control the behaviour of the APZC.
   1.141 + * The default values are provided in gfxPrefs.h.
   1.142 + *
   1.143 + * "apz.allow-checkerboarding"
   1.144 + * Pref that allows or disallows checkerboarding
   1.145 + *
   1.146 + * "apz.asyncscroll.throttle"
   1.147 + * The time period in ms that throttles mozbrowserasyncscroll event.
   1.148 + *
   1.149 + * "apz.asyncscroll.timeout"
   1.150 + * The timeout in ms for mAsyncScrollTimeoutTask delay task.
   1.151 + *
   1.152 + * "apz.axis_lock_mode"
   1.153 + * The preferred axis locking style. See AxisLockMode for possible values.
   1.154 + *
   1.155 + * "apz.content_response_timeout"
   1.156 + * Amount of time before we timeout response from content. For example, if
   1.157 + * content is being unruly/slow and we don't get a response back within this
   1.158 + * time, we will just pretend that content did not preventDefault any touch
   1.159 + * events we dispatched to it.
   1.160 + *
   1.161 + * "apz.cross_slide_enabled"
   1.162 + * Pref that enables integration with the Metro "cross-slide" gesture.
   1.163 + *
   1.164 + * "apz.enlarge_displayport_when_clipped"
   1.165 + * Pref that enables enlarging of the displayport along one axis when the
   1.166 + * generated displayport's size is beyond that of the scrollable rect on the
   1.167 + * opposite axis.
   1.168 + *
   1.169 + * "apz.fling_friction"
   1.170 + * Amount of friction applied during flings.
   1.171 + *
   1.172 + * "apz.fling_repaint_interval"
   1.173 + * Maximum amount of time flinging before sending a viewport change. This will
   1.174 + * asynchronously repaint the page.
   1.175 + *
   1.176 + * "apz.fling_stopped_threshold"
   1.177 + * When flinging, if the velocity goes below this number, we just stop the
   1.178 + * animation completely. This is to prevent asymptotically approaching 0
   1.179 + * velocity and rerendering unnecessarily.
   1.180 + *
   1.181 + * "apz.max_velocity_inches_per_ms"
   1.182 + * Maximum velocity in inches per millisecond.  Velocity will be capped at this
   1.183 + * value if a faster fling occurs.  Negative values indicate unlimited velocity.
   1.184 + *
   1.185 + * "apz.max_velocity_queue_size"
   1.186 + * Maximum size of velocity queue. The queue contains last N velocity records.
   1.187 + * On touch end we calculate the average velocity in order to compensate
   1.188 + * touch/mouse drivers misbehaviour.
   1.189 + *
   1.190 + * "apz.min_skate_speed"
   1.191 + * Minimum amount of speed along an axis before we switch to "skate" multipliers
   1.192 + * rather than using the "stationary" multipliers.
   1.193 + *
   1.194 + * "apz.num_paint_duration_samples"
   1.195 + * Number of samples to store of how long it took to paint after the previous
   1.196 + * requests.
   1.197 + *
   1.198 + * "apz.pan_repaint_interval"
   1.199 + * Maximum amount of time while panning before sending a viewport change. This
   1.200 + * will asynchronously repaint the page. It is also forced when panning stops.
   1.201 + *
   1.202 + * "apz.touch_start_tolerance"
   1.203 + * Constant describing the tolerance in distance we use, multiplied by the
   1.204 + * device DPI, before we start panning the screen. This is to prevent us from
   1.205 + * accidentally processing taps as touch moves, and from very short/accidental
   1.206 + * touches moving the screen.
   1.207 + *
   1.208 + * "apz.use_paint_duration"
   1.209 + * Whether or not to use the estimated paint duration as a factor when projecting
   1.210 + * the displayport in the direction of scrolling. If this value is set to false,
   1.211 + * a constant 50ms paint time is used; the projection can be scaled as desired
   1.212 + * using the apz.velocity_bias pref below.
   1.213 + *
   1.214 + * "apz.velocity_bias"
   1.215 + * How much to adjust the displayport in the direction of scrolling. This value
   1.216 + * is multiplied by the velocity and added to the displayport offset.
   1.217 + *
   1.218 + * "apz.x_skate_size_multiplier", "apz.y_skate_size_multiplier"
   1.219 + * The multiplier we apply to the displayport size if it is skating (current
   1.220 + * velocity is above apz.min_skate_speed). We prefer to increase the size of the
   1.221 + * Y axis because it is more natural in the case that a user is reading a page
   1.222 + * that scrolls up/down. Note that one, both or neither of these may be used
   1.223 + * at any instant.
   1.224 + * In general we want apz.[xy]_skate_size_multiplier to be smaller than the corresponding
   1.225 + * stationary size multiplier because when panning fast we would like to paint
   1.226 + * less and get faster, more predictable paint times. When panning slowly we
   1.227 + * can afford to paint more even though it's slower.
   1.228 + *
   1.229 + * "apz.x_stationary_size_multiplier", "apz.y_stationary_size_multiplier"
   1.230 + * The multiplier we apply to the displayport size if it is not skating (see
   1.231 + * documentation for the skate size multipliers above).
   1.232 + */
   1.233 +
   1.234 +/**
   1.235 + * Default touch behavior (is used when not touch behavior is set).
   1.236 + */
   1.237 +static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
   1.238 +                                             AllowedTouchBehavior::HORIZONTAL_PAN |
   1.239 +                                             AllowedTouchBehavior::PINCH_ZOOM |
   1.240 +                                             AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
   1.241 +
   1.242 +/**
   1.243 + * Angle from axis within which we stay axis-locked
   1.244 + */
   1.245 +static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees
   1.246 +
   1.247 +/**
   1.248 + * The distance in inches the user must pan before axis lock can be broken
   1.249 + */
   1.250 +static const float AXIS_BREAKOUT_THRESHOLD = 1.0f/32.0f;
   1.251 +
   1.252 +/**
   1.253 + * The angle at which axis lock can be broken
   1.254 + */
   1.255 +static const double AXIS_BREAKOUT_ANGLE = M_PI / 8.0; // 22.5 degrees
   1.256 +
   1.257 +/**
   1.258 + * Angle from axis to the line drawn by pan move.
   1.259 + * If angle is less than this value we can assume that panning
   1.260 + * can be done in allowed direction (horizontal or vertical).
   1.261 + * Currently used only for touch-action css property stuff and was
   1.262 + * added to keep behavior consistent with IE.
   1.263 + */
   1.264 +static const double ALLOWED_DIRECT_PAN_ANGLE = M_PI / 3.0; // 60 degrees
   1.265 +
   1.266 +/**
   1.267 + * Duration of a zoom to animation.
   1.268 + */
   1.269 +static const TimeDuration ZOOM_TO_DURATION = TimeDuration::FromSeconds(0.25);
   1.270 +
   1.271 +/**
   1.272 + * Computed time function used for sampling frames of a zoom to animation.
   1.273 + */
   1.274 +StaticAutoPtr<ComputedTimingFunction> gComputedTimingFunction;
   1.275 +
   1.276 +/**
   1.277 + * Maximum zoom amount, always used, even if a page asks for higher.
   1.278 + */
   1.279 +static const CSSToScreenScale MAX_ZOOM(8.0f);
   1.280 +
   1.281 +/**
   1.282 + * Minimum zoom amount, always used, even if a page asks for lower.
   1.283 + */
   1.284 +static const CSSToScreenScale MIN_ZOOM(0.125f);
   1.285 +
   1.286 +/**
   1.287 + * Is aAngle within the given threshold of the horizontal axis?
   1.288 + * @param aAngle an angle in radians in the range [0, pi]
   1.289 + * @param aThreshold an angle in radians in the range [0, pi/2]
   1.290 + */
   1.291 +static bool IsCloseToHorizontal(float aAngle, float aThreshold)
   1.292 +{
   1.293 +  return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
   1.294 +}
   1.295 +
   1.296 +// As above, but for the vertical axis.
   1.297 +static bool IsCloseToVertical(float aAngle, float aThreshold)
   1.298 +{
   1.299 +  return (fabs(aAngle - (M_PI / 2)) < aThreshold);
   1.300 +}
   1.301 +
   1.302 +template <typename Units>
   1.303 +static bool IsZero(const gfx::PointTyped<Units>& aPoint)
   1.304 +{
   1.305 +  return FuzzyEqualsMultiplicative(aPoint.x, 0.0f)
   1.306 +      && FuzzyEqualsMultiplicative(aPoint.y, 0.0f);
   1.307 +}
   1.308 +
   1.309 +static inline void LogRendertraceRect(const ScrollableLayerGuid& aGuid, const char* aDesc, const char* aColor, const CSSRect& aRect)
   1.310 +{
   1.311 +#ifdef APZC_ENABLE_RENDERTRACE
   1.312 +  static const TimeStamp sRenderStart = TimeStamp::Now();
   1.313 +  TimeDuration delta = TimeStamp::Now() - sRenderStart;
   1.314 +  printf_stderr("(%llu,%lu,%llu)%s RENDERTRACE %f rect %s %f %f %f %f\n",
   1.315 +    aGuid.mLayersId, aGuid.mPresShellId, aGuid.GetScrollId(),
   1.316 +    aDesc, delta.ToMilliseconds(), aColor,
   1.317 +    aRect.x, aRect.y, aRect.width, aRect.height);
   1.318 +#endif
   1.319 +}
   1.320 +
   1.321 +static TimeStamp sFrameTime;
   1.322 +
   1.323 +// Counter used to give each APZC a unique id
   1.324 +static uint32_t sAsyncPanZoomControllerCount = 0;
   1.325 +
   1.326 +static TimeStamp
   1.327 +GetFrameTime() {
   1.328 +  if (sFrameTime.IsNull()) {
   1.329 +    return TimeStamp::Now();
   1.330 +  }
   1.331 +  return sFrameTime;
   1.332 +}
   1.333 +
   1.334 +class FlingAnimation: public AsyncPanZoomAnimation {
   1.335 +public:
   1.336 +  FlingAnimation(AsyncPanZoomController& aApzc)
   1.337 +    : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
   1.338 +    , mApzc(aApzc)
   1.339 +  {}
   1.340 +  /**
   1.341 +   * Advances a fling by an interpolated amount based on the passed in |aDelta|.
   1.342 +   * This should be called whenever sampling the content transform for this
   1.343 +   * frame. Returns true if the fling animation should be advanced by one frame,
   1.344 +   * or false if there is no fling or the fling has ended.
   1.345 +   */
   1.346 +  virtual bool Sample(FrameMetrics& aFrameMetrics,
   1.347 +                      const TimeDuration& aDelta);
   1.348 +
   1.349 +private:
   1.350 +  AsyncPanZoomController& mApzc;
   1.351 +};
   1.352 +
   1.353 +class ZoomAnimation: public AsyncPanZoomAnimation {
   1.354 +public:
   1.355 +  ZoomAnimation(CSSPoint aStartOffset, CSSToScreenScale aStartZoom,
   1.356 +                CSSPoint aEndOffset, CSSToScreenScale aEndZoom)
   1.357 +    : mStartOffset(aStartOffset)
   1.358 +    , mStartZoom(aStartZoom)
   1.359 +    , mEndOffset(aEndOffset)
   1.360 +    , mEndZoom(aEndZoom)
   1.361 +  {}
   1.362 +
   1.363 +  virtual bool Sample(FrameMetrics& aFrameMetrics,
   1.364 +                      const TimeDuration& aDelta);
   1.365 +
   1.366 +private:
   1.367 +  TimeDuration mDuration;
   1.368 +
   1.369 +  // Old metrics from before we started a zoom animation. This is only valid
   1.370 +  // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
   1.371 +  // interpolate between the start and end frames. We only use the
   1.372 +  // |mViewportScrollOffset| and |mResolution| fields on this.
   1.373 +  CSSPoint mStartOffset;
   1.374 +  CSSToScreenScale mStartZoom;
   1.375 +
   1.376 +  // Target metrics for a zoom to animation. This is only valid when we are in
   1.377 +  // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
   1.378 +  // |mResolution| fields on this.
   1.379 +  CSSPoint mEndOffset;
   1.380 +  CSSToScreenScale mEndZoom;
   1.381 +};
   1.382 +
   1.383 +void
   1.384 +AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
   1.385 +  sFrameTime = aTime;
   1.386 +}
   1.387 +
   1.388 +/*static*/ void
   1.389 +AsyncPanZoomController::InitializeGlobalState()
   1.390 +{
   1.391 +  MOZ_ASSERT(NS_IsMainThread());
   1.392 +
   1.393 +  static bool sInitialized = false;
   1.394 +  if (sInitialized)
   1.395 +    return;
   1.396 +  sInitialized = true;
   1.397 +
   1.398 +  gComputedTimingFunction = new ComputedTimingFunction();
   1.399 +  gComputedTimingFunction->Init(
   1.400 +    nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
   1.401 +  ClearOnShutdown(&gComputedTimingFunction);
   1.402 +}
   1.403 +
   1.404 +AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
   1.405 +                                               APZCTreeManager* aTreeManager,
   1.406 +                                               GeckoContentController* aGeckoContentController,
   1.407 +                                               GestureBehavior aGestures)
   1.408 +  :  mLayersId(aLayersId),
   1.409 +     mCrossProcessCompositorParent(nullptr),
   1.410 +     mPaintThrottler(GetFrameTime()),
   1.411 +     mGeckoContentController(aGeckoContentController),
   1.412 +     mRefPtrMonitor("RefPtrMonitor"),
   1.413 +     mMonitor("AsyncPanZoomController"),
   1.414 +     mTouchActionPropertyEnabled(gfxPrefs::TouchActionEnabled()),
   1.415 +     mContentResponseTimeoutTask(nullptr),
   1.416 +     mX(MOZ_THIS_IN_INITIALIZER_LIST()),
   1.417 +     mY(MOZ_THIS_IN_INITIALIZER_LIST()),
   1.418 +     mPanDirRestricted(false),
   1.419 +     mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
   1.420 +     mLastSampleTime(GetFrameTime()),
   1.421 +     mState(NOTHING),
   1.422 +     mLastAsyncScrollTime(GetFrameTime()),
   1.423 +     mLastAsyncScrollOffset(0, 0),
   1.424 +     mCurrentAsyncScrollOffset(0, 0),
   1.425 +     mAsyncScrollTimeoutTask(nullptr),
   1.426 +     mHandlingTouchQueue(false),
   1.427 +     mTreeManager(aTreeManager),
   1.428 +     mScrollParentId(FrameMetrics::NULL_SCROLL_ID),
   1.429 +     mAPZCId(sAsyncPanZoomControllerCount++),
   1.430 +     mSharedFrameMetricsBuffer(nullptr),
   1.431 +     mSharedLock(nullptr)
   1.432 +{
   1.433 +  MOZ_COUNT_CTOR(AsyncPanZoomController);
   1.434 +
   1.435 +  if (aGestures == USE_GESTURE_DETECTOR) {
   1.436 +    mGestureEventListener = new GestureEventListener(this);
   1.437 +  }
   1.438 +}
   1.439 +
   1.440 +AsyncPanZoomController::~AsyncPanZoomController() {
   1.441 +
   1.442 +  PCompositorParent* compositor =
   1.443 +    (mCrossProcessCompositorParent ? mCrossProcessCompositorParent : mCompositorParent.get());
   1.444 +
   1.445 +  // Only send the release message if the SharedFrameMetrics has been created.
   1.446 +  if (compositor && mSharedFrameMetricsBuffer) {
   1.447 +    unused << compositor->SendReleaseSharedCompositorFrameMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
   1.448 +  }
   1.449 +
   1.450 +  delete mSharedFrameMetricsBuffer;
   1.451 +  delete mSharedLock;
   1.452 +
   1.453 +  MOZ_COUNT_DTOR(AsyncPanZoomController);
   1.454 +}
   1.455 +
   1.456 +already_AddRefed<GeckoContentController>
   1.457 +AsyncPanZoomController::GetGeckoContentController() {
   1.458 +  MonitorAutoLock lock(mRefPtrMonitor);
   1.459 +  nsRefPtr<GeckoContentController> controller = mGeckoContentController;
   1.460 +  return controller.forget();
   1.461 +}
   1.462 +
   1.463 +already_AddRefed<GestureEventListener>
   1.464 +AsyncPanZoomController::GetGestureEventListener() {
   1.465 +  MonitorAutoLock lock(mRefPtrMonitor);
   1.466 +  nsRefPtr<GestureEventListener> listener = mGestureEventListener;
   1.467 +  return listener.forget();
   1.468 +}
   1.469 +
   1.470 +void
   1.471 +AsyncPanZoomController::Destroy()
   1.472 +{
   1.473 +  { // scope the lock
   1.474 +    MonitorAutoLock lock(mRefPtrMonitor);
   1.475 +    mGeckoContentController = nullptr;
   1.476 +    mGestureEventListener = nullptr;
   1.477 +  }
   1.478 +  mPrevSibling = nullptr;
   1.479 +  mLastChild = nullptr;
   1.480 +  mParent = nullptr;
   1.481 +  mTreeManager = nullptr;
   1.482 +}
   1.483 +
   1.484 +bool
   1.485 +AsyncPanZoomController::IsDestroyed()
   1.486 +{
   1.487 +  return mTreeManager == nullptr;
   1.488 +}
   1.489 +
   1.490 +/* static */float
   1.491 +AsyncPanZoomController::GetTouchStartTolerance()
   1.492 +{
   1.493 +  return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
   1.494 +}
   1.495 +
   1.496 +/* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
   1.497 +{
   1.498 +  return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
   1.499 +}
   1.500 +
   1.501 +nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
   1.502 +  if (aEvent.mInputType == MULTITOUCH_INPUT &&
   1.503 +      aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
   1.504 +    // Starting a new touch block, clear old touch block state.
   1.505 +    mTouchBlockState = TouchBlockState();
   1.506 +  }
   1.507 +
   1.508 +  // If we may have touch listeners and touch action property is enabled, we
   1.509 +  // enable the machinery that allows touch listeners to preventDefault any touch inputs
   1.510 +  // and also waits for the allowed touch behavior values to be received from the outside.
   1.511 +  // This should not happen unless there are actually touch listeners and touch-action property
   1.512 +  // enable as it introduces potentially unbounded lag because it causes a round-trip through
   1.513 +  // content.  Usually, if content is responding in a timely fashion, this only introduces a
   1.514 +  // nearly constant few hundred ms of lag.
   1.515 +  if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
   1.516 +      (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
   1.517 +    const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   1.518 +    if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
   1.519 +      SetState(WAITING_CONTENT_RESPONSE);
   1.520 +    }
   1.521 +  }
   1.522 +
   1.523 +  if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
   1.524 +    if (aEvent.mInputType == MULTITOUCH_INPUT) {
   1.525 +      const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   1.526 +      mTouchQueue.AppendElement(multiTouchInput);
   1.527 +
   1.528 +      SetContentResponseTimer();
   1.529 +    }
   1.530 +    return nsEventStatus_eIgnore;
   1.531 +  }
   1.532 +
   1.533 +  return HandleInputEvent(aEvent);
   1.534 +}
   1.535 +
   1.536 +nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
   1.537 +  nsEventStatus rv = nsEventStatus_eIgnore;
   1.538 +
   1.539 +  switch (aEvent.mInputType) {
   1.540 +  case MULTITOUCH_INPUT: {
   1.541 +    const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   1.542 +
   1.543 +    nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
   1.544 +    if (listener) {
   1.545 +      rv = listener->HandleInputEvent(multiTouchInput);
   1.546 +      if (rv == nsEventStatus_eConsumeNoDefault) {
   1.547 +        return rv;
   1.548 +      }
   1.549 +    }
   1.550 +
   1.551 +    switch (multiTouchInput.mType) {
   1.552 +      case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
   1.553 +      case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
   1.554 +      case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
   1.555 +      case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
   1.556 +      default: NS_WARNING("Unhandled multitouch"); break;
   1.557 +    }
   1.558 +    break;
   1.559 +  }
   1.560 +  default: NS_WARNING("Unhandled input event"); break;
   1.561 +  }
   1.562 +
   1.563 +  mLastEventTime = aEvent.mTime;
   1.564 +  return rv;
   1.565 +}
   1.566 +
   1.567 +nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
   1.568 +{
   1.569 +  nsEventStatus rv = nsEventStatus_eIgnore;
   1.570 +
   1.571 +  switch (aEvent.mInputType) {
   1.572 +  case PINCHGESTURE_INPUT: {
   1.573 +    const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
   1.574 +    switch (pinchGestureInput.mType) {
   1.575 +      case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
   1.576 +      case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
   1.577 +      case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
   1.578 +      default: NS_WARNING("Unhandled pinch gesture"); break;
   1.579 +    }
   1.580 +    break;
   1.581 +  }
   1.582 +  case TAPGESTURE_INPUT: {
   1.583 +    const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
   1.584 +    switch (tapGestureInput.mType) {
   1.585 +      case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
   1.586 +      case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
   1.587 +      case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
   1.588 +      case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
   1.589 +      case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
   1.590 +      case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
   1.591 +      default: NS_WARNING("Unhandled tap gesture"); break;
   1.592 +    }
   1.593 +    break;
   1.594 +  }
   1.595 +  default: NS_WARNING("Unhandled input event"); break;
   1.596 +  }
   1.597 +
   1.598 +  mLastEventTime = aEvent.mTime;
   1.599 +  return rv;
   1.600 +}
   1.601 +
   1.602 +nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   1.603 +  APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   1.604 +  mPanDirRestricted = false;
   1.605 +  ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
   1.606 +
   1.607 +  switch (mState) {
   1.608 +    case ANIMATING_ZOOM:
   1.609 +      // We just interrupted a double-tap animation, so force a redraw in case
   1.610 +      // this touchstart is just a tap that doesn't end up triggering a redraw.
   1.611 +      {
   1.612 +        ReentrantMonitorAutoEnter lock(mMonitor);
   1.613 +        RequestContentRepaint();
   1.614 +        ScheduleComposite();
   1.615 +        UpdateSharedCompositorFrameMetrics();
   1.616 +      }
   1.617 +      // Fall through.
   1.618 +    case FLING:
   1.619 +      CancelAnimation();
   1.620 +      // Fall through.
   1.621 +    case NOTHING: {
   1.622 +      mX.StartTouch(point.x);
   1.623 +      mY.StartTouch(point.y);
   1.624 +      APZCTreeManager* treeManagerLocal = mTreeManager;
   1.625 +      nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   1.626 +      if (treeManagerLocal && controller) {
   1.627 +        bool touchCanBePan = treeManagerLocal->CanBePanned(this);
   1.628 +        controller->NotifyAPZStateChange(
   1.629 +            GetGuid(), APZStateChange::StartTouch, touchCanBePan);
   1.630 +      }
   1.631 +      SetState(TOUCHING);
   1.632 +      break;
   1.633 +    }
   1.634 +    case TOUCHING:
   1.635 +    case PANNING:
   1.636 +    case PANNING_LOCKED_X:
   1.637 +    case PANNING_LOCKED_Y:
   1.638 +    case CROSS_SLIDING_X:
   1.639 +    case CROSS_SLIDING_Y:
   1.640 +    case PINCHING:
   1.641 +    case WAITING_CONTENT_RESPONSE:
   1.642 +      NS_WARNING("Received impossible touch in OnTouchStart");
   1.643 +      break;
   1.644 +    default:
   1.645 +      NS_WARNING("Unhandled case in OnTouchStart");
   1.646 +      break;
   1.647 +  }
   1.648 +
   1.649 +  return nsEventStatus_eConsumeNoDefault;
   1.650 +}
   1.651 +
   1.652 +nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
   1.653 +  APZC_LOG("%p got a touch-move in state %d\n", this, mState);
   1.654 +  switch (mState) {
   1.655 +    case FLING:
   1.656 +    case NOTHING:
   1.657 +    case ANIMATING_ZOOM:
   1.658 +      // May happen if the user double-taps and drags without lifting after the
   1.659 +      // second tap. Ignore the move if this happens.
   1.660 +      return nsEventStatus_eIgnore;
   1.661 +
   1.662 +    case CROSS_SLIDING_X:
   1.663 +    case CROSS_SLIDING_Y:
   1.664 +      // While cross-sliding, we don't want to consume any touchmove events for
   1.665 +      // panning or zooming, and let the caller handle them instead.
   1.666 +      return nsEventStatus_eIgnore;
   1.667 +
   1.668 +    case TOUCHING: {
   1.669 +      float panThreshold = GetTouchStartTolerance();
   1.670 +      UpdateWithTouchAtDevicePoint(aEvent);
   1.671 +
   1.672 +      if (PanDistance() < panThreshold) {
   1.673 +        return nsEventStatus_eIgnore;
   1.674 +      }
   1.675 +
   1.676 +      if (mTouchActionPropertyEnabled &&
   1.677 +          (GetTouchBehavior(0) & AllowedTouchBehavior::VERTICAL_PAN) &&
   1.678 +          (GetTouchBehavior(0) & AllowedTouchBehavior::HORIZONTAL_PAN)) {
   1.679 +        // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
   1.680 +        // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
   1.681 +        // status immediately to trigger cancel event further. It should happen independent of
   1.682 +        // the parent type (whether it is scrolling or not).
   1.683 +        StartPanning(aEvent);
   1.684 +        return nsEventStatus_eConsumeNoDefault;
   1.685 +      }
   1.686 +
   1.687 +      return StartPanning(aEvent);
   1.688 +    }
   1.689 +
   1.690 +    case PANNING:
   1.691 +    case PANNING_LOCKED_X:
   1.692 +    case PANNING_LOCKED_Y:
   1.693 +      TrackTouch(aEvent);
   1.694 +      return nsEventStatus_eConsumeNoDefault;
   1.695 +
   1.696 +    case PINCHING:
   1.697 +      // The scale gesture listener should have handled this.
   1.698 +      NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
   1.699 +      return nsEventStatus_eIgnore;
   1.700 +
   1.701 +    case WAITING_CONTENT_RESPONSE:
   1.702 +      NS_WARNING("Received impossible touch in OnTouchMove");
   1.703 +      break;
   1.704 +  }
   1.705 +
   1.706 +  return nsEventStatus_eConsumeNoDefault;
   1.707 +}
   1.708 +
   1.709 +nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
   1.710 +  APZC_LOG("%p got a touch-end in state %d\n", this, mState);
   1.711 +
   1.712 +  OnTouchEndOrCancel();
   1.713 +
   1.714 +  // In case no touch behavior triggered previously we can avoid sending
   1.715 +  // scroll events or requesting content repaint. This condition is added
   1.716 +  // to make tests consistent - in case touch-action is NONE (and therefore
   1.717 +  // no pans/zooms can be performed) we expected neither scroll or repaint
   1.718 +  // events.
   1.719 +  if (mState != NOTHING) {
   1.720 +    ReentrantMonitorAutoEnter lock(mMonitor);
   1.721 +    SendAsyncScrollEvent();
   1.722 +  }
   1.723 +
   1.724 +  switch (mState) {
   1.725 +  case FLING:
   1.726 +    // Should never happen.
   1.727 +    NS_WARNING("Received impossible touch end in OnTouchEnd.");
   1.728 +    // Fall through.
   1.729 +  case ANIMATING_ZOOM:
   1.730 +  case NOTHING:
   1.731 +    // May happen if the user double-taps and drags without lifting after the
   1.732 +    // second tap. Ignore if this happens.
   1.733 +    return nsEventStatus_eIgnore;
   1.734 +
   1.735 +  case TOUCHING:
   1.736 +  case CROSS_SLIDING_X:
   1.737 +  case CROSS_SLIDING_Y:
   1.738 +    SetState(NOTHING);
   1.739 +    return nsEventStatus_eIgnore;
   1.740 +
   1.741 +  case PANNING:
   1.742 +  case PANNING_LOCKED_X:
   1.743 +  case PANNING_LOCKED_Y:
   1.744 +    {
   1.745 +      // Make a local copy of the tree manager pointer and check if it's not
   1.746 +      // null before calling FlushRepaintsForOverscrollHandoffChain().
   1.747 +      // This is necessary because Destroy(), which nulls out mTreeManager,
   1.748 +      // could be called concurrently.
   1.749 +      APZCTreeManager* treeManagerLocal = mTreeManager;
   1.750 +      if (treeManagerLocal) {
   1.751 +        if (!treeManagerLocal->FlushRepaintsForOverscrollHandoffChain()) {
   1.752 +          NS_WARNING("Overscroll handoff chain was empty during panning! This should not be the case.");
   1.753 +          // Graceful handling of error condition
   1.754 +          FlushRepaintForOverscrollHandoff();
   1.755 +        }
   1.756 +      }
   1.757 +    }
   1.758 +    mX.EndTouch();
   1.759 +    mY.EndTouch();
   1.760 +    SetState(FLING);
   1.761 +    StartAnimation(new FlingAnimation(*this));
   1.762 +    return nsEventStatus_eConsumeNoDefault;
   1.763 +
   1.764 +  case PINCHING:
   1.765 +    SetState(NOTHING);
   1.766 +    // Scale gesture listener should have handled this.
   1.767 +    NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
   1.768 +    return nsEventStatus_eIgnore;
   1.769 +
   1.770 +  case WAITING_CONTENT_RESPONSE:
   1.771 +    NS_WARNING("Received impossible touch in OnTouchEnd");
   1.772 +    break;
   1.773 +  }
   1.774 +
   1.775 +  return nsEventStatus_eConsumeNoDefault;
   1.776 +}
   1.777 +
   1.778 +nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
   1.779 +  APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
   1.780 +  OnTouchEndOrCancel();
   1.781 +  SetState(NOTHING);
   1.782 +  return nsEventStatus_eConsumeNoDefault;
   1.783 +}
   1.784 +
   1.785 +nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   1.786 +  APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
   1.787 +
   1.788 +  if (!TouchActionAllowPinchZoom()) {
   1.789 +    return nsEventStatus_eIgnore;
   1.790 +  }
   1.791 +
   1.792 +  if (!mZoomConstraints.mAllowZoom) {
   1.793 +    return nsEventStatus_eConsumeNoDefault;
   1.794 +  }
   1.795 +
   1.796 +  SetState(PINCHING);
   1.797 +  mLastZoomFocus = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
   1.798 +
   1.799 +  return nsEventStatus_eConsumeNoDefault;
   1.800 +}
   1.801 +
   1.802 +nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
   1.803 +  APZC_LOG("%p got a scale in state %d\n", this, mState);
   1.804 +  if (mState != PINCHING) {
   1.805 +    return nsEventStatus_eConsumeNoDefault;
   1.806 +  }
   1.807 +
   1.808 +  float prevSpan = aEvent.mPreviousSpan;
   1.809 +  if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
   1.810 +    // We're still handling it; we've just decided to throw this event away.
   1.811 +    return nsEventStatus_eConsumeNoDefault;
   1.812 +  }
   1.813 +
   1.814 +  float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
   1.815 +
   1.816 +  {
   1.817 +    ReentrantMonitorAutoEnter lock(mMonitor);
   1.818 +
   1.819 +    CSSToParentLayerScale userZoom = mFrameMetrics.GetZoomToParent();
   1.820 +    ParentLayerPoint focusPoint = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
   1.821 +    CSSPoint cssFocusPoint = focusPoint / userZoom;
   1.822 +
   1.823 +    CSSPoint focusChange = (mLastZoomFocus - focusPoint) / userZoom;
   1.824 +    // If displacing by the change in focus point will take us off page bounds,
   1.825 +    // then reduce the displacement such that it doesn't.
   1.826 +    if (mX.DisplacementWillOverscroll(focusChange.x) != Axis::OVERSCROLL_NONE) {
   1.827 +      focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
   1.828 +    }
   1.829 +    if (mY.DisplacementWillOverscroll(focusChange.y) != Axis::OVERSCROLL_NONE) {
   1.830 +      focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
   1.831 +    }
   1.832 +    ScrollBy(focusChange);
   1.833 +
   1.834 +    // When we zoom in with focus, we can zoom too much towards the boundaries
   1.835 +    // that we actually go over them. These are the needed displacements along
   1.836 +    // either axis such that we don't overscroll the boundaries when zooming.
   1.837 +    CSSPoint neededDisplacement;
   1.838 +
   1.839 +    CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale;
   1.840 +    CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
   1.841 +    realMinZoom.scale = std::max(realMinZoom.scale,
   1.842 +                                 mFrameMetrics.mCompositionBounds.width / mFrameMetrics.mScrollableRect.width);
   1.843 +    realMinZoom.scale = std::max(realMinZoom.scale,
   1.844 +                                 mFrameMetrics.mCompositionBounds.height / mFrameMetrics.mScrollableRect.height);
   1.845 +    if (realMaxZoom < realMinZoom) {
   1.846 +      realMaxZoom = realMinZoom;
   1.847 +    }
   1.848 +
   1.849 +    bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
   1.850 +                   (spanRatio < 1.0 && userZoom > realMinZoom);
   1.851 +
   1.852 +    if (doScale) {
   1.853 +      spanRatio = clamped(spanRatio,
   1.854 +                          realMinZoom.scale / userZoom.scale,
   1.855 +                          realMaxZoom.scale / userZoom.scale);
   1.856 +
   1.857 +      // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
   1.858 +      // up above we clamped it.
   1.859 +      neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
   1.860 +      neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
   1.861 +
   1.862 +      ScaleWithFocus(spanRatio, cssFocusPoint);
   1.863 +
   1.864 +      if (neededDisplacement != CSSPoint()) {
   1.865 +        ScrollBy(neededDisplacement);
   1.866 +      }
   1.867 +
   1.868 +      ScheduleComposite();
   1.869 +      // We don't want to redraw on every scale, so don't use
   1.870 +      // RequestContentRepaint()
   1.871 +      UpdateSharedCompositorFrameMetrics();
   1.872 +    }
   1.873 +
   1.874 +    mLastZoomFocus = focusPoint;
   1.875 +  }
   1.876 +
   1.877 +  return nsEventStatus_eConsumeNoDefault;
   1.878 +}
   1.879 +
   1.880 +nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
   1.881 +  APZC_LOG("%p got a scale-end in state %d\n", this, mState);
   1.882 +
   1.883 +  SetState(NOTHING);
   1.884 +
   1.885 +  {
   1.886 +    ReentrantMonitorAutoEnter lock(mMonitor);
   1.887 +    ScheduleComposite();
   1.888 +    RequestContentRepaint();
   1.889 +    UpdateSharedCompositorFrameMetrics();
   1.890 +  }
   1.891 +
   1.892 +  return nsEventStatus_eConsumeNoDefault;
   1.893 +}
   1.894 +
   1.895 +bool
   1.896 +AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut)
   1.897 +{
   1.898 +  APZCTreeManager* treeManagerLocal = mTreeManager;
   1.899 +  if (treeManagerLocal) {
   1.900 +    gfx3DMatrix transformToApzc;
   1.901 +    gfx3DMatrix transformToGecko;
   1.902 +    treeManagerLocal->GetInputTransforms(this, transformToApzc, transformToGecko);
   1.903 +    gfxPoint result = transformToGecko.Transform(gfxPoint(aPoint.x, aPoint.y));
   1.904 +    // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
   1.905 +    // for this coordinate space and it maps the closest to LayoutDevicePoint.
   1.906 +    LayoutDevicePoint layoutPoint = LayoutDevicePoint(result.x, result.y);
   1.907 +    { // scoped lock to access mFrameMetrics
   1.908 +      ReentrantMonitorAutoEnter lock(mMonitor);
   1.909 +      *aOut = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
   1.910 +    }
   1.911 +    return true;
   1.912 +  }
   1.913 +  return false;
   1.914 +}
   1.915 +
   1.916 +nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   1.917 +  APZC_LOG("%p got a long-press in state %d\n", this, mState);
   1.918 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   1.919 +  if (controller) {
   1.920 +    int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   1.921 +    CSSPoint geckoScreenPoint;
   1.922 +    if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   1.923 +      SetState(WAITING_CONTENT_RESPONSE);
   1.924 +      SetContentResponseTimer();
   1.925 +      controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
   1.926 +      return nsEventStatus_eConsumeNoDefault;
   1.927 +    }
   1.928 +  }
   1.929 +  return nsEventStatus_eIgnore;
   1.930 +}
   1.931 +
   1.932 +nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
   1.933 +  APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
   1.934 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   1.935 +  if (controller) {
   1.936 +    int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   1.937 +    CSSPoint geckoScreenPoint;
   1.938 +    if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   1.939 +      controller->HandleLongTapUp(geckoScreenPoint, modifiers, GetGuid());
   1.940 +      return nsEventStatus_eConsumeNoDefault;
   1.941 +    }
   1.942 +  }
   1.943 +  return nsEventStatus_eIgnore;
   1.944 +}
   1.945 +
   1.946 +nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   1.947 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   1.948 +  if (controller) {
   1.949 +    CSSPoint geckoScreenPoint;
   1.950 +    if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
   1.951 +      int32_t modifiers = WidgetModifiersToDOMModifiers(aModifiers);
   1.952 +      // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
   1.953 +      // calling controller->HandleSingleTap directly might mean that content receives
   1.954 +      // the single tap message before the corresponding touch-up. To avoid that we
   1.955 +      // schedule the singletap message to run on the next spin of the event loop.
   1.956 +      // See bug 965381 for the issue this was causing.
   1.957 +      controller->PostDelayedTask(
   1.958 +        NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
   1.959 +                          geckoScreenPoint, modifiers, GetGuid()),
   1.960 +        0);
   1.961 +      mTouchBlockState.mSingleTapOccurred = true;
   1.962 +      return nsEventStatus_eConsumeNoDefault;
   1.963 +    }
   1.964 +  }
   1.965 +  return nsEventStatus_eIgnore;
   1.966 +}
   1.967 +
   1.968 +void AsyncPanZoomController::OnTouchEndOrCancel() {
   1.969 +  if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
   1.970 +    controller->NotifyAPZStateChange(
   1.971 +        GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
   1.972 +  }
   1.973 +}
   1.974 +
   1.975 +nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   1.976 +  APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   1.977 +  // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   1.978 +  // sending event to content
   1.979 +  if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
   1.980 +    return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   1.981 +  }
   1.982 +  return nsEventStatus_eIgnore;
   1.983 +}
   1.984 +
   1.985 +nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   1.986 +  APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   1.987 +  return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   1.988 +}
   1.989 +
   1.990 +nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   1.991 +  APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   1.992 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   1.993 +  if (controller) {
   1.994 +    if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
   1.995 +      int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   1.996 +      CSSPoint geckoScreenPoint;
   1.997 +      if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   1.998 +        controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
   1.999 +      }
  1.1000 +    }
  1.1001 +    return nsEventStatus_eConsumeNoDefault;
  1.1002 +  }
  1.1003 +  return nsEventStatus_eIgnore;
  1.1004 +}
  1.1005 +
  1.1006 +nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
  1.1007 +  APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
  1.1008 +  // XXX: Implement this.
  1.1009 +  return nsEventStatus_eIgnore;
  1.1010 +}
  1.1011 +
  1.1012 +float AsyncPanZoomController::PanDistance() {
  1.1013 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1014 +  return NS_hypot(mX.PanDistance(), mY.PanDistance());
  1.1015 +}
  1.1016 +
  1.1017 +const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
  1.1018 +  return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
  1.1019 +}
  1.1020 +
  1.1021 +void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBehaviorFlags aBehavior) {
  1.1022 +  // Handling of cross sliding will need to be added in this method after touch-action released
  1.1023 +  // enabled by default.
  1.1024 +  if ((aBehavior & AllowedTouchBehavior::VERTICAL_PAN) && (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN)) {
  1.1025 +    if (mX.Scrollable() && mY.Scrollable()) {
  1.1026 +      if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
  1.1027 +        mY.SetAxisLocked(true);
  1.1028 +        SetState(PANNING_LOCKED_X);
  1.1029 +      } else if (IsCloseToVertical(aAngle, AXIS_LOCK_ANGLE)) {
  1.1030 +        mX.SetAxisLocked(true);
  1.1031 +        SetState(PANNING_LOCKED_Y);
  1.1032 +      } else {
  1.1033 +        SetState(PANNING);
  1.1034 +      }
  1.1035 +    } else if (mX.Scrollable() || mY.Scrollable()) {
  1.1036 +      SetState(PANNING);
  1.1037 +    } else {
  1.1038 +      SetState(NOTHING);
  1.1039 +    }
  1.1040 +  } else if (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN) {
  1.1041 +    // Using bigger angle for panning to keep behavior consistent
  1.1042 +    // with IE.
  1.1043 +    if (IsCloseToHorizontal(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
  1.1044 +      mY.SetAxisLocked(true);
  1.1045 +      SetState(PANNING_LOCKED_X);
  1.1046 +      mPanDirRestricted = true;
  1.1047 +    } else {
  1.1048 +      // Don't treat these touches as pan/zoom movements since 'touch-action' value
  1.1049 +      // requires it.
  1.1050 +      SetState(NOTHING);
  1.1051 +    }
  1.1052 +  } else if (aBehavior & AllowedTouchBehavior::VERTICAL_PAN) {
  1.1053 +    if (IsCloseToVertical(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
  1.1054 +      mX.SetAxisLocked(true);
  1.1055 +      SetState(PANNING_LOCKED_Y);
  1.1056 +      mPanDirRestricted = true;
  1.1057 +    } else {
  1.1058 +      SetState(NOTHING);
  1.1059 +    }
  1.1060 +  } else {
  1.1061 +    SetState(NOTHING);
  1.1062 +  }
  1.1063 +}
  1.1064 +
  1.1065 +void AsyncPanZoomController::HandlePanning(double aAngle) {
  1.1066 +  if (!gfxPrefs::APZCrossSlideEnabled() && (!mX.Scrollable() || !mY.Scrollable())) {
  1.1067 +    SetState(PANNING);
  1.1068 +  } else if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
  1.1069 +    mY.SetAxisLocked(true);
  1.1070 +    if (mX.Scrollable()) {
  1.1071 +      SetState(PANNING_LOCKED_X);
  1.1072 +    } else {
  1.1073 +      SetState(CROSS_SLIDING_X);
  1.1074 +      mX.SetAxisLocked(true);
  1.1075 +    }
  1.1076 +  } else if (IsCloseToVertical(aAngle, AXIS_LOCK_ANGLE)) {
  1.1077 +    mX.SetAxisLocked(true);
  1.1078 +    if (mY.Scrollable()) {
  1.1079 +      SetState(PANNING_LOCKED_Y);
  1.1080 +    } else {
  1.1081 +      SetState(CROSS_SLIDING_Y);
  1.1082 +      mY.SetAxisLocked(true);
  1.1083 +    }
  1.1084 +  } else {
  1.1085 +    SetState(PANNING);
  1.1086 +  }
  1.1087 +}
  1.1088 +
  1.1089 +nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
  1.1090 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1091 +
  1.1092 +  ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1.1093 +  float dx = mX.PanDistance(point.x);
  1.1094 +  float dy = mY.PanDistance(point.y);
  1.1095 +
  1.1096 +  // When the touch move breaks through the pan threshold, reposition the touch down origin
  1.1097 +  // so the page won't jump when we start panning.
  1.1098 +  mX.StartTouch(point.x);
  1.1099 +  mY.StartTouch(point.y);
  1.1100 +  mLastEventTime = aEvent.mTime;
  1.1101 +
  1.1102 +  double angle = atan2(dy, dx); // range [-pi, pi]
  1.1103 +  angle = fabs(angle); // range [0, pi]
  1.1104 +
  1.1105 +  if (mTouchActionPropertyEnabled) {
  1.1106 +    HandlePanningWithTouchAction(angle, GetTouchBehavior(0));
  1.1107 +  } else {
  1.1108 +    if (GetAxisLockMode() == FREE) {
  1.1109 +      SetState(PANNING);
  1.1110 +    } else {
  1.1111 +      HandlePanning(angle);
  1.1112 +    }
  1.1113 +  }
  1.1114 +
  1.1115 +  if (IsPanningState(mState)) {
  1.1116 +    if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1.1117 +      controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
  1.1118 +    }
  1.1119 +    return nsEventStatus_eConsumeNoDefault;
  1.1120 +  }
  1.1121 +  // Don't consume an event that didn't trigger a panning.
  1.1122 +  return nsEventStatus_eIgnore;
  1.1123 +}
  1.1124 +
  1.1125 +void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
  1.1126 +  ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1.1127 +  TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
  1.1128 +
  1.1129 +  // Probably a duplicate event, just throw it away.
  1.1130 +  if (timeDelta.ToMilliseconds() <= EPSILON) {
  1.1131 +    return;
  1.1132 +  }
  1.1133 +
  1.1134 +  mX.UpdateWithTouchAtDevicePoint(point.x, timeDelta);
  1.1135 +  mY.UpdateWithTouchAtDevicePoint(point.y, timeDelta);
  1.1136 +}
  1.1137 +
  1.1138 +void AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
  1.1139 +                                           const ScreenPoint& aEndPoint,
  1.1140 +                                           uint32_t aOverscrollHandoffChainIndex) {
  1.1141 +
  1.1142 +  // "start - end" rather than "end - start" because e.g. moving your finger
  1.1143 +  // down (*positive* direction along y axis) causes the vertical scroll offset
  1.1144 +  // to *decrease* as the page follows your finger.
  1.1145 +  ScreenPoint displacement = aStartPoint - aEndPoint;
  1.1146 +
  1.1147 +  ScreenPoint overscroll;  // will be used outside monitor block
  1.1148 +  {
  1.1149 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.1150 +
  1.1151 +    CSSToScreenScale zoom = mFrameMetrics.GetZoom();
  1.1152 +
  1.1153 +    // Inversely scale the offset by the resolution (when you're zoomed further in,
  1.1154 +    // the same swipe should move you a shorter distance).
  1.1155 +    CSSPoint cssDisplacement = displacement / zoom;
  1.1156 +
  1.1157 +    CSSPoint cssOverscroll;
  1.1158 +    CSSPoint allowedDisplacement(mX.AdjustDisplacement(cssDisplacement.x,
  1.1159 +                                                       cssOverscroll.x),
  1.1160 +                                 mY.AdjustDisplacement(cssDisplacement.y,
  1.1161 +                                                       cssOverscroll.y));
  1.1162 +    overscroll = cssOverscroll * zoom;
  1.1163 +
  1.1164 +    if (!IsZero(allowedDisplacement)) {
  1.1165 +      ScrollBy(allowedDisplacement);
  1.1166 +      ScheduleComposite();
  1.1167 +
  1.1168 +      TimeDuration timePaintDelta = mPaintThrottler.TimeSinceLastRequest(GetFrameTime());
  1.1169 +      if (timePaintDelta.ToMilliseconds() > gfxPrefs::APZPanRepaintInterval()) {
  1.1170 +        RequestContentRepaint();
  1.1171 +      }
  1.1172 +      UpdateSharedCompositorFrameMetrics();
  1.1173 +    }
  1.1174 +  }
  1.1175 +
  1.1176 +  if (!IsZero(overscroll)) {
  1.1177 +    // "+ overscroll" rather than "- overscroll" because "overscroll" is what's
  1.1178 +    // left of "displacement", and "displacement" is "start - end".
  1.1179 +    CallDispatchScroll(aEndPoint + overscroll, aEndPoint, aOverscrollHandoffChainIndex + 1);
  1.1180 +  }
  1.1181 +}
  1.1182 +
  1.1183 +void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
  1.1184 +  // We may have a pre-existing velocity for whatever reason (for example,
  1.1185 +  // a previously handed off fling). We don't want to clobber that.
  1.1186 +  mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
  1.1187 +  mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
  1.1188 +  SetState(FLING);
  1.1189 +  StartAnimation(new FlingAnimation(*this));
  1.1190 +}
  1.1191 +
  1.1192 +void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
  1.1193 +                                                uint32_t aOverscrollHandoffChainIndex) {
  1.1194 +  // Make a local copy of the tree manager pointer and check if it's not
  1.1195 +  // null before calling DispatchScroll(). This is necessary because
  1.1196 +  // Destroy(), which nulls out mTreeManager, could be called concurrently.
  1.1197 +  APZCTreeManager* treeManagerLocal = mTreeManager;
  1.1198 +  if (treeManagerLocal) {
  1.1199 +    treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
  1.1200 +                                     aOverscrollHandoffChainIndex);
  1.1201 +  }
  1.1202 +}
  1.1203 +
  1.1204 +void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
  1.1205 +  ScreenIntPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
  1.1206 +  ScreenIntPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
  1.1207 +  TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
  1.1208 +
  1.1209 +  // Probably a duplicate event, just throw it away.
  1.1210 +  if (timeDelta.ToMilliseconds() <= EPSILON) {
  1.1211 +    return;
  1.1212 +  }
  1.1213 +
  1.1214 +  // If we're axis-locked, check if the user is trying to break the lock
  1.1215 +  if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
  1.1216 +    ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1.1217 +    float dx = mX.PanDistance(point.x);
  1.1218 +    float dy = mY.PanDistance(point.y);
  1.1219 +
  1.1220 +    double angle = atan2(dy, dx); // range [-pi, pi]
  1.1221 +    angle = fabs(angle); // range [0, pi]
  1.1222 +
  1.1223 +    float breakThreshold = AXIS_BREAKOUT_THRESHOLD * APZCTreeManager::GetDPI();
  1.1224 +
  1.1225 +    if (fabs(dx) > breakThreshold || fabs(dy) > breakThreshold) {
  1.1226 +      if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
  1.1227 +        if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
  1.1228 +          mY.SetAxisLocked(false);
  1.1229 +          SetState(PANNING);
  1.1230 +        }
  1.1231 +      } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
  1.1232 +        if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
  1.1233 +          mX.SetAxisLocked(false);
  1.1234 +          SetState(PANNING);
  1.1235 +        }
  1.1236 +      }
  1.1237 +    }
  1.1238 +  }
  1.1239 +
  1.1240 +  UpdateWithTouchAtDevicePoint(aEvent);
  1.1241 +
  1.1242 +  CallDispatchScroll(prevTouchPoint, touchPoint, 0);
  1.1243 +}
  1.1244 +
  1.1245 +ScreenIntPoint& AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
  1.1246 +  return ((SingleTouchData&)aEvent.mTouches[0]).mScreenPoint;
  1.1247 +}
  1.1248 +
  1.1249 +bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
  1.1250 +                            const TimeDuration& aDelta) {
  1.1251 +
  1.1252 +  // If the fling is handed off to our APZC from a child, on the first call to
  1.1253 +  // Sample() aDelta might be negative because it's computed as the sample time
  1.1254 +  // from SampleContentTransformForFrame() minus our APZC's mLastSampleTime
  1.1255 +  // which is the time the child handed off the fling from its call to
  1.1256 +  // SampleContentTransformForFrame() with the same sample time. If we allow
  1.1257 +  // the negative aDelta to be processed, it will yield a displacement in the
  1.1258 +  // direction opposite to the fling, which can cause us to overscroll and
  1.1259 +  // hand off the fling to _our_ parent, which effectively kills the fling.
  1.1260 +  if (aDelta.ToMilliseconds() <= 0) {
  1.1261 +    return true;
  1.1262 +  }
  1.1263 +
  1.1264 +  bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta),
  1.1265 +       shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta);
  1.1266 +  // If we shouldn't continue the fling, let's just stop and repaint.
  1.1267 +  if (!shouldContinueFlingX && !shouldContinueFlingY) {
  1.1268 +    return false;
  1.1269 +  }
  1.1270 +
  1.1271 +  // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
  1.1272 +  // Since we need to hand off the velocity to the tree manager in such a case,
  1.1273 +  // we save it here. Would be ScreenVector instead of ScreenPoint if we had
  1.1274 +  // vector classes.
  1.1275 +  ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
  1.1276 +
  1.1277 +  ScreenPoint offset = velocity * aDelta.ToMilliseconds();
  1.1278 +
  1.1279 +  // Inversely scale the offset by the resolution (when you're zoomed further in,
  1.1280 +  // the same swipe should move you a shorter distance).
  1.1281 +  CSSPoint cssOffset = offset / aFrameMetrics.GetZoom();
  1.1282 +  CSSPoint overscroll;
  1.1283 +  aFrameMetrics.ScrollBy(CSSPoint(
  1.1284 +    mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
  1.1285 +    mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
  1.1286 +  ));
  1.1287 +
  1.1288 +  // If the fling has caused us to reach the end of our scroll range, hand
  1.1289 +  // off the fling to the next APZC in the overscroll handoff chain.
  1.1290 +  if (!IsZero(overscroll)) {
  1.1291 +    // We may have reached the end of the scroll range along one axis but
  1.1292 +    // not the other. In such a case we only want to hand off the relevant
  1.1293 +    // component of the fling.
  1.1294 +    if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
  1.1295 +      velocity.x = 0;
  1.1296 +    } else if (FuzzyEqualsMultiplicative(overscroll.y, 0.0f)) {
  1.1297 +      velocity.y = 0;
  1.1298 +    }
  1.1299 +
  1.1300 +    // To hand off the fling, we call APZCTreeManager::HandleFlingOverscroll()
  1.1301 +    // which starts a new fling in the next APZC in the handoff chain with
  1.1302 +    // the same velocity. For simplicity, the actual overscroll of the current
  1.1303 +    // sample is discarded rather than being handed off. The compositor should
  1.1304 +    // sample animations sufficiently frequently that this is not noticeable.
  1.1305 +
  1.1306 +    // Make a local copy of the tree manager pointer and check if it's not
  1.1307 +    // null before calling HandleFlingOverscroll(). This is necessary because
  1.1308 +    // Destroy(), which nulls out mTreeManager, could be called concurrently.
  1.1309 +    APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
  1.1310 +    if (treeManagerLocal) {
  1.1311 +      // APZC is holding mMonitor, so directly calling HandleFlingOverscroll()
  1.1312 +      // (which acquires the tree lock) would violate the lock ordering. Instead
  1.1313 +      // we schedule HandleFlingOverscroll() to be called after mMonitor is
  1.1314 +      // released.
  1.1315 +      mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
  1.1316 +                                              &APZCTreeManager::HandOffFling,
  1.1317 +                                              &mApzc,
  1.1318 +                                              velocity));
  1.1319 +    }
  1.1320 +  }
  1.1321 +
  1.1322 +  return true;
  1.1323 +}
  1.1324 +
  1.1325 +void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
  1.1326 +{
  1.1327 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1328 +  mAnimation = aAnimation;
  1.1329 +  mLastSampleTime = GetFrameTime();
  1.1330 +  ScheduleComposite();
  1.1331 +}
  1.1332 +
  1.1333 +void AsyncPanZoomController::CancelAnimation() {
  1.1334 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1335 +  SetState(NOTHING);
  1.1336 +  mAnimation = nullptr;
  1.1337 +}
  1.1338 +
  1.1339 +void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
  1.1340 +  mCompositorParent = aCompositorParent;
  1.1341 +}
  1.1342 +
  1.1343 +void AsyncPanZoomController::SetCrossProcessCompositorParent(PCompositorParent* aCrossProcessCompositorParent) {
  1.1344 +  mCrossProcessCompositorParent = aCrossProcessCompositorParent;
  1.1345 +}
  1.1346 +
  1.1347 +void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
  1.1348 +  mFrameMetrics.ScrollBy(aOffset);
  1.1349 +}
  1.1350 +
  1.1351 +void AsyncPanZoomController::ScaleWithFocus(float aScale,
  1.1352 +                                            const CSSPoint& aFocus) {
  1.1353 +  mFrameMetrics.ZoomBy(aScale);
  1.1354 +  // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
  1.1355 +  // at the same position on the screen before and after the change in zoom. The below code
  1.1356 +  // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
  1.1357 +  // in-depth explanation of how.
  1.1358 +  mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
  1.1359 +}
  1.1360 +
  1.1361 +/**
  1.1362 + * Enlarges the displayport along both axes based on the velocity.
  1.1363 + */
  1.1364 +static CSSSize
  1.1365 +CalculateDisplayPortSize(const CSSSize& aCompositionSize,
  1.1366 +                         const CSSPoint& aVelocity)
  1.1367 +{
  1.1368 +  float xMultiplier = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed()
  1.1369 +                        ? gfxPrefs::APZXStationarySizeMultiplier()
  1.1370 +                        : gfxPrefs::APZXSkateSizeMultiplier();
  1.1371 +  float yMultiplier = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed()
  1.1372 +                        ? gfxPrefs::APZYStationarySizeMultiplier()
  1.1373 +                        : gfxPrefs::APZYSkateSizeMultiplier();
  1.1374 +  return CSSSize(aCompositionSize.width * xMultiplier,
  1.1375 +                 aCompositionSize.height * yMultiplier);
  1.1376 +}
  1.1377 +
  1.1378 +/**
  1.1379 + * Attempts to redistribute any area in the displayport that would get clipped
  1.1380 + * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
  1.1381 + * other axis, while maintaining total displayport area.
  1.1382 + */
  1.1383 +static void
  1.1384 +RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
  1.1385 +                              const CSSRect& aScrollableRect)
  1.1386 +{
  1.1387 +  float xSlack = std::max(0.0f, aDisplayPortSize.width - aScrollableRect.width);
  1.1388 +  float ySlack = std::max(0.0f, aDisplayPortSize.height - aScrollableRect.height);
  1.1389 +
  1.1390 +  if (ySlack > 0) {
  1.1391 +    // Reassign wasted y-axis displayport to the x-axis
  1.1392 +    aDisplayPortSize.height -= ySlack;
  1.1393 +    float xExtra = ySlack * aDisplayPortSize.width / aDisplayPortSize.height;
  1.1394 +    aDisplayPortSize.width += xExtra;
  1.1395 +  } else if (xSlack > 0) {
  1.1396 +    // Reassign wasted x-axis displayport to the y-axis
  1.1397 +    aDisplayPortSize.width -= xSlack;
  1.1398 +    float yExtra = xSlack * aDisplayPortSize.height / aDisplayPortSize.width;
  1.1399 +    aDisplayPortSize.height += yExtra;
  1.1400 +  }
  1.1401 +}
  1.1402 +
  1.1403 +/* static */
  1.1404 +const LayerMargin AsyncPanZoomController::CalculatePendingDisplayPort(
  1.1405 +  const FrameMetrics& aFrameMetrics,
  1.1406 +  const ScreenPoint& aVelocity,
  1.1407 +  double aEstimatedPaintDuration)
  1.1408 +{
  1.1409 +  CSSSize compositionBounds = aFrameMetrics.CalculateCompositedSizeInCssPixels();
  1.1410 +  CSSSize compositionSize = aFrameMetrics.GetRootCompositionSize();
  1.1411 +  compositionSize =
  1.1412 +    CSSSize(std::min(compositionBounds.width, compositionSize.width),
  1.1413 +            std::min(compositionBounds.height, compositionSize.height));
  1.1414 +  CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
  1.1415 +  CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
  1.1416 +  CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
  1.1417 +
  1.1418 +  // Calculate the displayport size based on how fast we're moving along each axis.
  1.1419 +  CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
  1.1420 +
  1.1421 +  if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
  1.1422 +    RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
  1.1423 +  }
  1.1424 +
  1.1425 +  // Offset the displayport, depending on how fast we're moving and the
  1.1426 +  // estimated time it takes to paint, to try to minimise checkerboarding.
  1.1427 +  float estimatedPaintDurationMillis = (float)(aEstimatedPaintDuration * 1000.0);
  1.1428 +  float paintFactor = (gfxPrefs::APZUsePaintDuration() ? estimatedPaintDurationMillis : 50.0f);
  1.1429 +  CSSRect displayPort = CSSRect(scrollOffset + (velocity * paintFactor * gfxPrefs::APZVelocityBias()),
  1.1430 +                                displayPortSize);
  1.1431 +
  1.1432 +  // Re-center the displayport based on its expansion over the composition size.
  1.1433 +  displayPort.MoveBy((compositionSize.width - displayPort.width)/2.0f,
  1.1434 +                     (compositionSize.height - displayPort.height)/2.0f);
  1.1435 +
  1.1436 +  // Make sure the displayport remains within the scrollable rect.
  1.1437 +  displayPort = displayPort.ForceInside(scrollableRect) - scrollOffset;
  1.1438 +
  1.1439 +  APZC_LOG_FM(aFrameMetrics,
  1.1440 +    "Calculated displayport as (%f %f %f %f) from velocity (%f %f) paint time %f metrics",
  1.1441 +    displayPort.x, displayPort.y, displayPort.width, displayPort.height,
  1.1442 +    aVelocity.x, aVelocity.y, (float)estimatedPaintDurationMillis);
  1.1443 +
  1.1444 +  CSSMargin cssMargins;
  1.1445 +  cssMargins.left = -displayPort.x;
  1.1446 +  cssMargins.top = -displayPort.y;
  1.1447 +  cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
  1.1448 +  cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
  1.1449 +
  1.1450 +  LayerMargin layerMargins = cssMargins * aFrameMetrics.LayersPixelsPerCSSPixel();
  1.1451 +
  1.1452 +  return layerMargins;
  1.1453 +}
  1.1454 +
  1.1455 +void AsyncPanZoomController::ScheduleComposite() {
  1.1456 +  if (mCompositorParent) {
  1.1457 +    mCompositorParent->ScheduleRenderOnCompositorThread();
  1.1458 +  }
  1.1459 +}
  1.1460 +
  1.1461 +void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
  1.1462 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1463 +  RequestContentRepaint();
  1.1464 +  UpdateSharedCompositorFrameMetrics();
  1.1465 +}
  1.1466 +
  1.1467 +bool AsyncPanZoomController::IsPannable() const {
  1.1468 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1469 +  return mX.HasRoomToPan() || mY.HasRoomToPan();
  1.1470 +}
  1.1471 +
  1.1472 +void AsyncPanZoomController::RequestContentRepaint() {
  1.1473 +  RequestContentRepaint(mFrameMetrics);
  1.1474 +}
  1.1475 +
  1.1476 +void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
  1.1477 +  aFrameMetrics.SetDisplayPortMargins(
  1.1478 +    CalculatePendingDisplayPort(aFrameMetrics,
  1.1479 +                                GetVelocityVector(),
  1.1480 +                                mPaintThrottler.AverageDuration().ToSeconds()));
  1.1481 +  aFrameMetrics.SetUseDisplayPortMargins();
  1.1482 +
  1.1483 +  // If we're trying to paint what we already think is painted, discard this
  1.1484 +  // request since it's a pointless paint.
  1.1485 +  LayerMargin marginDelta = mLastPaintRequestMetrics.GetDisplayPortMargins()
  1.1486 +                          - aFrameMetrics.GetDisplayPortMargins();
  1.1487 +  if (fabsf(marginDelta.left) < EPSILON &&
  1.1488 +      fabsf(marginDelta.top) < EPSILON &&
  1.1489 +      fabsf(marginDelta.right) < EPSILON &&
  1.1490 +      fabsf(marginDelta.bottom) < EPSILON &&
  1.1491 +      fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
  1.1492 +            aFrameMetrics.GetScrollOffset().x) < EPSILON &&
  1.1493 +      fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
  1.1494 +            aFrameMetrics.GetScrollOffset().y) < EPSILON &&
  1.1495 +      aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
  1.1496 +      fabsf(aFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON &&
  1.1497 +      fabsf(aFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) {
  1.1498 +    return;
  1.1499 +  }
  1.1500 +
  1.1501 +  SendAsyncScrollEvent();
  1.1502 +  mPaintThrottler.PostTask(
  1.1503 +    FROM_HERE,
  1.1504 +    NewRunnableMethod(this,
  1.1505 +                      &AsyncPanZoomController::DispatchRepaintRequest,
  1.1506 +                      aFrameMetrics),
  1.1507 +    GetFrameTime());
  1.1508 +
  1.1509 +  aFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
  1.1510 +  mLastPaintRequestMetrics = aFrameMetrics;
  1.1511 +}
  1.1512 +
  1.1513 +/*static*/ CSSRect
  1.1514 +GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
  1.1515 +{
  1.1516 +  // This computation is based on what happens in CalculatePendingDisplayPort. If that
  1.1517 +  // changes then this might need to change too
  1.1518 +  CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
  1.1519 +                   CSSSize(std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().width,
  1.1520 +                                    aFrameMetrics.GetRootCompositionSize().width),
  1.1521 +                           std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().height,
  1.1522 +                                    aFrameMetrics.GetRootCompositionSize().height)));
  1.1523 +  baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.LayersPixelsPerCSSPixel());
  1.1524 +  return baseRect;
  1.1525 +}
  1.1526 +
  1.1527 +void
  1.1528 +AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
  1.1529 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1.1530 +  if (controller) {
  1.1531 +    APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
  1.1532 +    LogRendertraceRect(GetGuid(), "requested displayport", "yellow", GetDisplayPortRect(aFrameMetrics));
  1.1533 +
  1.1534 +    controller->RequestContentRepaint(aFrameMetrics);
  1.1535 +    mLastDispatchedPaintMetrics = aFrameMetrics;
  1.1536 +  }
  1.1537 +}
  1.1538 +
  1.1539 +void
  1.1540 +AsyncPanZoomController::FireAsyncScrollOnTimeout()
  1.1541 +{
  1.1542 +  if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  1.1543 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.1544 +    SendAsyncScrollEvent();
  1.1545 +  }
  1.1546 +  mAsyncScrollTimeoutTask = nullptr;
  1.1547 +}
  1.1548 +
  1.1549 +bool ZoomAnimation::Sample(FrameMetrics& aFrameMetrics,
  1.1550 +                           const TimeDuration& aDelta) {
  1.1551 +  mDuration += aDelta;
  1.1552 +  double animPosition = mDuration / ZOOM_TO_DURATION;
  1.1553 +
  1.1554 +  if (animPosition >= 1.0) {
  1.1555 +    aFrameMetrics.SetZoom(mEndZoom);
  1.1556 +    aFrameMetrics.SetScrollOffset(mEndOffset);
  1.1557 +    return false;
  1.1558 +  }
  1.1559 +
  1.1560 +  // Sample the zoom at the current time point.  The sampled zoom
  1.1561 +  // will affect the final computed resolution.
  1.1562 +  double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
  1.1563 +
  1.1564 +  // We scale the scrollOffset linearly with sampledPosition, so the zoom
  1.1565 +  // needs to scale inversely to match.
  1.1566 +  aFrameMetrics.SetZoom(CSSToScreenScale(1 /
  1.1567 +    (sampledPosition / mEndZoom.scale +
  1.1568 +    (1 - sampledPosition) / mStartZoom.scale)));
  1.1569 +
  1.1570 +  aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
  1.1571 +    mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
  1.1572 +    mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
  1.1573 +  )));
  1.1574 +
  1.1575 +  return true;
  1.1576 +}
  1.1577 +
  1.1578 +bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime)
  1.1579 +{
  1.1580 +  if (mAnimation) {
  1.1581 +    if (mAnimation->Sample(mFrameMetrics, aSampleTime - mLastSampleTime)) {
  1.1582 +      if (mPaintThrottler.TimeSinceLastRequest(aSampleTime) >
  1.1583 +          mAnimation->mRepaintInterval) {
  1.1584 +        RequestContentRepaint();
  1.1585 +      }
  1.1586 +    } else {
  1.1587 +      mAnimation = nullptr;
  1.1588 +      SetState(NOTHING);
  1.1589 +      SendAsyncScrollEvent();
  1.1590 +      RequestContentRepaint();
  1.1591 +    }
  1.1592 +    UpdateSharedCompositorFrameMetrics();
  1.1593 +    mLastSampleTime = aSampleTime;
  1.1594 +    return true;
  1.1595 +  }
  1.1596 +  return false;
  1.1597 +}
  1.1598 +
  1.1599 +bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
  1.1600 +                                                            ViewTransform* aNewTransform,
  1.1601 +                                                            ScreenPoint& aScrollOffset) {
  1.1602 +  // The eventual return value of this function. The compositor needs to know
  1.1603 +  // whether or not to advance by a frame as soon as it can. For example, if a
  1.1604 +  // fling is happening, it has to keep compositing so that the animation is
  1.1605 +  // smooth. If an animation frame is requested, it is the compositor's
  1.1606 +  // responsibility to schedule a composite.
  1.1607 +  bool requestAnimationFrame = false;
  1.1608 +  Vector<Task*> deferredTasks;
  1.1609 +
  1.1610 +  {
  1.1611 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.1612 +
  1.1613 +    requestAnimationFrame = UpdateAnimation(aSampleTime);
  1.1614 +
  1.1615 +    aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
  1.1616 +    *aNewTransform = GetCurrentAsyncTransform();
  1.1617 +
  1.1618 +    LogRendertraceRect(GetGuid(), "viewport", "red",
  1.1619 +      CSSRect(mFrameMetrics.GetScrollOffset(),
  1.1620 +              ParentLayerSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.GetZoomToParent()));
  1.1621 +
  1.1622 +    mCurrentAsyncScrollOffset = mFrameMetrics.GetScrollOffset();
  1.1623 +
  1.1624 +    // Get any deferred tasks queued up by mAnimation's Sample() (called by
  1.1625 +    // UpdateAnimation()). This needs to be done here since mAnimation can
  1.1626 +    // be destroyed by another thread when we release the monitor, but
  1.1627 +    // the tasks need to be executed after we release the monitor since they
  1.1628 +    // are allowed to call APZCTreeManager methods which can grab the tree lock. 
  1.1629 +    if (mAnimation) {
  1.1630 +      deferredTasks = mAnimation->TakeDeferredTasks();
  1.1631 +    }
  1.1632 +  }
  1.1633 +
  1.1634 +  for (uint32_t i = 0; i < deferredTasks.length(); ++i) {
  1.1635 +    deferredTasks[i]->Run();
  1.1636 +    delete deferredTasks[i];
  1.1637 +  }
  1.1638 +
  1.1639 +  // Cancel the mAsyncScrollTimeoutTask because we will fire a
  1.1640 +  // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
  1.1641 +  if (mAsyncScrollTimeoutTask) {
  1.1642 +    mAsyncScrollTimeoutTask->Cancel();
  1.1643 +    mAsyncScrollTimeoutTask = nullptr;
  1.1644 +  }
  1.1645 +  // Fire the mozbrowserasyncscroll event immediately if it's been
  1.1646 +  // sAsyncScrollThrottleTime ms since the last time we fired the event and the
  1.1647 +  // current scroll offset is different than the mLastAsyncScrollOffset we sent
  1.1648 +  // with the last event.
  1.1649 +  // Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
  1.1650 +  TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
  1.1651 +  if (delta.ToMilliseconds() > gfxPrefs::APZAsyncScrollThrottleTime() &&
  1.1652 +      mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  1.1653 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.1654 +    mLastAsyncScrollTime = aSampleTime;
  1.1655 +    mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
  1.1656 +    SendAsyncScrollEvent();
  1.1657 +  }
  1.1658 +  else {
  1.1659 +    mAsyncScrollTimeoutTask =
  1.1660 +      NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
  1.1661 +    MessageLoop::current()->PostDelayedTask(FROM_HERE,
  1.1662 +                                            mAsyncScrollTimeoutTask,
  1.1663 +                                            gfxPrefs::APZAsyncScrollTimeout());
  1.1664 +  }
  1.1665 +
  1.1666 +  return requestAnimationFrame;
  1.1667 +}
  1.1668 +
  1.1669 +ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
  1.1670 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1671 +
  1.1672 +  CSSPoint lastPaintScrollOffset;
  1.1673 +  if (mLastContentPaintMetrics.IsScrollable()) {
  1.1674 +    lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
  1.1675 +  }
  1.1676 +
  1.1677 +  CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
  1.1678 +    mTestAsyncScrollOffset;
  1.1679 +
  1.1680 +  // If checkerboarding has been disallowed, clamp the scroll position to stay
  1.1681 +  // within rendered content.
  1.1682 +  if (!gfxPrefs::APZAllowCheckerboarding() &&
  1.1683 +      !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
  1.1684 +    CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
  1.1685 +    CSSPoint maxScrollOffset = lastPaintScrollOffset +
  1.1686 +      CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedSize.width,
  1.1687 +               mLastContentPaintMetrics.mDisplayPort.YMost() - compositedSize.height);
  1.1688 +    CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
  1.1689 +
  1.1690 +    if (minScrollOffset.x < maxScrollOffset.x) {
  1.1691 +      currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
  1.1692 +    }
  1.1693 +    if (minScrollOffset.y < maxScrollOffset.y) {
  1.1694 +      currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
  1.1695 +    }
  1.1696 +  }
  1.1697 +
  1.1698 +  LayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
  1.1699 +                         * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
  1.1700 +
  1.1701 +  return ViewTransform(-translation,
  1.1702 +                       mFrameMetrics.GetZoom()
  1.1703 +                     / mLastContentPaintMetrics.mDevPixelsPerCSSPixel
  1.1704 +                     / mFrameMetrics.GetParentResolution());
  1.1705 +}
  1.1706 +
  1.1707 +gfx3DMatrix AsyncPanZoomController::GetNontransientAsyncTransform() {
  1.1708 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1709 +  return gfx3DMatrix::ScalingMatrix(mLastContentPaintMetrics.mResolution.scale,
  1.1710 +                                    mLastContentPaintMetrics.mResolution.scale,
  1.1711 +                                    1.0f);
  1.1712 +}
  1.1713 +
  1.1714 +gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
  1.1715 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1716 +  LayerPoint scrollChange = (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
  1.1717 +                          * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
  1.1718 +  float zoomChange = mLastContentPaintMetrics.GetZoom().scale / mLastDispatchedPaintMetrics.GetZoom().scale;
  1.1719 +  return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
  1.1720 +         gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
  1.1721 +}
  1.1722 +
  1.1723 +void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
  1.1724 +  ReentrantMonitorAutoEnter lock(mMonitor);
  1.1725 +
  1.1726 +  mLastContentPaintMetrics = aLayerMetrics;
  1.1727 +  UpdateTransformScale();
  1.1728 +
  1.1729 +  bool isDefault = mFrameMetrics.IsDefault();
  1.1730 +  mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
  1.1731 +  APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
  1.1732 +
  1.1733 +  LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
  1.1734 +  LogRendertraceRect(GetGuid(), "painted displayport", "green",
  1.1735 +    aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
  1.1736 +
  1.1737 +  mPaintThrottler.TaskComplete(GetFrameTime());
  1.1738 +  bool needContentRepaint = false;
  1.1739 +  if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
  1.1740 +      aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
  1.1741 +    // Remote content has sync'd up to the composition geometry
  1.1742 +    // change, so we can accept the viewport it's calculated.
  1.1743 +    if (mFrameMetrics.mViewport.width != aLayerMetrics.mViewport.width ||
  1.1744 +        mFrameMetrics.mViewport.height != aLayerMetrics.mViewport.height) {
  1.1745 +      needContentRepaint = true;
  1.1746 +    }
  1.1747 +    mFrameMetrics.mViewport = aLayerMetrics.mViewport;
  1.1748 +  }
  1.1749 +
  1.1750 +  // If the layers update was not triggered by our own repaint request, then
  1.1751 +  // we want to take the new scroll offset. Check the scroll generation as well
  1.1752 +  // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
  1.1753 +  // update message.
  1.1754 +  bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
  1.1755 +        && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
  1.1756 +
  1.1757 +  if (aIsFirstPaint || isDefault) {
  1.1758 +    // Initialize our internal state to something sane when the content
  1.1759 +    // that was just painted is something we knew nothing about previously
  1.1760 +    mPaintThrottler.ClearHistory();
  1.1761 +    mPaintThrottler.SetMaxDurations(gfxPrefs::APZNumPaintDurationSamples());
  1.1762 +
  1.1763 +    mX.CancelTouch();
  1.1764 +    mY.CancelTouch();
  1.1765 +    SetState(NOTHING);
  1.1766 +
  1.1767 +    mFrameMetrics = aLayerMetrics;
  1.1768 +    mLastDispatchedPaintMetrics = aLayerMetrics;
  1.1769 +    ShareCompositorFrameMetrics();
  1.1770 +  } else {
  1.1771 +    // If we're not taking the aLayerMetrics wholesale we still need to pull
  1.1772 +    // in some things into our local mFrameMetrics because these things are
  1.1773 +    // determined by Gecko and our copy in mFrameMetrics may be stale.
  1.1774 +
  1.1775 +    if (mFrameMetrics.mCompositionBounds.width == aLayerMetrics.mCompositionBounds.width &&
  1.1776 +        mFrameMetrics.mDevPixelsPerCSSPixel == aLayerMetrics.mDevPixelsPerCSSPixel) {
  1.1777 +      float parentResolutionChange = aLayerMetrics.GetParentResolution().scale
  1.1778 +                                   / mFrameMetrics.GetParentResolution().scale;
  1.1779 +      mFrameMetrics.ZoomBy(parentResolutionChange);
  1.1780 +    } else {
  1.1781 +      // Take the new zoom as either device scale or composition width or both
  1.1782 +      // got changed (e.g. due to orientation change).
  1.1783 +      mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
  1.1784 +      mFrameMetrics.mDevPixelsPerCSSPixel.scale = aLayerMetrics.mDevPixelsPerCSSPixel.scale;
  1.1785 +    }
  1.1786 +    mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
  1.1787 +    mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
  1.1788 +    mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
  1.1789 +    mFrameMetrics.mResolution = aLayerMetrics.mResolution;
  1.1790 +    mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
  1.1791 +    mFrameMetrics.mHasScrollgrab = aLayerMetrics.mHasScrollgrab;
  1.1792 +
  1.1793 +    if (scrollOffsetUpdated) {
  1.1794 +      APZC_LOG("%p updating scroll offset from (%f, %f) to (%f, %f)\n", this,
  1.1795 +        mFrameMetrics.GetScrollOffset().x, mFrameMetrics.GetScrollOffset().y,
  1.1796 +        aLayerMetrics.GetScrollOffset().x, aLayerMetrics.GetScrollOffset().y);
  1.1797 +
  1.1798 +      mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
  1.1799 +
  1.1800 +      // Because of the scroll offset update, any inflight paint requests are
  1.1801 +      // going to be ignored by layout, and so mLastDispatchedPaintMetrics
  1.1802 +      // becomes incorrect for the purposes of calculating the LD transform. To
  1.1803 +      // correct this we need to update mLastDispatchedPaintMetrics to be the
  1.1804 +      // last thing we know was painted by Gecko.
  1.1805 +      mLastDispatchedPaintMetrics = aLayerMetrics;
  1.1806 +    }
  1.1807 +  }
  1.1808 +
  1.1809 +  if (scrollOffsetUpdated) {
  1.1810 +    // Once layout issues a scroll offset update, it becomes impervious to
  1.1811 +    // scroll offset updates from APZ until we acknowledge the update it sent.
  1.1812 +    // This prevents APZ updates from clobbering scroll updates from other
  1.1813 +    // more "legitimate" sources like content scripts.
  1.1814 +    nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1.1815 +    if (controller) {
  1.1816 +      APZC_LOG("%p sending scroll update acknowledgement with gen %lu\n", this, aLayerMetrics.GetScrollGeneration());
  1.1817 +      controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
  1.1818 +                                          aLayerMetrics.GetScrollGeneration());
  1.1819 +    }
  1.1820 +  }
  1.1821 +
  1.1822 +  if (needContentRepaint) {
  1.1823 +    RequestContentRepaint();
  1.1824 +  }
  1.1825 +  UpdateSharedCompositorFrameMetrics();
  1.1826 +}
  1.1827 +
  1.1828 +const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
  1.1829 +  mMonitor.AssertCurrentThreadIn();
  1.1830 +  return mFrameMetrics;
  1.1831 +}
  1.1832 +
  1.1833 +void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
  1.1834 +  if (!aRect.IsFinite()) {
  1.1835 +    NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...\n");
  1.1836 +    return;
  1.1837 +  }
  1.1838 +
  1.1839 +  SetState(ANIMATING_ZOOM);
  1.1840 +
  1.1841 +  {
  1.1842 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.1843 +
  1.1844 +    ParentLayerIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
  1.1845 +    CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
  1.1846 +    CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
  1.1847 +    CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoomToParent();
  1.1848 +    CSSToParentLayerScale targetZoom;
  1.1849 +
  1.1850 +    // The minimum zoom to prevent over-zoom-out.
  1.1851 +    // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
  1.1852 +    // then the CSS content rect, in layers pixels, will be smaller than the
  1.1853 +    // composition bounds. If this happens, we can't fill the target composited
  1.1854 +    // area with this frame.
  1.1855 +    CSSToParentLayerScale localMinZoom(std::max((mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale).scale,
  1.1856 +                                       std::max(compositionBounds.width / cssPageRect.width,
  1.1857 +                                                compositionBounds.height / cssPageRect.height)));
  1.1858 +    CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
  1.1859 +
  1.1860 +    if (!aRect.IsEmpty()) {
  1.1861 +      // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
  1.1862 +      aRect = aRect.Intersect(cssPageRect);
  1.1863 +      targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  1.1864 +                                                  compositionBounds.height / aRect.height));
  1.1865 +    }
  1.1866 +    // 1. If the rect is empty, request received from browserElementScrolling.js
  1.1867 +    // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
  1.1868 +    // 3. currentZoom is equal to localMinZoom and user still double-tapping it
  1.1869 +    // Treat these three cases as a request to zoom out as much as possible.
  1.1870 +    if (aRect.IsEmpty() ||
  1.1871 +        (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
  1.1872 +        (currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
  1.1873 +      CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
  1.1874 +      float y = scrollOffset.y;
  1.1875 +      float newHeight =
  1.1876 +        cssPageRect.width * (compositedSize.height / compositedSize.width);
  1.1877 +      float dh = compositedSize.height - newHeight;
  1.1878 +
  1.1879 +      aRect = CSSRect(0.0f,
  1.1880 +                      y + dh/2,
  1.1881 +                      cssPageRect.width,
  1.1882 +                      newHeight);
  1.1883 +      aRect = aRect.Intersect(cssPageRect);
  1.1884 +      targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  1.1885 +                                                  compositionBounds.height / aRect.height));
  1.1886 +    }
  1.1887 +
  1.1888 +    targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
  1.1889 +    FrameMetrics endZoomToMetrics = mFrameMetrics;
  1.1890 +    endZoomToMetrics.SetZoom(targetZoom / mFrameMetrics.mTransformScale);
  1.1891 +
  1.1892 +    // Adjust the zoomToRect to a sensible position to prevent overscrolling.
  1.1893 +    CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
  1.1894 +
  1.1895 +    // If either of these conditions are met, the page will be
  1.1896 +    // overscrolled after zoomed
  1.1897 +    if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
  1.1898 +      aRect.y = cssPageRect.height - sizeAfterZoom.height;
  1.1899 +      aRect.y = aRect.y > 0 ? aRect.y : 0;
  1.1900 +    }
  1.1901 +    if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
  1.1902 +      aRect.x = cssPageRect.width - sizeAfterZoom.width;
  1.1903 +      aRect.x = aRect.x > 0 ? aRect.x : 0;
  1.1904 +    }
  1.1905 +
  1.1906 +    endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
  1.1907 +    endZoomToMetrics.SetDisplayPortMargins(
  1.1908 +      CalculatePendingDisplayPort(endZoomToMetrics,
  1.1909 +                                  ScreenPoint(0,0),
  1.1910 +                                  0));
  1.1911 +    endZoomToMetrics.SetUseDisplayPortMargins();
  1.1912 +
  1.1913 +    StartAnimation(new ZoomAnimation(
  1.1914 +        mFrameMetrics.GetScrollOffset(),
  1.1915 +        mFrameMetrics.GetZoom(),
  1.1916 +        endZoomToMetrics.GetScrollOffset(),
  1.1917 +        endZoomToMetrics.GetZoom()));
  1.1918 +
  1.1919 +    // Schedule a repaint now, so the new displayport will be painted before the
  1.1920 +    // animation finishes.
  1.1921 +    RequestContentRepaint(endZoomToMetrics);
  1.1922 +  }
  1.1923 +}
  1.1924 +
  1.1925 +void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
  1.1926 +  mTouchBlockState.mPreventDefaultSet = true;
  1.1927 +  mTouchBlockState.mPreventDefault = aPreventDefault;
  1.1928 +  CheckContentResponse();
  1.1929 +}
  1.1930 +
  1.1931 +void AsyncPanZoomController::CheckContentResponse() {
  1.1932 +  bool canProceedToTouchState = true;
  1.1933 +
  1.1934 +  if (mFrameMetrics.mMayHaveTouchListeners) {
  1.1935 +    canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
  1.1936 +  }
  1.1937 +
  1.1938 +  if (mTouchActionPropertyEnabled) {
  1.1939 +    canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
  1.1940 +  }
  1.1941 +
  1.1942 +  if (!canProceedToTouchState) {
  1.1943 +    return;
  1.1944 +  }
  1.1945 +
  1.1946 +  if (mContentResponseTimeoutTask) {
  1.1947 +    mContentResponseTimeoutTask->Cancel();
  1.1948 +    mContentResponseTimeoutTask = nullptr;
  1.1949 +  }
  1.1950 +
  1.1951 +  if (mState == WAITING_CONTENT_RESPONSE) {
  1.1952 +    if (!mTouchBlockState.mPreventDefault) {
  1.1953 +      SetState(NOTHING);
  1.1954 +    }
  1.1955 +
  1.1956 +    mHandlingTouchQueue = true;
  1.1957 +
  1.1958 +    while (!mTouchQueue.IsEmpty()) {
  1.1959 +      if (!mTouchBlockState.mPreventDefault) {
  1.1960 +        HandleInputEvent(mTouchQueue[0]);
  1.1961 +      }
  1.1962 +
  1.1963 +      if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_END ||
  1.1964 +          mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_CANCEL) {
  1.1965 +        mTouchQueue.RemoveElementAt(0);
  1.1966 +        break;
  1.1967 +      }
  1.1968 +
  1.1969 +      mTouchQueue.RemoveElementAt(0);
  1.1970 +    }
  1.1971 +
  1.1972 +    mHandlingTouchQueue = false;
  1.1973 +  }
  1.1974 +}
  1.1975 +
  1.1976 +bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
  1.1977 +  if (!mTouchActionPropertyEnabled) {
  1.1978 +    return true;
  1.1979 +  }
  1.1980 +  // Pointer events specification implies all touch points to allow zoom
  1.1981 +  // to perform it.
  1.1982 +  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
  1.1983 +    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
  1.1984 +      return false;
  1.1985 +    }
  1.1986 +  }
  1.1987 +  return true;
  1.1988 +}
  1.1989 +
  1.1990 +bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
  1.1991 +  if (!mTouchActionPropertyEnabled) {
  1.1992 +    return true;
  1.1993 +  }
  1.1994 +  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
  1.1995 +    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
  1.1996 +      return false;
  1.1997 +    }
  1.1998 +  }
  1.1999 +  return true;
  1.2000 +}
  1.2001 +
  1.2002 +AsyncPanZoomController::TouchBehaviorFlags
  1.2003 +AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
  1.2004 +  if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
  1.2005 +    return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
  1.2006 +  }
  1.2007 +  return DefaultTouchBehavior;
  1.2008 +}
  1.2009 +
  1.2010 +AsyncPanZoomController::TouchBehaviorFlags
  1.2011 +AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
  1.2012 +  // Here we need to perform a hit testing over the touch-action regions attached to the
  1.2013 +  // layer associated with current apzc.
  1.2014 +  // Currently they are in progress, for more info see bug 928833.
  1.2015 +  return AllowedTouchBehavior::UNKNOWN;
  1.2016 +}
  1.2017 +
  1.2018 +void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
  1.2019 +  mTouchBlockState.mAllowedTouchBehaviors.Clear();
  1.2020 +  mTouchBlockState.mAllowedTouchBehaviors.AppendElements(aBehaviors);
  1.2021 +  mTouchBlockState.mAllowedTouchBehaviorSet = true;
  1.2022 +  CheckContentResponse();
  1.2023 +}
  1.2024 +
  1.2025 +void AsyncPanZoomController::SetState(PanZoomState aNewState) {
  1.2026 +
  1.2027 +  PanZoomState oldState;
  1.2028 +
  1.2029 +  // Intentional scoping for mutex
  1.2030 +  {
  1.2031 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.2032 +    oldState = mState;
  1.2033 +    mState = aNewState;
  1.2034 +  }
  1.2035 +
  1.2036 +  if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1.2037 +    if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
  1.2038 +      controller->NotifyAPZStateChange(
  1.2039 +          GetGuid(), APZStateChange::TransformBegin);
  1.2040 +    } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
  1.2041 +      controller->NotifyAPZStateChange(
  1.2042 +          GetGuid(), APZStateChange::TransformEnd);
  1.2043 +    }
  1.2044 +  }
  1.2045 +}
  1.2046 +
  1.2047 +bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
  1.2048 +  return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
  1.2049 +}
  1.2050 +
  1.2051 +bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
  1.2052 +  return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
  1.2053 +}
  1.2054 +
  1.2055 +void AsyncPanZoomController::SetContentResponseTimer() {
  1.2056 +  if (!mContentResponseTimeoutTask) {
  1.2057 +    mContentResponseTimeoutTask =
  1.2058 +      NewRunnableMethod(this, &AsyncPanZoomController::TimeoutContentResponse);
  1.2059 +
  1.2060 +    PostDelayedTask(mContentResponseTimeoutTask, gfxPrefs::APZContentResponseTimeout());
  1.2061 +  }
  1.2062 +}
  1.2063 +
  1.2064 +void AsyncPanZoomController::TimeoutContentResponse() {
  1.2065 +  mContentResponseTimeoutTask = nullptr;
  1.2066 +  ContentReceivedTouch(false);
  1.2067 +}
  1.2068 +
  1.2069 +void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
  1.2070 +  APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
  1.2071 +    aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
  1.2072 +  if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
  1.2073 +    NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
  1.2074 +    return;
  1.2075 +  }
  1.2076 +  // inf float values and other bad cases should be sanitized by the code below.
  1.2077 +  mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
  1.2078 +  mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
  1.2079 +  mZoomConstraints.mMinZoom = (MIN_ZOOM > aConstraints.mMinZoom ? MIN_ZOOM : aConstraints.mMinZoom);
  1.2080 +  mZoomConstraints.mMaxZoom = (MAX_ZOOM > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : MAX_ZOOM);
  1.2081 +  if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
  1.2082 +    mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
  1.2083 +  }
  1.2084 +}
  1.2085 +
  1.2086 +ZoomConstraints
  1.2087 +AsyncPanZoomController::GetZoomConstraints() const
  1.2088 +{
  1.2089 +  return mZoomConstraints;
  1.2090 +}
  1.2091 +
  1.2092 +
  1.2093 +void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
  1.2094 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1.2095 +  if (controller) {
  1.2096 +    controller->PostDelayedTask(aTask, aDelayMs);
  1.2097 +  }
  1.2098 +}
  1.2099 +
  1.2100 +void AsyncPanZoomController::SendAsyncScrollEvent() {
  1.2101 +  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1.2102 +  if (!controller) {
  1.2103 +    return;
  1.2104 +  }
  1.2105 +
  1.2106 +  bool isRoot;
  1.2107 +  CSSRect contentRect;
  1.2108 +  CSSSize scrollableSize;
  1.2109 +  {
  1.2110 +    ReentrantMonitorAutoEnter lock(mMonitor);
  1.2111 +
  1.2112 +    isRoot = mFrameMetrics.mIsRoot;
  1.2113 +    scrollableSize = mFrameMetrics.mScrollableRect.Size();
  1.2114 +    contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
  1.2115 +    contentRect.MoveTo(mCurrentAsyncScrollOffset);
  1.2116 +  }
  1.2117 +
  1.2118 +  controller->SendAsyncScrollDOMEvent(isRoot, contentRect, scrollableSize);
  1.2119 +}
  1.2120 +
  1.2121 +bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
  1.2122 +{
  1.2123 +  return aGuid == GetGuid();
  1.2124 +}
  1.2125 +
  1.2126 +void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut)
  1.2127 +{
  1.2128 +  if (aGuidOut) {
  1.2129 +    *aGuidOut = GetGuid();
  1.2130 +  }
  1.2131 +}
  1.2132 +
  1.2133 +ScrollableLayerGuid AsyncPanZoomController::GetGuid()
  1.2134 +{
  1.2135 +  return ScrollableLayerGuid(mLayersId, mFrameMetrics);
  1.2136 +}
  1.2137 +
  1.2138 +void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
  1.2139 +{
  1.2140 +  mMonitor.AssertCurrentThreadIn();
  1.2141 +
  1.2142 +  FrameMetrics* frame = mSharedFrameMetricsBuffer ?
  1.2143 +      static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
  1.2144 +
  1.2145 +  if (frame && mSharedLock && gfxPrefs::UseProgressiveTilePainting()) {
  1.2146 +    mSharedLock->Lock();
  1.2147 +    *frame = mFrameMetrics;
  1.2148 +    mSharedLock->Unlock();
  1.2149 +  }
  1.2150 +}
  1.2151 +
  1.2152 +void AsyncPanZoomController::ShareCompositorFrameMetrics() {
  1.2153 +
  1.2154 +  PCompositorParent* compositor =
  1.2155 +    (mCrossProcessCompositorParent ? mCrossProcessCompositorParent : mCompositorParent.get());
  1.2156 +
  1.2157 +  // Only create the shared memory buffer if it hasn't already been created,
  1.2158 +  // we are using progressive tile painting, and we have a
  1.2159 +  // compositor to pass the shared memory back to the content process/thread.
  1.2160 +  if (!mSharedFrameMetricsBuffer && compositor && gfxPrefs::UseProgressiveTilePainting()) {
  1.2161 +
  1.2162 +    // Create shared memory and initialize it with the current FrameMetrics value
  1.2163 +    mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
  1.2164 +    FrameMetrics* frame = nullptr;
  1.2165 +    mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
  1.2166 +    mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
  1.2167 +    frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
  1.2168 +
  1.2169 +    if (frame) {
  1.2170 +
  1.2171 +      { // scope the monitor, only needed to copy the FrameMetrics.
  1.2172 +        ReentrantMonitorAutoEnter lock(mMonitor);
  1.2173 +        *frame = mFrameMetrics;
  1.2174 +      }
  1.2175 +
  1.2176 +      // Get the process id of the content process
  1.2177 +      base::ProcessHandle processHandle = compositor->OtherProcess();
  1.2178 +      ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
  1.2179 +
  1.2180 +      // Get the shared memory handle to share with the content process
  1.2181 +      mSharedFrameMetricsBuffer->ShareToProcess(processHandle, &mem);
  1.2182 +
  1.2183 +      // Get the cross process mutex handle to share with the content process
  1.2184 +      mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
  1.2185 +      CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(processHandle);
  1.2186 +
  1.2187 +      // Send the shared memory handle and cross process handle to the content
  1.2188 +      // process by an asynchronous ipc call. Include the APZC unique ID
  1.2189 +      // so the content process know which APZC sent this shared FrameMetrics.
  1.2190 +      if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mAPZCId)) {
  1.2191 +        APZC_LOG("%p failed to share FrameMetrics with content process.", this);
  1.2192 +      }
  1.2193 +    }
  1.2194 +  }
  1.2195 +}
  1.2196 +
  1.2197 +ParentLayerPoint AsyncPanZoomController::ToParentLayerCoords(const ScreenPoint& aPoint)
  1.2198 +{
  1.2199 +  return TransformTo<ParentLayerPixel>(GetNontransientAsyncTransform() * GetCSSTransform(), aPoint);
  1.2200 +}
  1.2201 +
  1.2202 +void AsyncPanZoomController::UpdateTransformScale()
  1.2203 +{
  1.2204 +  gfx3DMatrix nontransientTransforms = GetNontransientAsyncTransform() * GetCSSTransform();
  1.2205 +  if (!FuzzyEqualsMultiplicative(nontransientTransforms.GetXScale(), nontransientTransforms.GetYScale())) {
  1.2206 +    NS_WARNING("The x- and y-scales of the nontransient transforms should be equal");
  1.2207 +  }
  1.2208 +  mFrameMetrics.mTransformScale.scale = nontransientTransforms.GetXScale();
  1.2209 +}
  1.2210 +
  1.2211 +}
  1.2212 +}

mercurial