gfx/layers/apz/src/AsyncPanZoomController.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <math.h>                       // for fabsf, fabs, atan2
     8 #include <stdint.h>                     // for uint32_t, uint64_t
     9 #include <sys/types.h>                  // for int32_t
    10 #include <algorithm>                    // for max, min
    11 #include "AnimationCommon.h"            // for ComputedTimingFunction
    12 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
    13 #include "CompositorParent.h"           // for CompositorParent
    14 #include "FrameMetrics.h"               // for FrameMetrics, etc
    15 #include "GestureEventListener.h"       // for GestureEventListener
    16 #include "InputData.h"                  // for MultiTouchInput, etc
    17 #include "Units.h"                      // for CSSRect, CSSPoint, etc
    18 #include "UnitTransforms.h"             // for TransformTo
    19 #include "base/message_loop.h"          // for MessageLoop
    20 #include "base/task.h"                  // for NewRunnableMethod, etc
    21 #include "base/tracked.h"               // for FROM_HERE
    22 #include "gfxPrefs.h"                   // for gfxPrefs
    23 #include "gfxTypes.h"                   // for gfxFloat
    24 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
    25 #include "mozilla/BasicEvents.h"        // for Modifiers, MODIFIER_*
    26 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
    27 #include "mozilla/Constants.h"          // for M_PI
    28 #include "mozilla/EventForwards.h"      // for nsEventStatus_*
    29 #include "mozilla/Preferences.h"        // for Preferences
    30 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
    31 #include "mozilla/StaticPtr.h"          // for StaticAutoPtr
    32 #include "mozilla/TimeStamp.h"          // for TimeDuration, TimeStamp
    33 #include "mozilla/dom/Touch.h"          // for Touch
    34 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
    35 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
    36 #include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
    37 #include "mozilla/gfx/Rect.h"           // for RoundedIn
    38 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
    39 #include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
    40 #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
    41 #include "mozilla/layers/Axis.h"        // for AxisX, AxisY, Axis, etc
    42 #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
    43 #include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
    44 #include "mozilla/layers/TaskThrottler.h"  // for TaskThrottler
    45 #include "mozilla/mozalloc.h"           // for operator new, etc
    46 #include "mozilla/unused.h"             // for unused
    47 #include "mozilla/FloatingPoint.h"      // for FuzzyEqualsMultiplicative
    48 #include "nsAlgorithm.h"                // for clamped
    49 #include "nsAutoPtr.h"                  // for nsRefPtr
    50 #include "nsCOMPtr.h"                   // for already_AddRefed
    51 #include "nsDebug.h"                    // for NS_WARNING
    52 #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
    53 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
    54 #include "nsMathUtils.h"                // for NS_hypot
    55 #include "nsPoint.h"                    // for nsIntPoint
    56 #include "nsStyleConsts.h"
    57 #include "nsStyleStruct.h"              // for nsTimingFunction
    58 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
    59 #include "nsThreadUtils.h"              // for NS_IsMainThread
    60 #include "SharedMemoryBasic.h"          // for SharedMemoryBasic
    62 // #define APZC_ENABLE_RENDERTRACE
    64 #define APZC_LOG(...)
    65 // #define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
    66 #define APZC_LOG_FM(fm, prefix, ...) \
    67   APZC_LOG(prefix ":" \
    68            " i=(%ld %lld) cb=(%d %d %d %d) rcs=(%.3f %.3f) dp=(%.3f %.3f %.3f %.3f) dpm=(%.3f %.3f %.3f %.3f) um=%d " \
    69            "v=(%.3f %.3f %.3f %.3f) s=(%.3f %.3f) sr=(%.3f %.3f %.3f %.3f) z=(%.3f %.3f %.3f %.3f) u=(%d %lu)\n", \
    70            __VA_ARGS__, \
    71            fm.mPresShellId, fm.GetScrollId(), \
    72            fm.mCompositionBounds.x, fm.mCompositionBounds.y, fm.mCompositionBounds.width, fm.mCompositionBounds.height, \
    73            fm.GetRootCompositionSize().width, fm.GetRootCompositionSize().height, \
    74            fm.mDisplayPort.x, fm.mDisplayPort.y, fm.mDisplayPort.width, fm.mDisplayPort.height, \
    75            fm.GetDisplayPortMargins().top, fm.GetDisplayPortMargins().right, fm.GetDisplayPortMargins().bottom, fm.GetDisplayPortMargins().left, \
    76            fm.GetUseDisplayPortMargins() ? 1 : 0, \
    77            fm.mViewport.x, fm.mViewport.y, fm.mViewport.width, fm.mViewport.height, \
    78            fm.GetScrollOffset().x, fm.GetScrollOffset().y, \
    79            fm.mScrollableRect.x, fm.mScrollableRect.y, fm.mScrollableRect.width, fm.mScrollableRect.height, \
    80            fm.mDevPixelsPerCSSPixel.scale, fm.mResolution.scale, fm.mCumulativeResolution.scale, fm.GetZoom().scale, \
    81            fm.GetScrollOffsetUpdated(), fm.GetScrollGeneration()); \
    83 // Static helper functions
    84 namespace {
    86 int32_t
    87 WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
    88 {
    89   int32_t result = 0;
    90   if (aModifiers & mozilla::MODIFIER_SHIFT) {
    91     result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
    92   }
    93   if (aModifiers & mozilla::MODIFIER_CONTROL) {
    94     result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
    95   }
    96   if (aModifiers & mozilla::MODIFIER_ALT) {
    97     result |= nsIDOMWindowUtils::MODIFIER_ALT;
    98   }
    99   if (aModifiers & mozilla::MODIFIER_META) {
   100     result |= nsIDOMWindowUtils::MODIFIER_META;
   101   }
   102   if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
   103     result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
   104   }
   105   if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
   106     result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
   107   }
   108   if (aModifiers & mozilla::MODIFIER_FN) {
   109     result |= nsIDOMWindowUtils::MODIFIER_FN;
   110   }
   111   if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
   112     result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
   113   }
   114   if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
   115     result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
   116   }
   117   if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
   118     result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
   119   }
   120   if (aModifiers & mozilla::MODIFIER_OS) {
   121     result |= nsIDOMWindowUtils::MODIFIER_OS;
   122   }
   123   return result;
   124 }
   126 }
   128 using namespace mozilla::css;
   130 namespace mozilla {
   131 namespace layers {
   133 typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
   134 typedef GeckoContentController::APZStateChange APZStateChange;
   136 /*
   137  * The following prefs are used to control the behaviour of the APZC.
   138  * The default values are provided in gfxPrefs.h.
   139  *
   140  * "apz.allow-checkerboarding"
   141  * Pref that allows or disallows checkerboarding
   142  *
   143  * "apz.asyncscroll.throttle"
   144  * The time period in ms that throttles mozbrowserasyncscroll event.
   145  *
   146  * "apz.asyncscroll.timeout"
   147  * The timeout in ms for mAsyncScrollTimeoutTask delay task.
   148  *
   149  * "apz.axis_lock_mode"
   150  * The preferred axis locking style. See AxisLockMode for possible values.
   151  *
   152  * "apz.content_response_timeout"
   153  * Amount of time before we timeout response from content. For example, if
   154  * content is being unruly/slow and we don't get a response back within this
   155  * time, we will just pretend that content did not preventDefault any touch
   156  * events we dispatched to it.
   157  *
   158  * "apz.cross_slide_enabled"
   159  * Pref that enables integration with the Metro "cross-slide" gesture.
   160  *
   161  * "apz.enlarge_displayport_when_clipped"
   162  * Pref that enables enlarging of the displayport along one axis when the
   163  * generated displayport's size is beyond that of the scrollable rect on the
   164  * opposite axis.
   165  *
   166  * "apz.fling_friction"
   167  * Amount of friction applied during flings.
   168  *
   169  * "apz.fling_repaint_interval"
   170  * Maximum amount of time flinging before sending a viewport change. This will
   171  * asynchronously repaint the page.
   172  *
   173  * "apz.fling_stopped_threshold"
   174  * When flinging, if the velocity goes below this number, we just stop the
   175  * animation completely. This is to prevent asymptotically approaching 0
   176  * velocity and rerendering unnecessarily.
   177  *
   178  * "apz.max_velocity_inches_per_ms"
   179  * Maximum velocity in inches per millisecond.  Velocity will be capped at this
   180  * value if a faster fling occurs.  Negative values indicate unlimited velocity.
   181  *
   182  * "apz.max_velocity_queue_size"
   183  * Maximum size of velocity queue. The queue contains last N velocity records.
   184  * On touch end we calculate the average velocity in order to compensate
   185  * touch/mouse drivers misbehaviour.
   186  *
   187  * "apz.min_skate_speed"
   188  * Minimum amount of speed along an axis before we switch to "skate" multipliers
   189  * rather than using the "stationary" multipliers.
   190  *
   191  * "apz.num_paint_duration_samples"
   192  * Number of samples to store of how long it took to paint after the previous
   193  * requests.
   194  *
   195  * "apz.pan_repaint_interval"
   196  * Maximum amount of time while panning before sending a viewport change. This
   197  * will asynchronously repaint the page. It is also forced when panning stops.
   198  *
   199  * "apz.touch_start_tolerance"
   200  * Constant describing the tolerance in distance we use, multiplied by the
   201  * device DPI, before we start panning the screen. This is to prevent us from
   202  * accidentally processing taps as touch moves, and from very short/accidental
   203  * touches moving the screen.
   204  *
   205  * "apz.use_paint_duration"
   206  * Whether or not to use the estimated paint duration as a factor when projecting
   207  * the displayport in the direction of scrolling. If this value is set to false,
   208  * a constant 50ms paint time is used; the projection can be scaled as desired
   209  * using the apz.velocity_bias pref below.
   210  *
   211  * "apz.velocity_bias"
   212  * How much to adjust the displayport in the direction of scrolling. This value
   213  * is multiplied by the velocity and added to the displayport offset.
   214  *
   215  * "apz.x_skate_size_multiplier", "apz.y_skate_size_multiplier"
   216  * The multiplier we apply to the displayport size if it is skating (current
   217  * velocity is above apz.min_skate_speed). We prefer to increase the size of the
   218  * Y axis because it is more natural in the case that a user is reading a page
   219  * that scrolls up/down. Note that one, both or neither of these may be used
   220  * at any instant.
   221  * In general we want apz.[xy]_skate_size_multiplier to be smaller than the corresponding
   222  * stationary size multiplier because when panning fast we would like to paint
   223  * less and get faster, more predictable paint times. When panning slowly we
   224  * can afford to paint more even though it's slower.
   225  *
   226  * "apz.x_stationary_size_multiplier", "apz.y_stationary_size_multiplier"
   227  * The multiplier we apply to the displayport size if it is not skating (see
   228  * documentation for the skate size multipliers above).
   229  */
   231 /**
   232  * Default touch behavior (is used when not touch behavior is set).
   233  */
   234 static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
   235                                              AllowedTouchBehavior::HORIZONTAL_PAN |
   236                                              AllowedTouchBehavior::PINCH_ZOOM |
   237                                              AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
   239 /**
   240  * Angle from axis within which we stay axis-locked
   241  */
   242 static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees
   244 /**
   245  * The distance in inches the user must pan before axis lock can be broken
   246  */
   247 static const float AXIS_BREAKOUT_THRESHOLD = 1.0f/32.0f;
   249 /**
   250  * The angle at which axis lock can be broken
   251  */
   252 static const double AXIS_BREAKOUT_ANGLE = M_PI / 8.0; // 22.5 degrees
   254 /**
   255  * Angle from axis to the line drawn by pan move.
   256  * If angle is less than this value we can assume that panning
   257  * can be done in allowed direction (horizontal or vertical).
   258  * Currently used only for touch-action css property stuff and was
   259  * added to keep behavior consistent with IE.
   260  */
   261 static const double ALLOWED_DIRECT_PAN_ANGLE = M_PI / 3.0; // 60 degrees
   263 /**
   264  * Duration of a zoom to animation.
   265  */
   266 static const TimeDuration ZOOM_TO_DURATION = TimeDuration::FromSeconds(0.25);
   268 /**
   269  * Computed time function used for sampling frames of a zoom to animation.
   270  */
   271 StaticAutoPtr<ComputedTimingFunction> gComputedTimingFunction;
   273 /**
   274  * Maximum zoom amount, always used, even if a page asks for higher.
   275  */
   276 static const CSSToScreenScale MAX_ZOOM(8.0f);
   278 /**
   279  * Minimum zoom amount, always used, even if a page asks for lower.
   280  */
   281 static const CSSToScreenScale MIN_ZOOM(0.125f);
   283 /**
   284  * Is aAngle within the given threshold of the horizontal axis?
   285  * @param aAngle an angle in radians in the range [0, pi]
   286  * @param aThreshold an angle in radians in the range [0, pi/2]
   287  */
   288 static bool IsCloseToHorizontal(float aAngle, float aThreshold)
   289 {
   290   return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
   291 }
   293 // As above, but for the vertical axis.
   294 static bool IsCloseToVertical(float aAngle, float aThreshold)
   295 {
   296   return (fabs(aAngle - (M_PI / 2)) < aThreshold);
   297 }
   299 template <typename Units>
   300 static bool IsZero(const gfx::PointTyped<Units>& aPoint)
   301 {
   302   return FuzzyEqualsMultiplicative(aPoint.x, 0.0f)
   303       && FuzzyEqualsMultiplicative(aPoint.y, 0.0f);
   304 }
   306 static inline void LogRendertraceRect(const ScrollableLayerGuid& aGuid, const char* aDesc, const char* aColor, const CSSRect& aRect)
   307 {
   308 #ifdef APZC_ENABLE_RENDERTRACE
   309   static const TimeStamp sRenderStart = TimeStamp::Now();
   310   TimeDuration delta = TimeStamp::Now() - sRenderStart;
   311   printf_stderr("(%llu,%lu,%llu)%s RENDERTRACE %f rect %s %f %f %f %f\n",
   312     aGuid.mLayersId, aGuid.mPresShellId, aGuid.GetScrollId(),
   313     aDesc, delta.ToMilliseconds(), aColor,
   314     aRect.x, aRect.y, aRect.width, aRect.height);
   315 #endif
   316 }
   318 static TimeStamp sFrameTime;
   320 // Counter used to give each APZC a unique id
   321 static uint32_t sAsyncPanZoomControllerCount = 0;
   323 static TimeStamp
   324 GetFrameTime() {
   325   if (sFrameTime.IsNull()) {
   326     return TimeStamp::Now();
   327   }
   328   return sFrameTime;
   329 }
   331 class FlingAnimation: public AsyncPanZoomAnimation {
   332 public:
   333   FlingAnimation(AsyncPanZoomController& aApzc)
   334     : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
   335     , mApzc(aApzc)
   336   {}
   337   /**
   338    * Advances a fling by an interpolated amount based on the passed in |aDelta|.
   339    * This should be called whenever sampling the content transform for this
   340    * frame. Returns true if the fling animation should be advanced by one frame,
   341    * or false if there is no fling or the fling has ended.
   342    */
   343   virtual bool Sample(FrameMetrics& aFrameMetrics,
   344                       const TimeDuration& aDelta);
   346 private:
   347   AsyncPanZoomController& mApzc;
   348 };
   350 class ZoomAnimation: public AsyncPanZoomAnimation {
   351 public:
   352   ZoomAnimation(CSSPoint aStartOffset, CSSToScreenScale aStartZoom,
   353                 CSSPoint aEndOffset, CSSToScreenScale aEndZoom)
   354     : mStartOffset(aStartOffset)
   355     , mStartZoom(aStartZoom)
   356     , mEndOffset(aEndOffset)
   357     , mEndZoom(aEndZoom)
   358   {}
   360   virtual bool Sample(FrameMetrics& aFrameMetrics,
   361                       const TimeDuration& aDelta);
   363 private:
   364   TimeDuration mDuration;
   366   // Old metrics from before we started a zoom animation. This is only valid
   367   // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
   368   // interpolate between the start and end frames. We only use the
   369   // |mViewportScrollOffset| and |mResolution| fields on this.
   370   CSSPoint mStartOffset;
   371   CSSToScreenScale mStartZoom;
   373   // Target metrics for a zoom to animation. This is only valid when we are in
   374   // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
   375   // |mResolution| fields on this.
   376   CSSPoint mEndOffset;
   377   CSSToScreenScale mEndZoom;
   378 };
   380 void
   381 AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
   382   sFrameTime = aTime;
   383 }
   385 /*static*/ void
   386 AsyncPanZoomController::InitializeGlobalState()
   387 {
   388   MOZ_ASSERT(NS_IsMainThread());
   390   static bool sInitialized = false;
   391   if (sInitialized)
   392     return;
   393   sInitialized = true;
   395   gComputedTimingFunction = new ComputedTimingFunction();
   396   gComputedTimingFunction->Init(
   397     nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
   398   ClearOnShutdown(&gComputedTimingFunction);
   399 }
   401 AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
   402                                                APZCTreeManager* aTreeManager,
   403                                                GeckoContentController* aGeckoContentController,
   404                                                GestureBehavior aGestures)
   405   :  mLayersId(aLayersId),
   406      mCrossProcessCompositorParent(nullptr),
   407      mPaintThrottler(GetFrameTime()),
   408      mGeckoContentController(aGeckoContentController),
   409      mRefPtrMonitor("RefPtrMonitor"),
   410      mMonitor("AsyncPanZoomController"),
   411      mTouchActionPropertyEnabled(gfxPrefs::TouchActionEnabled()),
   412      mContentResponseTimeoutTask(nullptr),
   413      mX(MOZ_THIS_IN_INITIALIZER_LIST()),
   414      mY(MOZ_THIS_IN_INITIALIZER_LIST()),
   415      mPanDirRestricted(false),
   416      mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
   417      mLastSampleTime(GetFrameTime()),
   418      mState(NOTHING),
   419      mLastAsyncScrollTime(GetFrameTime()),
   420      mLastAsyncScrollOffset(0, 0),
   421      mCurrentAsyncScrollOffset(0, 0),
   422      mAsyncScrollTimeoutTask(nullptr),
   423      mHandlingTouchQueue(false),
   424      mTreeManager(aTreeManager),
   425      mScrollParentId(FrameMetrics::NULL_SCROLL_ID),
   426      mAPZCId(sAsyncPanZoomControllerCount++),
   427      mSharedFrameMetricsBuffer(nullptr),
   428      mSharedLock(nullptr)
   429 {
   430   MOZ_COUNT_CTOR(AsyncPanZoomController);
   432   if (aGestures == USE_GESTURE_DETECTOR) {
   433     mGestureEventListener = new GestureEventListener(this);
   434   }
   435 }
   437 AsyncPanZoomController::~AsyncPanZoomController() {
   439   PCompositorParent* compositor =
   440     (mCrossProcessCompositorParent ? mCrossProcessCompositorParent : mCompositorParent.get());
   442   // Only send the release message if the SharedFrameMetrics has been created.
   443   if (compositor && mSharedFrameMetricsBuffer) {
   444     unused << compositor->SendReleaseSharedCompositorFrameMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
   445   }
   447   delete mSharedFrameMetricsBuffer;
   448   delete mSharedLock;
   450   MOZ_COUNT_DTOR(AsyncPanZoomController);
   451 }
   453 already_AddRefed<GeckoContentController>
   454 AsyncPanZoomController::GetGeckoContentController() {
   455   MonitorAutoLock lock(mRefPtrMonitor);
   456   nsRefPtr<GeckoContentController> controller = mGeckoContentController;
   457   return controller.forget();
   458 }
   460 already_AddRefed<GestureEventListener>
   461 AsyncPanZoomController::GetGestureEventListener() {
   462   MonitorAutoLock lock(mRefPtrMonitor);
   463   nsRefPtr<GestureEventListener> listener = mGestureEventListener;
   464   return listener.forget();
   465 }
   467 void
   468 AsyncPanZoomController::Destroy()
   469 {
   470   { // scope the lock
   471     MonitorAutoLock lock(mRefPtrMonitor);
   472     mGeckoContentController = nullptr;
   473     mGestureEventListener = nullptr;
   474   }
   475   mPrevSibling = nullptr;
   476   mLastChild = nullptr;
   477   mParent = nullptr;
   478   mTreeManager = nullptr;
   479 }
   481 bool
   482 AsyncPanZoomController::IsDestroyed()
   483 {
   484   return mTreeManager == nullptr;
   485 }
   487 /* static */float
   488 AsyncPanZoomController::GetTouchStartTolerance()
   489 {
   490   return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
   491 }
   493 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
   494 {
   495   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
   496 }
   498 nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
   499   if (aEvent.mInputType == MULTITOUCH_INPUT &&
   500       aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
   501     // Starting a new touch block, clear old touch block state.
   502     mTouchBlockState = TouchBlockState();
   503   }
   505   // If we may have touch listeners and touch action property is enabled, we
   506   // enable the machinery that allows touch listeners to preventDefault any touch inputs
   507   // and also waits for the allowed touch behavior values to be received from the outside.
   508   // This should not happen unless there are actually touch listeners and touch-action property
   509   // enable as it introduces potentially unbounded lag because it causes a round-trip through
   510   // content.  Usually, if content is responding in a timely fashion, this only introduces a
   511   // nearly constant few hundred ms of lag.
   512   if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
   513       (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
   514     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   515     if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
   516       SetState(WAITING_CONTENT_RESPONSE);
   517     }
   518   }
   520   if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
   521     if (aEvent.mInputType == MULTITOUCH_INPUT) {
   522       const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   523       mTouchQueue.AppendElement(multiTouchInput);
   525       SetContentResponseTimer();
   526     }
   527     return nsEventStatus_eIgnore;
   528   }
   530   return HandleInputEvent(aEvent);
   531 }
   533 nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
   534   nsEventStatus rv = nsEventStatus_eIgnore;
   536   switch (aEvent.mInputType) {
   537   case MULTITOUCH_INPUT: {
   538     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
   540     nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
   541     if (listener) {
   542       rv = listener->HandleInputEvent(multiTouchInput);
   543       if (rv == nsEventStatus_eConsumeNoDefault) {
   544         return rv;
   545       }
   546     }
   548     switch (multiTouchInput.mType) {
   549       case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
   550       case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
   551       case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
   552       case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
   553       default: NS_WARNING("Unhandled multitouch"); break;
   554     }
   555     break;
   556   }
   557   default: NS_WARNING("Unhandled input event"); break;
   558   }
   560   mLastEventTime = aEvent.mTime;
   561   return rv;
   562 }
   564 nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
   565 {
   566   nsEventStatus rv = nsEventStatus_eIgnore;
   568   switch (aEvent.mInputType) {
   569   case PINCHGESTURE_INPUT: {
   570     const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
   571     switch (pinchGestureInput.mType) {
   572       case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
   573       case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
   574       case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
   575       default: NS_WARNING("Unhandled pinch gesture"); break;
   576     }
   577     break;
   578   }
   579   case TAPGESTURE_INPUT: {
   580     const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
   581     switch (tapGestureInput.mType) {
   582       case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
   583       case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
   584       case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
   585       case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
   586       case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
   587       case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
   588       default: NS_WARNING("Unhandled tap gesture"); break;
   589     }
   590     break;
   591   }
   592   default: NS_WARNING("Unhandled input event"); break;
   593   }
   595   mLastEventTime = aEvent.mTime;
   596   return rv;
   597 }
   599 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   600   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   601   mPanDirRestricted = false;
   602   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
   604   switch (mState) {
   605     case ANIMATING_ZOOM:
   606       // We just interrupted a double-tap animation, so force a redraw in case
   607       // this touchstart is just a tap that doesn't end up triggering a redraw.
   608       {
   609         ReentrantMonitorAutoEnter lock(mMonitor);
   610         RequestContentRepaint();
   611         ScheduleComposite();
   612         UpdateSharedCompositorFrameMetrics();
   613       }
   614       // Fall through.
   615     case FLING:
   616       CancelAnimation();
   617       // Fall through.
   618     case NOTHING: {
   619       mX.StartTouch(point.x);
   620       mY.StartTouch(point.y);
   621       APZCTreeManager* treeManagerLocal = mTreeManager;
   622       nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   623       if (treeManagerLocal && controller) {
   624         bool touchCanBePan = treeManagerLocal->CanBePanned(this);
   625         controller->NotifyAPZStateChange(
   626             GetGuid(), APZStateChange::StartTouch, touchCanBePan);
   627       }
   628       SetState(TOUCHING);
   629       break;
   630     }
   631     case TOUCHING:
   632     case PANNING:
   633     case PANNING_LOCKED_X:
   634     case PANNING_LOCKED_Y:
   635     case CROSS_SLIDING_X:
   636     case CROSS_SLIDING_Y:
   637     case PINCHING:
   638     case WAITING_CONTENT_RESPONSE:
   639       NS_WARNING("Received impossible touch in OnTouchStart");
   640       break;
   641     default:
   642       NS_WARNING("Unhandled case in OnTouchStart");
   643       break;
   644   }
   646   return nsEventStatus_eConsumeNoDefault;
   647 }
   649 nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
   650   APZC_LOG("%p got a touch-move in state %d\n", this, mState);
   651   switch (mState) {
   652     case FLING:
   653     case NOTHING:
   654     case ANIMATING_ZOOM:
   655       // May happen if the user double-taps and drags without lifting after the
   656       // second tap. Ignore the move if this happens.
   657       return nsEventStatus_eIgnore;
   659     case CROSS_SLIDING_X:
   660     case CROSS_SLIDING_Y:
   661       // While cross-sliding, we don't want to consume any touchmove events for
   662       // panning or zooming, and let the caller handle them instead.
   663       return nsEventStatus_eIgnore;
   665     case TOUCHING: {
   666       float panThreshold = GetTouchStartTolerance();
   667       UpdateWithTouchAtDevicePoint(aEvent);
   669       if (PanDistance() < panThreshold) {
   670         return nsEventStatus_eIgnore;
   671       }
   673       if (mTouchActionPropertyEnabled &&
   674           (GetTouchBehavior(0) & AllowedTouchBehavior::VERTICAL_PAN) &&
   675           (GetTouchBehavior(0) & AllowedTouchBehavior::HORIZONTAL_PAN)) {
   676         // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
   677         // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
   678         // status immediately to trigger cancel event further. It should happen independent of
   679         // the parent type (whether it is scrolling or not).
   680         StartPanning(aEvent);
   681         return nsEventStatus_eConsumeNoDefault;
   682       }
   684       return StartPanning(aEvent);
   685     }
   687     case PANNING:
   688     case PANNING_LOCKED_X:
   689     case PANNING_LOCKED_Y:
   690       TrackTouch(aEvent);
   691       return nsEventStatus_eConsumeNoDefault;
   693     case PINCHING:
   694       // The scale gesture listener should have handled this.
   695       NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
   696       return nsEventStatus_eIgnore;
   698     case WAITING_CONTENT_RESPONSE:
   699       NS_WARNING("Received impossible touch in OnTouchMove");
   700       break;
   701   }
   703   return nsEventStatus_eConsumeNoDefault;
   704 }
   706 nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
   707   APZC_LOG("%p got a touch-end in state %d\n", this, mState);
   709   OnTouchEndOrCancel();
   711   // In case no touch behavior triggered previously we can avoid sending
   712   // scroll events or requesting content repaint. This condition is added
   713   // to make tests consistent - in case touch-action is NONE (and therefore
   714   // no pans/zooms can be performed) we expected neither scroll or repaint
   715   // events.
   716   if (mState != NOTHING) {
   717     ReentrantMonitorAutoEnter lock(mMonitor);
   718     SendAsyncScrollEvent();
   719   }
   721   switch (mState) {
   722   case FLING:
   723     // Should never happen.
   724     NS_WARNING("Received impossible touch end in OnTouchEnd.");
   725     // Fall through.
   726   case ANIMATING_ZOOM:
   727   case NOTHING:
   728     // May happen if the user double-taps and drags without lifting after the
   729     // second tap. Ignore if this happens.
   730     return nsEventStatus_eIgnore;
   732   case TOUCHING:
   733   case CROSS_SLIDING_X:
   734   case CROSS_SLIDING_Y:
   735     SetState(NOTHING);
   736     return nsEventStatus_eIgnore;
   738   case PANNING:
   739   case PANNING_LOCKED_X:
   740   case PANNING_LOCKED_Y:
   741     {
   742       // Make a local copy of the tree manager pointer and check if it's not
   743       // null before calling FlushRepaintsForOverscrollHandoffChain().
   744       // This is necessary because Destroy(), which nulls out mTreeManager,
   745       // could be called concurrently.
   746       APZCTreeManager* treeManagerLocal = mTreeManager;
   747       if (treeManagerLocal) {
   748         if (!treeManagerLocal->FlushRepaintsForOverscrollHandoffChain()) {
   749           NS_WARNING("Overscroll handoff chain was empty during panning! This should not be the case.");
   750           // Graceful handling of error condition
   751           FlushRepaintForOverscrollHandoff();
   752         }
   753       }
   754     }
   755     mX.EndTouch();
   756     mY.EndTouch();
   757     SetState(FLING);
   758     StartAnimation(new FlingAnimation(*this));
   759     return nsEventStatus_eConsumeNoDefault;
   761   case PINCHING:
   762     SetState(NOTHING);
   763     // Scale gesture listener should have handled this.
   764     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
   765     return nsEventStatus_eIgnore;
   767   case WAITING_CONTENT_RESPONSE:
   768     NS_WARNING("Received impossible touch in OnTouchEnd");
   769     break;
   770   }
   772   return nsEventStatus_eConsumeNoDefault;
   773 }
   775 nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
   776   APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
   777   OnTouchEndOrCancel();
   778   SetState(NOTHING);
   779   return nsEventStatus_eConsumeNoDefault;
   780 }
   782 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   783   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
   785   if (!TouchActionAllowPinchZoom()) {
   786     return nsEventStatus_eIgnore;
   787   }
   789   if (!mZoomConstraints.mAllowZoom) {
   790     return nsEventStatus_eConsumeNoDefault;
   791   }
   793   SetState(PINCHING);
   794   mLastZoomFocus = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
   796   return nsEventStatus_eConsumeNoDefault;
   797 }
   799 nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
   800   APZC_LOG("%p got a scale in state %d\n", this, mState);
   801   if (mState != PINCHING) {
   802     return nsEventStatus_eConsumeNoDefault;
   803   }
   805   float prevSpan = aEvent.mPreviousSpan;
   806   if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
   807     // We're still handling it; we've just decided to throw this event away.
   808     return nsEventStatus_eConsumeNoDefault;
   809   }
   811   float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
   813   {
   814     ReentrantMonitorAutoEnter lock(mMonitor);
   816     CSSToParentLayerScale userZoom = mFrameMetrics.GetZoomToParent();
   817     ParentLayerPoint focusPoint = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
   818     CSSPoint cssFocusPoint = focusPoint / userZoom;
   820     CSSPoint focusChange = (mLastZoomFocus - focusPoint) / userZoom;
   821     // If displacing by the change in focus point will take us off page bounds,
   822     // then reduce the displacement such that it doesn't.
   823     if (mX.DisplacementWillOverscroll(focusChange.x) != Axis::OVERSCROLL_NONE) {
   824       focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
   825     }
   826     if (mY.DisplacementWillOverscroll(focusChange.y) != Axis::OVERSCROLL_NONE) {
   827       focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
   828     }
   829     ScrollBy(focusChange);
   831     // When we zoom in with focus, we can zoom too much towards the boundaries
   832     // that we actually go over them. These are the needed displacements along
   833     // either axis such that we don't overscroll the boundaries when zooming.
   834     CSSPoint neededDisplacement;
   836     CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale;
   837     CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
   838     realMinZoom.scale = std::max(realMinZoom.scale,
   839                                  mFrameMetrics.mCompositionBounds.width / mFrameMetrics.mScrollableRect.width);
   840     realMinZoom.scale = std::max(realMinZoom.scale,
   841                                  mFrameMetrics.mCompositionBounds.height / mFrameMetrics.mScrollableRect.height);
   842     if (realMaxZoom < realMinZoom) {
   843       realMaxZoom = realMinZoom;
   844     }
   846     bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
   847                    (spanRatio < 1.0 && userZoom > realMinZoom);
   849     if (doScale) {
   850       spanRatio = clamped(spanRatio,
   851                           realMinZoom.scale / userZoom.scale,
   852                           realMaxZoom.scale / userZoom.scale);
   854       // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
   855       // up above we clamped it.
   856       neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
   857       neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
   859       ScaleWithFocus(spanRatio, cssFocusPoint);
   861       if (neededDisplacement != CSSPoint()) {
   862         ScrollBy(neededDisplacement);
   863       }
   865       ScheduleComposite();
   866       // We don't want to redraw on every scale, so don't use
   867       // RequestContentRepaint()
   868       UpdateSharedCompositorFrameMetrics();
   869     }
   871     mLastZoomFocus = focusPoint;
   872   }
   874   return nsEventStatus_eConsumeNoDefault;
   875 }
   877 nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
   878   APZC_LOG("%p got a scale-end in state %d\n", this, mState);
   880   SetState(NOTHING);
   882   {
   883     ReentrantMonitorAutoEnter lock(mMonitor);
   884     ScheduleComposite();
   885     RequestContentRepaint();
   886     UpdateSharedCompositorFrameMetrics();
   887   }
   889   return nsEventStatus_eConsumeNoDefault;
   890 }
   892 bool
   893 AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut)
   894 {
   895   APZCTreeManager* treeManagerLocal = mTreeManager;
   896   if (treeManagerLocal) {
   897     gfx3DMatrix transformToApzc;
   898     gfx3DMatrix transformToGecko;
   899     treeManagerLocal->GetInputTransforms(this, transformToApzc, transformToGecko);
   900     gfxPoint result = transformToGecko.Transform(gfxPoint(aPoint.x, aPoint.y));
   901     // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
   902     // for this coordinate space and it maps the closest to LayoutDevicePoint.
   903     LayoutDevicePoint layoutPoint = LayoutDevicePoint(result.x, result.y);
   904     { // scoped lock to access mFrameMetrics
   905       ReentrantMonitorAutoEnter lock(mMonitor);
   906       *aOut = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
   907     }
   908     return true;
   909   }
   910   return false;
   911 }
   913 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   914   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   915   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   916   if (controller) {
   917     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   918     CSSPoint geckoScreenPoint;
   919     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   920       SetState(WAITING_CONTENT_RESPONSE);
   921       SetContentResponseTimer();
   922       controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
   923       return nsEventStatus_eConsumeNoDefault;
   924     }
   925   }
   926   return nsEventStatus_eIgnore;
   927 }
   929 nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
   930   APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
   931   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   932   if (controller) {
   933     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   934     CSSPoint geckoScreenPoint;
   935     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   936       controller->HandleLongTapUp(geckoScreenPoint, modifiers, GetGuid());
   937       return nsEventStatus_eConsumeNoDefault;
   938     }
   939   }
   940   return nsEventStatus_eIgnore;
   941 }
   943 nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   944   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   945   if (controller) {
   946     CSSPoint geckoScreenPoint;
   947     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
   948       int32_t modifiers = WidgetModifiersToDOMModifiers(aModifiers);
   949       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
   950       // calling controller->HandleSingleTap directly might mean that content receives
   951       // the single tap message before the corresponding touch-up. To avoid that we
   952       // schedule the singletap message to run on the next spin of the event loop.
   953       // See bug 965381 for the issue this was causing.
   954       controller->PostDelayedTask(
   955         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
   956                           geckoScreenPoint, modifiers, GetGuid()),
   957         0);
   958       mTouchBlockState.mSingleTapOccurred = true;
   959       return nsEventStatus_eConsumeNoDefault;
   960     }
   961   }
   962   return nsEventStatus_eIgnore;
   963 }
   965 void AsyncPanZoomController::OnTouchEndOrCancel() {
   966   if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
   967     controller->NotifyAPZStateChange(
   968         GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
   969   }
   970 }
   972 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   973   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   974   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   975   // sending event to content
   976   if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
   977     return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   978   }
   979   return nsEventStatus_eIgnore;
   980 }
   982 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   983   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   984   return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   985 }
   987 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   988   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   989   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   990   if (controller) {
   991     if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
   992       int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
   993       CSSPoint geckoScreenPoint;
   994       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
   995         controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
   996       }
   997     }
   998     return nsEventStatus_eConsumeNoDefault;
   999   }
  1000   return nsEventStatus_eIgnore;
  1003 nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
  1004   APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
  1005   // XXX: Implement this.
  1006   return nsEventStatus_eIgnore;
  1009 float AsyncPanZoomController::PanDistance() {
  1010   ReentrantMonitorAutoEnter lock(mMonitor);
  1011   return NS_hypot(mX.PanDistance(), mY.PanDistance());
  1014 const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
  1015   return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
  1018 void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBehaviorFlags aBehavior) {
  1019   // Handling of cross sliding will need to be added in this method after touch-action released
  1020   // enabled by default.
  1021   if ((aBehavior & AllowedTouchBehavior::VERTICAL_PAN) && (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN)) {
  1022     if (mX.Scrollable() && mY.Scrollable()) {
  1023       if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
  1024         mY.SetAxisLocked(true);
  1025         SetState(PANNING_LOCKED_X);
  1026       } else if (IsCloseToVertical(aAngle, AXIS_LOCK_ANGLE)) {
  1027         mX.SetAxisLocked(true);
  1028         SetState(PANNING_LOCKED_Y);
  1029       } else {
  1030         SetState(PANNING);
  1032     } else if (mX.Scrollable() || mY.Scrollable()) {
  1033       SetState(PANNING);
  1034     } else {
  1035       SetState(NOTHING);
  1037   } else if (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN) {
  1038     // Using bigger angle for panning to keep behavior consistent
  1039     // with IE.
  1040     if (IsCloseToHorizontal(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
  1041       mY.SetAxisLocked(true);
  1042       SetState(PANNING_LOCKED_X);
  1043       mPanDirRestricted = true;
  1044     } else {
  1045       // Don't treat these touches as pan/zoom movements since 'touch-action' value
  1046       // requires it.
  1047       SetState(NOTHING);
  1049   } else if (aBehavior & AllowedTouchBehavior::VERTICAL_PAN) {
  1050     if (IsCloseToVertical(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
  1051       mX.SetAxisLocked(true);
  1052       SetState(PANNING_LOCKED_Y);
  1053       mPanDirRestricted = true;
  1054     } else {
  1055       SetState(NOTHING);
  1057   } else {
  1058     SetState(NOTHING);
  1062 void AsyncPanZoomController::HandlePanning(double aAngle) {
  1063   if (!gfxPrefs::APZCrossSlideEnabled() && (!mX.Scrollable() || !mY.Scrollable())) {
  1064     SetState(PANNING);
  1065   } else if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
  1066     mY.SetAxisLocked(true);
  1067     if (mX.Scrollable()) {
  1068       SetState(PANNING_LOCKED_X);
  1069     } else {
  1070       SetState(CROSS_SLIDING_X);
  1071       mX.SetAxisLocked(true);
  1073   } else if (IsCloseToVertical(aAngle, AXIS_LOCK_ANGLE)) {
  1074     mX.SetAxisLocked(true);
  1075     if (mY.Scrollable()) {
  1076       SetState(PANNING_LOCKED_Y);
  1077     } else {
  1078       SetState(CROSS_SLIDING_Y);
  1079       mY.SetAxisLocked(true);
  1081   } else {
  1082     SetState(PANNING);
  1086 nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
  1087   ReentrantMonitorAutoEnter lock(mMonitor);
  1089   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1090   float dx = mX.PanDistance(point.x);
  1091   float dy = mY.PanDistance(point.y);
  1093   // When the touch move breaks through the pan threshold, reposition the touch down origin
  1094   // so the page won't jump when we start panning.
  1095   mX.StartTouch(point.x);
  1096   mY.StartTouch(point.y);
  1097   mLastEventTime = aEvent.mTime;
  1099   double angle = atan2(dy, dx); // range [-pi, pi]
  1100   angle = fabs(angle); // range [0, pi]
  1102   if (mTouchActionPropertyEnabled) {
  1103     HandlePanningWithTouchAction(angle, GetTouchBehavior(0));
  1104   } else {
  1105     if (GetAxisLockMode() == FREE) {
  1106       SetState(PANNING);
  1107     } else {
  1108       HandlePanning(angle);
  1112   if (IsPanningState(mState)) {
  1113     if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1114       controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
  1116     return nsEventStatus_eConsumeNoDefault;
  1118   // Don't consume an event that didn't trigger a panning.
  1119   return nsEventStatus_eIgnore;
  1122 void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
  1123   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1124   TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
  1126   // Probably a duplicate event, just throw it away.
  1127   if (timeDelta.ToMilliseconds() <= EPSILON) {
  1128     return;
  1131   mX.UpdateWithTouchAtDevicePoint(point.x, timeDelta);
  1132   mY.UpdateWithTouchAtDevicePoint(point.y, timeDelta);
  1135 void AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
  1136                                            const ScreenPoint& aEndPoint,
  1137                                            uint32_t aOverscrollHandoffChainIndex) {
  1139   // "start - end" rather than "end - start" because e.g. moving your finger
  1140   // down (*positive* direction along y axis) causes the vertical scroll offset
  1141   // to *decrease* as the page follows your finger.
  1142   ScreenPoint displacement = aStartPoint - aEndPoint;
  1144   ScreenPoint overscroll;  // will be used outside monitor block
  1146     ReentrantMonitorAutoEnter lock(mMonitor);
  1148     CSSToScreenScale zoom = mFrameMetrics.GetZoom();
  1150     // Inversely scale the offset by the resolution (when you're zoomed further in,
  1151     // the same swipe should move you a shorter distance).
  1152     CSSPoint cssDisplacement = displacement / zoom;
  1154     CSSPoint cssOverscroll;
  1155     CSSPoint allowedDisplacement(mX.AdjustDisplacement(cssDisplacement.x,
  1156                                                        cssOverscroll.x),
  1157                                  mY.AdjustDisplacement(cssDisplacement.y,
  1158                                                        cssOverscroll.y));
  1159     overscroll = cssOverscroll * zoom;
  1161     if (!IsZero(allowedDisplacement)) {
  1162       ScrollBy(allowedDisplacement);
  1163       ScheduleComposite();
  1165       TimeDuration timePaintDelta = mPaintThrottler.TimeSinceLastRequest(GetFrameTime());
  1166       if (timePaintDelta.ToMilliseconds() > gfxPrefs::APZPanRepaintInterval()) {
  1167         RequestContentRepaint();
  1169       UpdateSharedCompositorFrameMetrics();
  1173   if (!IsZero(overscroll)) {
  1174     // "+ overscroll" rather than "- overscroll" because "overscroll" is what's
  1175     // left of "displacement", and "displacement" is "start - end".
  1176     CallDispatchScroll(aEndPoint + overscroll, aEndPoint, aOverscrollHandoffChainIndex + 1);
  1180 void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
  1181   // We may have a pre-existing velocity for whatever reason (for example,
  1182   // a previously handed off fling). We don't want to clobber that.
  1183   mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
  1184   mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
  1185   SetState(FLING);
  1186   StartAnimation(new FlingAnimation(*this));
  1189 void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
  1190                                                 uint32_t aOverscrollHandoffChainIndex) {
  1191   // Make a local copy of the tree manager pointer and check if it's not
  1192   // null before calling DispatchScroll(). This is necessary because
  1193   // Destroy(), which nulls out mTreeManager, could be called concurrently.
  1194   APZCTreeManager* treeManagerLocal = mTreeManager;
  1195   if (treeManagerLocal) {
  1196     treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
  1197                                      aOverscrollHandoffChainIndex);
  1201 void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
  1202   ScreenIntPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
  1203   ScreenIntPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
  1204   TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
  1206   // Probably a duplicate event, just throw it away.
  1207   if (timeDelta.ToMilliseconds() <= EPSILON) {
  1208     return;
  1211   // If we're axis-locked, check if the user is trying to break the lock
  1212   if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
  1213     ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
  1214     float dx = mX.PanDistance(point.x);
  1215     float dy = mY.PanDistance(point.y);
  1217     double angle = atan2(dy, dx); // range [-pi, pi]
  1218     angle = fabs(angle); // range [0, pi]
  1220     float breakThreshold = AXIS_BREAKOUT_THRESHOLD * APZCTreeManager::GetDPI();
  1222     if (fabs(dx) > breakThreshold || fabs(dy) > breakThreshold) {
  1223       if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
  1224         if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
  1225           mY.SetAxisLocked(false);
  1226           SetState(PANNING);
  1228       } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
  1229         if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
  1230           mX.SetAxisLocked(false);
  1231           SetState(PANNING);
  1237   UpdateWithTouchAtDevicePoint(aEvent);
  1239   CallDispatchScroll(prevTouchPoint, touchPoint, 0);
  1242 ScreenIntPoint& AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
  1243   return ((SingleTouchData&)aEvent.mTouches[0]).mScreenPoint;
  1246 bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
  1247                             const TimeDuration& aDelta) {
  1249   // If the fling is handed off to our APZC from a child, on the first call to
  1250   // Sample() aDelta might be negative because it's computed as the sample time
  1251   // from SampleContentTransformForFrame() minus our APZC's mLastSampleTime
  1252   // which is the time the child handed off the fling from its call to
  1253   // SampleContentTransformForFrame() with the same sample time. If we allow
  1254   // the negative aDelta to be processed, it will yield a displacement in the
  1255   // direction opposite to the fling, which can cause us to overscroll and
  1256   // hand off the fling to _our_ parent, which effectively kills the fling.
  1257   if (aDelta.ToMilliseconds() <= 0) {
  1258     return true;
  1261   bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta),
  1262        shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta);
  1263   // If we shouldn't continue the fling, let's just stop and repaint.
  1264   if (!shouldContinueFlingX && !shouldContinueFlingY) {
  1265     return false;
  1268   // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
  1269   // Since we need to hand off the velocity to the tree manager in such a case,
  1270   // we save it here. Would be ScreenVector instead of ScreenPoint if we had
  1271   // vector classes.
  1272   ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
  1274   ScreenPoint offset = velocity * aDelta.ToMilliseconds();
  1276   // Inversely scale the offset by the resolution (when you're zoomed further in,
  1277   // the same swipe should move you a shorter distance).
  1278   CSSPoint cssOffset = offset / aFrameMetrics.GetZoom();
  1279   CSSPoint overscroll;
  1280   aFrameMetrics.ScrollBy(CSSPoint(
  1281     mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
  1282     mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
  1283   ));
  1285   // If the fling has caused us to reach the end of our scroll range, hand
  1286   // off the fling to the next APZC in the overscroll handoff chain.
  1287   if (!IsZero(overscroll)) {
  1288     // We may have reached the end of the scroll range along one axis but
  1289     // not the other. In such a case we only want to hand off the relevant
  1290     // component of the fling.
  1291     if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
  1292       velocity.x = 0;
  1293     } else if (FuzzyEqualsMultiplicative(overscroll.y, 0.0f)) {
  1294       velocity.y = 0;
  1297     // To hand off the fling, we call APZCTreeManager::HandleFlingOverscroll()
  1298     // which starts a new fling in the next APZC in the handoff chain with
  1299     // the same velocity. For simplicity, the actual overscroll of the current
  1300     // sample is discarded rather than being handed off. The compositor should
  1301     // sample animations sufficiently frequently that this is not noticeable.
  1303     // Make a local copy of the tree manager pointer and check if it's not
  1304     // null before calling HandleFlingOverscroll(). This is necessary because
  1305     // Destroy(), which nulls out mTreeManager, could be called concurrently.
  1306     APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
  1307     if (treeManagerLocal) {
  1308       // APZC is holding mMonitor, so directly calling HandleFlingOverscroll()
  1309       // (which acquires the tree lock) would violate the lock ordering. Instead
  1310       // we schedule HandleFlingOverscroll() to be called after mMonitor is
  1311       // released.
  1312       mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
  1313                                               &APZCTreeManager::HandOffFling,
  1314                                               &mApzc,
  1315                                               velocity));
  1319   return true;
  1322 void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
  1324   ReentrantMonitorAutoEnter lock(mMonitor);
  1325   mAnimation = aAnimation;
  1326   mLastSampleTime = GetFrameTime();
  1327   ScheduleComposite();
  1330 void AsyncPanZoomController::CancelAnimation() {
  1331   ReentrantMonitorAutoEnter lock(mMonitor);
  1332   SetState(NOTHING);
  1333   mAnimation = nullptr;
  1336 void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
  1337   mCompositorParent = aCompositorParent;
  1340 void AsyncPanZoomController::SetCrossProcessCompositorParent(PCompositorParent* aCrossProcessCompositorParent) {
  1341   mCrossProcessCompositorParent = aCrossProcessCompositorParent;
  1344 void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
  1345   mFrameMetrics.ScrollBy(aOffset);
  1348 void AsyncPanZoomController::ScaleWithFocus(float aScale,
  1349                                             const CSSPoint& aFocus) {
  1350   mFrameMetrics.ZoomBy(aScale);
  1351   // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
  1352   // at the same position on the screen before and after the change in zoom. The below code
  1353   // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
  1354   // in-depth explanation of how.
  1355   mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
  1358 /**
  1359  * Enlarges the displayport along both axes based on the velocity.
  1360  */
  1361 static CSSSize
  1362 CalculateDisplayPortSize(const CSSSize& aCompositionSize,
  1363                          const CSSPoint& aVelocity)
  1365   float xMultiplier = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed()
  1366                         ? gfxPrefs::APZXStationarySizeMultiplier()
  1367                         : gfxPrefs::APZXSkateSizeMultiplier();
  1368   float yMultiplier = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed()
  1369                         ? gfxPrefs::APZYStationarySizeMultiplier()
  1370                         : gfxPrefs::APZYSkateSizeMultiplier();
  1371   return CSSSize(aCompositionSize.width * xMultiplier,
  1372                  aCompositionSize.height * yMultiplier);
  1375 /**
  1376  * Attempts to redistribute any area in the displayport that would get clipped
  1377  * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
  1378  * other axis, while maintaining total displayport area.
  1379  */
  1380 static void
  1381 RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
  1382                               const CSSRect& aScrollableRect)
  1384   float xSlack = std::max(0.0f, aDisplayPortSize.width - aScrollableRect.width);
  1385   float ySlack = std::max(0.0f, aDisplayPortSize.height - aScrollableRect.height);
  1387   if (ySlack > 0) {
  1388     // Reassign wasted y-axis displayport to the x-axis
  1389     aDisplayPortSize.height -= ySlack;
  1390     float xExtra = ySlack * aDisplayPortSize.width / aDisplayPortSize.height;
  1391     aDisplayPortSize.width += xExtra;
  1392   } else if (xSlack > 0) {
  1393     // Reassign wasted x-axis displayport to the y-axis
  1394     aDisplayPortSize.width -= xSlack;
  1395     float yExtra = xSlack * aDisplayPortSize.height / aDisplayPortSize.width;
  1396     aDisplayPortSize.height += yExtra;
  1400 /* static */
  1401 const LayerMargin AsyncPanZoomController::CalculatePendingDisplayPort(
  1402   const FrameMetrics& aFrameMetrics,
  1403   const ScreenPoint& aVelocity,
  1404   double aEstimatedPaintDuration)
  1406   CSSSize compositionBounds = aFrameMetrics.CalculateCompositedSizeInCssPixels();
  1407   CSSSize compositionSize = aFrameMetrics.GetRootCompositionSize();
  1408   compositionSize =
  1409     CSSSize(std::min(compositionBounds.width, compositionSize.width),
  1410             std::min(compositionBounds.height, compositionSize.height));
  1411   CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
  1412   CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
  1413   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
  1415   // Calculate the displayport size based on how fast we're moving along each axis.
  1416   CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
  1418   if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
  1419     RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
  1422   // Offset the displayport, depending on how fast we're moving and the
  1423   // estimated time it takes to paint, to try to minimise checkerboarding.
  1424   float estimatedPaintDurationMillis = (float)(aEstimatedPaintDuration * 1000.0);
  1425   float paintFactor = (gfxPrefs::APZUsePaintDuration() ? estimatedPaintDurationMillis : 50.0f);
  1426   CSSRect displayPort = CSSRect(scrollOffset + (velocity * paintFactor * gfxPrefs::APZVelocityBias()),
  1427                                 displayPortSize);
  1429   // Re-center the displayport based on its expansion over the composition size.
  1430   displayPort.MoveBy((compositionSize.width - displayPort.width)/2.0f,
  1431                      (compositionSize.height - displayPort.height)/2.0f);
  1433   // Make sure the displayport remains within the scrollable rect.
  1434   displayPort = displayPort.ForceInside(scrollableRect) - scrollOffset;
  1436   APZC_LOG_FM(aFrameMetrics,
  1437     "Calculated displayport as (%f %f %f %f) from velocity (%f %f) paint time %f metrics",
  1438     displayPort.x, displayPort.y, displayPort.width, displayPort.height,
  1439     aVelocity.x, aVelocity.y, (float)estimatedPaintDurationMillis);
  1441   CSSMargin cssMargins;
  1442   cssMargins.left = -displayPort.x;
  1443   cssMargins.top = -displayPort.y;
  1444   cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
  1445   cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
  1447   LayerMargin layerMargins = cssMargins * aFrameMetrics.LayersPixelsPerCSSPixel();
  1449   return layerMargins;
  1452 void AsyncPanZoomController::ScheduleComposite() {
  1453   if (mCompositorParent) {
  1454     mCompositorParent->ScheduleRenderOnCompositorThread();
  1458 void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
  1459   ReentrantMonitorAutoEnter lock(mMonitor);
  1460   RequestContentRepaint();
  1461   UpdateSharedCompositorFrameMetrics();
  1464 bool AsyncPanZoomController::IsPannable() const {
  1465   ReentrantMonitorAutoEnter lock(mMonitor);
  1466   return mX.HasRoomToPan() || mY.HasRoomToPan();
  1469 void AsyncPanZoomController::RequestContentRepaint() {
  1470   RequestContentRepaint(mFrameMetrics);
  1473 void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
  1474   aFrameMetrics.SetDisplayPortMargins(
  1475     CalculatePendingDisplayPort(aFrameMetrics,
  1476                                 GetVelocityVector(),
  1477                                 mPaintThrottler.AverageDuration().ToSeconds()));
  1478   aFrameMetrics.SetUseDisplayPortMargins();
  1480   // If we're trying to paint what we already think is painted, discard this
  1481   // request since it's a pointless paint.
  1482   LayerMargin marginDelta = mLastPaintRequestMetrics.GetDisplayPortMargins()
  1483                           - aFrameMetrics.GetDisplayPortMargins();
  1484   if (fabsf(marginDelta.left) < EPSILON &&
  1485       fabsf(marginDelta.top) < EPSILON &&
  1486       fabsf(marginDelta.right) < EPSILON &&
  1487       fabsf(marginDelta.bottom) < EPSILON &&
  1488       fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
  1489             aFrameMetrics.GetScrollOffset().x) < EPSILON &&
  1490       fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
  1491             aFrameMetrics.GetScrollOffset().y) < EPSILON &&
  1492       aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
  1493       fabsf(aFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON &&
  1494       fabsf(aFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) {
  1495     return;
  1498   SendAsyncScrollEvent();
  1499   mPaintThrottler.PostTask(
  1500     FROM_HERE,
  1501     NewRunnableMethod(this,
  1502                       &AsyncPanZoomController::DispatchRepaintRequest,
  1503                       aFrameMetrics),
  1504     GetFrameTime());
  1506   aFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
  1507   mLastPaintRequestMetrics = aFrameMetrics;
  1510 /*static*/ CSSRect
  1511 GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
  1513   // This computation is based on what happens in CalculatePendingDisplayPort. If that
  1514   // changes then this might need to change too
  1515   CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
  1516                    CSSSize(std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().width,
  1517                                     aFrameMetrics.GetRootCompositionSize().width),
  1518                            std::min(aFrameMetrics.CalculateCompositedSizeInCssPixels().height,
  1519                                     aFrameMetrics.GetRootCompositionSize().height)));
  1520   baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.LayersPixelsPerCSSPixel());
  1521   return baseRect;
  1524 void
  1525 AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
  1526   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1527   if (controller) {
  1528     APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
  1529     LogRendertraceRect(GetGuid(), "requested displayport", "yellow", GetDisplayPortRect(aFrameMetrics));
  1531     controller->RequestContentRepaint(aFrameMetrics);
  1532     mLastDispatchedPaintMetrics = aFrameMetrics;
  1536 void
  1537 AsyncPanZoomController::FireAsyncScrollOnTimeout()
  1539   if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  1540     ReentrantMonitorAutoEnter lock(mMonitor);
  1541     SendAsyncScrollEvent();
  1543   mAsyncScrollTimeoutTask = nullptr;
  1546 bool ZoomAnimation::Sample(FrameMetrics& aFrameMetrics,
  1547                            const TimeDuration& aDelta) {
  1548   mDuration += aDelta;
  1549   double animPosition = mDuration / ZOOM_TO_DURATION;
  1551   if (animPosition >= 1.0) {
  1552     aFrameMetrics.SetZoom(mEndZoom);
  1553     aFrameMetrics.SetScrollOffset(mEndOffset);
  1554     return false;
  1557   // Sample the zoom at the current time point.  The sampled zoom
  1558   // will affect the final computed resolution.
  1559   double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
  1561   // We scale the scrollOffset linearly with sampledPosition, so the zoom
  1562   // needs to scale inversely to match.
  1563   aFrameMetrics.SetZoom(CSSToScreenScale(1 /
  1564     (sampledPosition / mEndZoom.scale +
  1565     (1 - sampledPosition) / mStartZoom.scale)));
  1567   aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
  1568     mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
  1569     mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
  1570   )));
  1572   return true;
  1575 bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime)
  1577   if (mAnimation) {
  1578     if (mAnimation->Sample(mFrameMetrics, aSampleTime - mLastSampleTime)) {
  1579       if (mPaintThrottler.TimeSinceLastRequest(aSampleTime) >
  1580           mAnimation->mRepaintInterval) {
  1581         RequestContentRepaint();
  1583     } else {
  1584       mAnimation = nullptr;
  1585       SetState(NOTHING);
  1586       SendAsyncScrollEvent();
  1587       RequestContentRepaint();
  1589     UpdateSharedCompositorFrameMetrics();
  1590     mLastSampleTime = aSampleTime;
  1591     return true;
  1593   return false;
  1596 bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
  1597                                                             ViewTransform* aNewTransform,
  1598                                                             ScreenPoint& aScrollOffset) {
  1599   // The eventual return value of this function. The compositor needs to know
  1600   // whether or not to advance by a frame as soon as it can. For example, if a
  1601   // fling is happening, it has to keep compositing so that the animation is
  1602   // smooth. If an animation frame is requested, it is the compositor's
  1603   // responsibility to schedule a composite.
  1604   bool requestAnimationFrame = false;
  1605   Vector<Task*> deferredTasks;
  1608     ReentrantMonitorAutoEnter lock(mMonitor);
  1610     requestAnimationFrame = UpdateAnimation(aSampleTime);
  1612     aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
  1613     *aNewTransform = GetCurrentAsyncTransform();
  1615     LogRendertraceRect(GetGuid(), "viewport", "red",
  1616       CSSRect(mFrameMetrics.GetScrollOffset(),
  1617               ParentLayerSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.GetZoomToParent()));
  1619     mCurrentAsyncScrollOffset = mFrameMetrics.GetScrollOffset();
  1621     // Get any deferred tasks queued up by mAnimation's Sample() (called by
  1622     // UpdateAnimation()). This needs to be done here since mAnimation can
  1623     // be destroyed by another thread when we release the monitor, but
  1624     // the tasks need to be executed after we release the monitor since they
  1625     // are allowed to call APZCTreeManager methods which can grab the tree lock. 
  1626     if (mAnimation) {
  1627       deferredTasks = mAnimation->TakeDeferredTasks();
  1631   for (uint32_t i = 0; i < deferredTasks.length(); ++i) {
  1632     deferredTasks[i]->Run();
  1633     delete deferredTasks[i];
  1636   // Cancel the mAsyncScrollTimeoutTask because we will fire a
  1637   // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
  1638   if (mAsyncScrollTimeoutTask) {
  1639     mAsyncScrollTimeoutTask->Cancel();
  1640     mAsyncScrollTimeoutTask = nullptr;
  1642   // Fire the mozbrowserasyncscroll event immediately if it's been
  1643   // sAsyncScrollThrottleTime ms since the last time we fired the event and the
  1644   // current scroll offset is different than the mLastAsyncScrollOffset we sent
  1645   // with the last event.
  1646   // Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
  1647   TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
  1648   if (delta.ToMilliseconds() > gfxPrefs::APZAsyncScrollThrottleTime() &&
  1649       mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  1650     ReentrantMonitorAutoEnter lock(mMonitor);
  1651     mLastAsyncScrollTime = aSampleTime;
  1652     mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
  1653     SendAsyncScrollEvent();
  1655   else {
  1656     mAsyncScrollTimeoutTask =
  1657       NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
  1658     MessageLoop::current()->PostDelayedTask(FROM_HERE,
  1659                                             mAsyncScrollTimeoutTask,
  1660                                             gfxPrefs::APZAsyncScrollTimeout());
  1663   return requestAnimationFrame;
  1666 ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
  1667   ReentrantMonitorAutoEnter lock(mMonitor);
  1669   CSSPoint lastPaintScrollOffset;
  1670   if (mLastContentPaintMetrics.IsScrollable()) {
  1671     lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
  1674   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
  1675     mTestAsyncScrollOffset;
  1677   // If checkerboarding has been disallowed, clamp the scroll position to stay
  1678   // within rendered content.
  1679   if (!gfxPrefs::APZAllowCheckerboarding() &&
  1680       !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
  1681     CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
  1682     CSSPoint maxScrollOffset = lastPaintScrollOffset +
  1683       CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedSize.width,
  1684                mLastContentPaintMetrics.mDisplayPort.YMost() - compositedSize.height);
  1685     CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
  1687     if (minScrollOffset.x < maxScrollOffset.x) {
  1688       currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
  1690     if (minScrollOffset.y < maxScrollOffset.y) {
  1691       currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
  1695   LayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
  1696                          * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
  1698   return ViewTransform(-translation,
  1699                        mFrameMetrics.GetZoom()
  1700                      / mLastContentPaintMetrics.mDevPixelsPerCSSPixel
  1701                      / mFrameMetrics.GetParentResolution());
  1704 gfx3DMatrix AsyncPanZoomController::GetNontransientAsyncTransform() {
  1705   ReentrantMonitorAutoEnter lock(mMonitor);
  1706   return gfx3DMatrix::ScalingMatrix(mLastContentPaintMetrics.mResolution.scale,
  1707                                     mLastContentPaintMetrics.mResolution.scale,
  1708                                     1.0f);
  1711 gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
  1712   ReentrantMonitorAutoEnter lock(mMonitor);
  1713   LayerPoint scrollChange = (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
  1714                           * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
  1715   float zoomChange = mLastContentPaintMetrics.GetZoom().scale / mLastDispatchedPaintMetrics.GetZoom().scale;
  1716   return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
  1717          gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
  1720 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
  1721   ReentrantMonitorAutoEnter lock(mMonitor);
  1723   mLastContentPaintMetrics = aLayerMetrics;
  1724   UpdateTransformScale();
  1726   bool isDefault = mFrameMetrics.IsDefault();
  1727   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
  1728   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
  1730   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
  1731   LogRendertraceRect(GetGuid(), "painted displayport", "green",
  1732     aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
  1734   mPaintThrottler.TaskComplete(GetFrameTime());
  1735   bool needContentRepaint = false;
  1736   if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
  1737       aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
  1738     // Remote content has sync'd up to the composition geometry
  1739     // change, so we can accept the viewport it's calculated.
  1740     if (mFrameMetrics.mViewport.width != aLayerMetrics.mViewport.width ||
  1741         mFrameMetrics.mViewport.height != aLayerMetrics.mViewport.height) {
  1742       needContentRepaint = true;
  1744     mFrameMetrics.mViewport = aLayerMetrics.mViewport;
  1747   // If the layers update was not triggered by our own repaint request, then
  1748   // we want to take the new scroll offset. Check the scroll generation as well
  1749   // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
  1750   // update message.
  1751   bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
  1752         && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
  1754   if (aIsFirstPaint || isDefault) {
  1755     // Initialize our internal state to something sane when the content
  1756     // that was just painted is something we knew nothing about previously
  1757     mPaintThrottler.ClearHistory();
  1758     mPaintThrottler.SetMaxDurations(gfxPrefs::APZNumPaintDurationSamples());
  1760     mX.CancelTouch();
  1761     mY.CancelTouch();
  1762     SetState(NOTHING);
  1764     mFrameMetrics = aLayerMetrics;
  1765     mLastDispatchedPaintMetrics = aLayerMetrics;
  1766     ShareCompositorFrameMetrics();
  1767   } else {
  1768     // If we're not taking the aLayerMetrics wholesale we still need to pull
  1769     // in some things into our local mFrameMetrics because these things are
  1770     // determined by Gecko and our copy in mFrameMetrics may be stale.
  1772     if (mFrameMetrics.mCompositionBounds.width == aLayerMetrics.mCompositionBounds.width &&
  1773         mFrameMetrics.mDevPixelsPerCSSPixel == aLayerMetrics.mDevPixelsPerCSSPixel) {
  1774       float parentResolutionChange = aLayerMetrics.GetParentResolution().scale
  1775                                    / mFrameMetrics.GetParentResolution().scale;
  1776       mFrameMetrics.ZoomBy(parentResolutionChange);
  1777     } else {
  1778       // Take the new zoom as either device scale or composition width or both
  1779       // got changed (e.g. due to orientation change).
  1780       mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
  1781       mFrameMetrics.mDevPixelsPerCSSPixel.scale = aLayerMetrics.mDevPixelsPerCSSPixel.scale;
  1783     mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
  1784     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
  1785     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
  1786     mFrameMetrics.mResolution = aLayerMetrics.mResolution;
  1787     mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
  1788     mFrameMetrics.mHasScrollgrab = aLayerMetrics.mHasScrollgrab;
  1790     if (scrollOffsetUpdated) {
  1791       APZC_LOG("%p updating scroll offset from (%f, %f) to (%f, %f)\n", this,
  1792         mFrameMetrics.GetScrollOffset().x, mFrameMetrics.GetScrollOffset().y,
  1793         aLayerMetrics.GetScrollOffset().x, aLayerMetrics.GetScrollOffset().y);
  1795       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
  1797       // Because of the scroll offset update, any inflight paint requests are
  1798       // going to be ignored by layout, and so mLastDispatchedPaintMetrics
  1799       // becomes incorrect for the purposes of calculating the LD transform. To
  1800       // correct this we need to update mLastDispatchedPaintMetrics to be the
  1801       // last thing we know was painted by Gecko.
  1802       mLastDispatchedPaintMetrics = aLayerMetrics;
  1806   if (scrollOffsetUpdated) {
  1807     // Once layout issues a scroll offset update, it becomes impervious to
  1808     // scroll offset updates from APZ until we acknowledge the update it sent.
  1809     // This prevents APZ updates from clobbering scroll updates from other
  1810     // more "legitimate" sources like content scripts.
  1811     nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1812     if (controller) {
  1813       APZC_LOG("%p sending scroll update acknowledgement with gen %lu\n", this, aLayerMetrics.GetScrollGeneration());
  1814       controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
  1815                                           aLayerMetrics.GetScrollGeneration());
  1819   if (needContentRepaint) {
  1820     RequestContentRepaint();
  1822   UpdateSharedCompositorFrameMetrics();
  1825 const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
  1826   mMonitor.AssertCurrentThreadIn();
  1827   return mFrameMetrics;
  1830 void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
  1831   if (!aRect.IsFinite()) {
  1832     NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...\n");
  1833     return;
  1836   SetState(ANIMATING_ZOOM);
  1839     ReentrantMonitorAutoEnter lock(mMonitor);
  1841     ParentLayerIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
  1842     CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
  1843     CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
  1844     CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoomToParent();
  1845     CSSToParentLayerScale targetZoom;
  1847     // The minimum zoom to prevent over-zoom-out.
  1848     // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
  1849     // then the CSS content rect, in layers pixels, will be smaller than the
  1850     // composition bounds. If this happens, we can't fill the target composited
  1851     // area with this frame.
  1852     CSSToParentLayerScale localMinZoom(std::max((mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale).scale,
  1853                                        std::max(compositionBounds.width / cssPageRect.width,
  1854                                                 compositionBounds.height / cssPageRect.height)));
  1855     CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
  1857     if (!aRect.IsEmpty()) {
  1858       // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
  1859       aRect = aRect.Intersect(cssPageRect);
  1860       targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  1861                                                   compositionBounds.height / aRect.height));
  1863     // 1. If the rect is empty, request received from browserElementScrolling.js
  1864     // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
  1865     // 3. currentZoom is equal to localMinZoom and user still double-tapping it
  1866     // Treat these three cases as a request to zoom out as much as possible.
  1867     if (aRect.IsEmpty() ||
  1868         (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
  1869         (currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
  1870       CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
  1871       float y = scrollOffset.y;
  1872       float newHeight =
  1873         cssPageRect.width * (compositedSize.height / compositedSize.width);
  1874       float dh = compositedSize.height - newHeight;
  1876       aRect = CSSRect(0.0f,
  1877                       y + dh/2,
  1878                       cssPageRect.width,
  1879                       newHeight);
  1880       aRect = aRect.Intersect(cssPageRect);
  1881       targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  1882                                                   compositionBounds.height / aRect.height));
  1885     targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
  1886     FrameMetrics endZoomToMetrics = mFrameMetrics;
  1887     endZoomToMetrics.SetZoom(targetZoom / mFrameMetrics.mTransformScale);
  1889     // Adjust the zoomToRect to a sensible position to prevent overscrolling.
  1890     CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
  1892     // If either of these conditions are met, the page will be
  1893     // overscrolled after zoomed
  1894     if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
  1895       aRect.y = cssPageRect.height - sizeAfterZoom.height;
  1896       aRect.y = aRect.y > 0 ? aRect.y : 0;
  1898     if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
  1899       aRect.x = cssPageRect.width - sizeAfterZoom.width;
  1900       aRect.x = aRect.x > 0 ? aRect.x : 0;
  1903     endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
  1904     endZoomToMetrics.SetDisplayPortMargins(
  1905       CalculatePendingDisplayPort(endZoomToMetrics,
  1906                                   ScreenPoint(0,0),
  1907                                   0));
  1908     endZoomToMetrics.SetUseDisplayPortMargins();
  1910     StartAnimation(new ZoomAnimation(
  1911         mFrameMetrics.GetScrollOffset(),
  1912         mFrameMetrics.GetZoom(),
  1913         endZoomToMetrics.GetScrollOffset(),
  1914         endZoomToMetrics.GetZoom()));
  1916     // Schedule a repaint now, so the new displayport will be painted before the
  1917     // animation finishes.
  1918     RequestContentRepaint(endZoomToMetrics);
  1922 void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
  1923   mTouchBlockState.mPreventDefaultSet = true;
  1924   mTouchBlockState.mPreventDefault = aPreventDefault;
  1925   CheckContentResponse();
  1928 void AsyncPanZoomController::CheckContentResponse() {
  1929   bool canProceedToTouchState = true;
  1931   if (mFrameMetrics.mMayHaveTouchListeners) {
  1932     canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
  1935   if (mTouchActionPropertyEnabled) {
  1936     canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
  1939   if (!canProceedToTouchState) {
  1940     return;
  1943   if (mContentResponseTimeoutTask) {
  1944     mContentResponseTimeoutTask->Cancel();
  1945     mContentResponseTimeoutTask = nullptr;
  1948   if (mState == WAITING_CONTENT_RESPONSE) {
  1949     if (!mTouchBlockState.mPreventDefault) {
  1950       SetState(NOTHING);
  1953     mHandlingTouchQueue = true;
  1955     while (!mTouchQueue.IsEmpty()) {
  1956       if (!mTouchBlockState.mPreventDefault) {
  1957         HandleInputEvent(mTouchQueue[0]);
  1960       if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_END ||
  1961           mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_CANCEL) {
  1962         mTouchQueue.RemoveElementAt(0);
  1963         break;
  1966       mTouchQueue.RemoveElementAt(0);
  1969     mHandlingTouchQueue = false;
  1973 bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
  1974   if (!mTouchActionPropertyEnabled) {
  1975     return true;
  1977   // Pointer events specification implies all touch points to allow zoom
  1978   // to perform it.
  1979   for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
  1980     if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
  1981       return false;
  1984   return true;
  1987 bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
  1988   if (!mTouchActionPropertyEnabled) {
  1989     return true;
  1991   for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
  1992     if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
  1993       return false;
  1996   return true;
  1999 AsyncPanZoomController::TouchBehaviorFlags
  2000 AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
  2001   if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
  2002     return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
  2004   return DefaultTouchBehavior;
  2007 AsyncPanZoomController::TouchBehaviorFlags
  2008 AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
  2009   // Here we need to perform a hit testing over the touch-action regions attached to the
  2010   // layer associated with current apzc.
  2011   // Currently they are in progress, for more info see bug 928833.
  2012   return AllowedTouchBehavior::UNKNOWN;
  2015 void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
  2016   mTouchBlockState.mAllowedTouchBehaviors.Clear();
  2017   mTouchBlockState.mAllowedTouchBehaviors.AppendElements(aBehaviors);
  2018   mTouchBlockState.mAllowedTouchBehaviorSet = true;
  2019   CheckContentResponse();
  2022 void AsyncPanZoomController::SetState(PanZoomState aNewState) {
  2024   PanZoomState oldState;
  2026   // Intentional scoping for mutex
  2028     ReentrantMonitorAutoEnter lock(mMonitor);
  2029     oldState = mState;
  2030     mState = aNewState;
  2033   if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  2034     if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
  2035       controller->NotifyAPZStateChange(
  2036           GetGuid(), APZStateChange::TransformBegin);
  2037     } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
  2038       controller->NotifyAPZStateChange(
  2039           GetGuid(), APZStateChange::TransformEnd);
  2044 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
  2045   return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
  2048 bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
  2049   return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
  2052 void AsyncPanZoomController::SetContentResponseTimer() {
  2053   if (!mContentResponseTimeoutTask) {
  2054     mContentResponseTimeoutTask =
  2055       NewRunnableMethod(this, &AsyncPanZoomController::TimeoutContentResponse);
  2057     PostDelayedTask(mContentResponseTimeoutTask, gfxPrefs::APZContentResponseTimeout());
  2061 void AsyncPanZoomController::TimeoutContentResponse() {
  2062   mContentResponseTimeoutTask = nullptr;
  2063   ContentReceivedTouch(false);
  2066 void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
  2067   APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
  2068     aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
  2069   if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
  2070     NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
  2071     return;
  2073   // inf float values and other bad cases should be sanitized by the code below.
  2074   mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
  2075   mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
  2076   mZoomConstraints.mMinZoom = (MIN_ZOOM > aConstraints.mMinZoom ? MIN_ZOOM : aConstraints.mMinZoom);
  2077   mZoomConstraints.mMaxZoom = (MAX_ZOOM > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : MAX_ZOOM);
  2078   if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
  2079     mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
  2083 ZoomConstraints
  2084 AsyncPanZoomController::GetZoomConstraints() const
  2086   return mZoomConstraints;
  2090 void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
  2091   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  2092   if (controller) {
  2093     controller->PostDelayedTask(aTask, aDelayMs);
  2097 void AsyncPanZoomController::SendAsyncScrollEvent() {
  2098   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  2099   if (!controller) {
  2100     return;
  2103   bool isRoot;
  2104   CSSRect contentRect;
  2105   CSSSize scrollableSize;
  2107     ReentrantMonitorAutoEnter lock(mMonitor);
  2109     isRoot = mFrameMetrics.mIsRoot;
  2110     scrollableSize = mFrameMetrics.mScrollableRect.Size();
  2111     contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
  2112     contentRect.MoveTo(mCurrentAsyncScrollOffset);
  2115   controller->SendAsyncScrollDOMEvent(isRoot, contentRect, scrollableSize);
  2118 bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
  2120   return aGuid == GetGuid();
  2123 void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut)
  2125   if (aGuidOut) {
  2126     *aGuidOut = GetGuid();
  2130 ScrollableLayerGuid AsyncPanZoomController::GetGuid()
  2132   return ScrollableLayerGuid(mLayersId, mFrameMetrics);
  2135 void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
  2137   mMonitor.AssertCurrentThreadIn();
  2139   FrameMetrics* frame = mSharedFrameMetricsBuffer ?
  2140       static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
  2142   if (frame && mSharedLock && gfxPrefs::UseProgressiveTilePainting()) {
  2143     mSharedLock->Lock();
  2144     *frame = mFrameMetrics;
  2145     mSharedLock->Unlock();
  2149 void AsyncPanZoomController::ShareCompositorFrameMetrics() {
  2151   PCompositorParent* compositor =
  2152     (mCrossProcessCompositorParent ? mCrossProcessCompositorParent : mCompositorParent.get());
  2154   // Only create the shared memory buffer if it hasn't already been created,
  2155   // we are using progressive tile painting, and we have a
  2156   // compositor to pass the shared memory back to the content process/thread.
  2157   if (!mSharedFrameMetricsBuffer && compositor && gfxPrefs::UseProgressiveTilePainting()) {
  2159     // Create shared memory and initialize it with the current FrameMetrics value
  2160     mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
  2161     FrameMetrics* frame = nullptr;
  2162     mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
  2163     mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
  2164     frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
  2166     if (frame) {
  2168       { // scope the monitor, only needed to copy the FrameMetrics.
  2169         ReentrantMonitorAutoEnter lock(mMonitor);
  2170         *frame = mFrameMetrics;
  2173       // Get the process id of the content process
  2174       base::ProcessHandle processHandle = compositor->OtherProcess();
  2175       ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
  2177       // Get the shared memory handle to share with the content process
  2178       mSharedFrameMetricsBuffer->ShareToProcess(processHandle, &mem);
  2180       // Get the cross process mutex handle to share with the content process
  2181       mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
  2182       CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(processHandle);
  2184       // Send the shared memory handle and cross process handle to the content
  2185       // process by an asynchronous ipc call. Include the APZC unique ID
  2186       // so the content process know which APZC sent this shared FrameMetrics.
  2187       if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mAPZCId)) {
  2188         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
  2194 ParentLayerPoint AsyncPanZoomController::ToParentLayerCoords(const ScreenPoint& aPoint)
  2196   return TransformTo<ParentLayerPixel>(GetNontransientAsyncTransform() * GetCSSTransform(), aPoint);
  2199 void AsyncPanZoomController::UpdateTransformScale()
  2201   gfx3DMatrix nontransientTransforms = GetNontransientAsyncTransform() * GetCSSTransform();
  2202   if (!FuzzyEqualsMultiplicative(nontransientTransforms.GetXScale(), nontransientTransforms.GetYScale())) {
  2203     NS_WARNING("The x- and y-scales of the nontransient transforms should be equal");
  2205   mFrameMetrics.mTransformScale.scale = nontransientTransforms.GetXScale();

mercurial