Advertisement
Guest User

Untitled

a guest
Dec 16th, 2014
233
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 126.82 KB | None | 0 0
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* vim: set sw=2 ts=8 et tw=80 : */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6.  
  7. #include <math.h> // for fabsf, fabs, atan2
  8. #include <stdint.h> // for uint32_t, uint64_t
  9. #include <sys/types.h> // for int32_t
  10. #include <algorithm> // for max, min
  11. #include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc
  12. #include "Axis.h" // for AxisX, AxisY, Axis, etc
  13. #include "Compositor.h" // for Compositor
  14. #include "FrameMetrics.h" // for FrameMetrics, etc
  15. #include "GestureEventListener.h" // for GestureEventListener
  16. #include "InputData.h" // for MultiTouchInput, etc
  17. #include "InputBlockState.h" // for InputBlockState, TouchBlockState
  18. #include "InputQueue.h" // for InputQueue
  19. #include "OverscrollHandoffState.h" // for OverscrollHandoffState
  20. #include "TaskThrottler.h" // for TaskThrottler
  21. #include "Units.h" // for CSSRect, CSSPoint, etc
  22. #include "UnitTransforms.h" // for TransformTo
  23. #include "base/message_loop.h" // for MessageLoop
  24. #include "base/task.h" // for NewRunnableMethod, etc
  25. #include "base/tracked.h" // for FROM_HERE
  26. #include "gfxPrefs.h" // for gfxPrefs
  27. #include "gfxTypes.h" // for gfxFloat
  28. #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
  29. #include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_*
  30. #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
  31. #include "mozilla/Constants.h" // for M_PI
  32. #include "mozilla/EventForwards.h" // for nsEventStatus_*
  33. #include "mozilla/Preferences.h" // for Preferences
  34. #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
  35. #include "mozilla/StaticPtr.h" // for StaticAutoPtr
  36. #include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
  37. #include "mozilla/dom/AnimationPlayer.h" // for ComputedTimingFunction
  38. #include "mozilla/dom/Touch.h" // for Touch
  39. #include "mozilla/gfx/BasePoint.h" // for BasePoint
  40. #include "mozilla/gfx/BaseRect.h" // for BaseRect
  41. #include "mozilla/gfx/Point.h" // for Point, RoundedToInt, etc
  42. #include "mozilla/gfx/Rect.h" // for RoundedIn
  43. #include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
  44. #include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid
  45. #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
  46. #include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
  47. #include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
  48. #include "mozilla/layers/CompositorParent.h" // for CompositorParent
  49. #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
  50. #include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
  51. #include "mozilla/mozalloc.h" // for operator new, etc
  52. #include "mozilla/unused.h" // for unused
  53. #include "mozilla/FloatingPoint.h" // for FuzzyEquals*
  54. #include "nsAlgorithm.h" // for clamped
  55. #include "nsAutoPtr.h" // for nsRefPtr
  56. #include "nsCOMPtr.h" // for already_AddRefed
  57. #include "nsDebug.h" // for NS_WARNING
  58. #include "nsIDOMWindowUtils.h" // for nsIDOMWindowUtils
  59. #include "nsMathUtils.h" // for NS_hypot
  60. #include "nsPoint.h" // for nsIntPoint
  61. #include "nsStyleConsts.h"
  62. #include "nsStyleStruct.h" // for nsTimingFunction
  63. #include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
  64. #include "nsThreadUtils.h" // for NS_IsMainThread
  65. #include "SharedMemoryBasic.h" // for SharedMemoryBasic
  66.  
  67. // #define APZC_ENABLE_RENDERTRACE
  68.  
  69. #define ENABLE_APZC_LOGGING 0
  70. // #define ENABLE_APZC_LOGGING 1
  71.  
  72. #if ENABLE_APZC_LOGGING
  73. # include "LayersLogging.h"
  74. # define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
  75. # define APZC_LOG_FM(fm, prefix, ...) \
  76. { std::stringstream ss; \
  77. ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
  78. AppendToString(ss, fm, ":", "", true); \
  79. APZC_LOG("%s\n", ss.str().c_str()); \
  80. }
  81. #else
  82. # define APZC_LOG(...)
  83. # define APZC_LOG_FM(fm, prefix, ...)
  84. #endif
  85.  
  86. // Static helper functions
  87. namespace {
  88.  
  89. int32_t
  90. WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
  91. {
  92. int32_t result = 0;
  93. if (aModifiers & mozilla::MODIFIER_SHIFT) {
  94. result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
  95. }
  96. if (aModifiers & mozilla::MODIFIER_CONTROL) {
  97. result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
  98. }
  99. if (aModifiers & mozilla::MODIFIER_ALT) {
  100. result |= nsIDOMWindowUtils::MODIFIER_ALT;
  101. }
  102. if (aModifiers & mozilla::MODIFIER_META) {
  103. result |= nsIDOMWindowUtils::MODIFIER_META;
  104. }
  105. if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
  106. result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
  107. }
  108. if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
  109. result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
  110. }
  111. if (aModifiers & mozilla::MODIFIER_FN) {
  112. result |= nsIDOMWindowUtils::MODIFIER_FN;
  113. }
  114. if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
  115. result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
  116. }
  117. if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
  118. result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
  119. }
  120. if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
  121. result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
  122. }
  123. if (aModifiers & mozilla::MODIFIER_OS) {
  124. result |= nsIDOMWindowUtils::MODIFIER_OS;
  125. }
  126. return result;
  127. }
  128.  
  129. }
  130.  
  131. namespace mozilla {
  132. namespace layers {
  133.  
  134. typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
  135. typedef GeckoContentController::APZStateChange APZStateChange;
  136. typedef mozilla::gfx::Point Point;
  137. typedef mozilla::gfx::Matrix4x4 Matrix4x4;
  138. using mozilla::gfx::PointTyped;
  139.  
  140. /**
  141. * \page APZCPrefs APZ preferences
  142. *
  143. * The following prefs are used to control the behaviour of the APZC.
  144. * The default values are provided in gfxPrefs.h.
  145. *
  146. * \li\b apz.allow_checkerboarding
  147. * Pref that allows or disallows checkerboarding
  148. *
  149. * \li\b apz.asyncscroll.throttle
  150. * The time period that throttles mozbrowserasyncscroll event.\n
  151. * Units: milliseconds
  152. *
  153. * \li\b apz.asyncscroll.timeout
  154. * The timeout for mAsyncScrollTimeoutTask delay task.\n
  155. * Units: milliseconds
  156. *
  157. * \li\b apz.axis_lock.mode
  158. * The preferred axis locking style. See AxisLockMode for possible values.
  159. *
  160. * \li\b apz.axis_lock.lock_angle
  161. * Angle from axis within which we stay axis-locked.\n
  162. * Units: radians
  163. *
  164. * \li\b apz.axis_lock.breakout_threshold
  165. * Distance in inches the user must pan before axis lock can be broken.\n
  166. * Units: (real-world, i.e. screen) inches
  167. *
  168. * \li\b apz.axis_lock.breakout_angle
  169. * Angle at which axis lock can be broken.\n
  170. * Units: radians
  171. *
  172. * \li\b apz.axis_lock.direct_pan_angle
  173. * If the angle from an axis to the line drawn by a pan move is less than
  174. * this value, we can assume that panning can be done in the allowed direction
  175. * (horizontal or vertical).\n
  176. * Currently used only for touch-action css property stuff and was addded to
  177. * keep behaviour consistent with IE.\n
  178. * Units: radians
  179. *
  180. * \li\b apz.content_response_timeout
  181. * Amount of time before we timeout response from content. For example, if
  182. * content is being unruly/slow and we don't get a response back within this
  183. * time, we will just pretend that content did not preventDefault any touch
  184. * events we dispatched to it.\n
  185. * Units: milliseconds
  186. *
  187. * \li\b apz.cross_slide_enabled
  188. * Pref that enables integration with the Metro "cross-slide" gesture.
  189. *
  190. * \li\b apz.danger_zone_x
  191. * \li\b apz.danger_zone_y
  192. * When drawing high-res tiles, we drop down to drawing low-res tiles
  193. * when we know we can't keep up with the scrolling. The way we determine
  194. * this is by checking if we are entering the "danger zone", which is the
  195. * boundary of the painted content. For example, if the painted content
  196. * goes from y=0...1000 and the visible portion is y=250...750 then
  197. * we're far from checkerboarding. If we get to y=490...990 though then we're
  198. * only 10 pixels away from showing checkerboarding so we are probably in
  199. * a state where we can't keep up with scrolling. The danger zone prefs specify
  200. * how wide this margin is; in the above example a y-axis danger zone of 10
  201. * pixels would make us drop to low-res at y=490...990.\n
  202. * This value is in layer pixels.
  203. *
  204. * \li\b apz.enlarge_displayport_when_clipped
  205. * Pref that enables enlarging of the displayport along one axis when the
  206. * generated displayport's size is beyond that of the scrollable rect on the
  207. * opposite axis.
  208. *
  209. * \li\b apz.fling_accel_interval_ms
  210. * The time that determines whether a second fling will be treated as
  211. * accelerated. If two flings are started within this interval, the second one
  212. * will be accelerated. Setting an interval of 0 means that acceleration will
  213. * be disabled.\n
  214. * Units: milliseconds
  215. *
  216. * \li\b apz.fling_accel_base_mult
  217. * \li\b apz.fling_accel_supplemental_mult
  218. * When applying an acceleration on a fling, the new computed velocity is
  219. * (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
  220. * The base_mult and supplemental_mult multiplier values are controlled by
  221. * these prefs. Note that "old_velocity" here is the initial velocity of the
  222. * previous fling _after_ acceleration was applied to it (if applicable).
  223. *
  224. * \li\b apz.fling_curve_function_x1
  225. * \li\b apz.fling_curve_function_y1
  226. * \li\b apz.fling_curve_function_x2
  227. * \li\b apz.fling_curve_function_y2
  228. * \li\b apz.fling_curve_threshold_inches_per_ms
  229. * These five parameters define a Bezier curve function and threshold used to
  230. * increase the actual velocity relative to the user's finger velocity. When the
  231. * finger velocity is below the threshold (or if the threshold is not positive),
  232. * the velocity is used as-is. If the finger velocity exceeds the threshold
  233. * velocity, then the function defined by the curve is applied on the part of
  234. * the velocity that exceeds the threshold. Note that the upper bound of the
  235. * velocity is still specified by the \b apz.max_velocity_inches_per_ms pref, and
  236. * the function will smoothly curve the velocity from the threshold to the
  237. * max. In general the function parameters chosen should define an ease-out
  238. * curve in order to increase the velocity in this range, or an ease-in curve to
  239. * decrease the velocity. A straight-line curve is equivalent to disabling the
  240. * curve entirely by setting the threshold to -1. The max velocity pref must
  241. * also be set in order for the curving to take effect, as it defines the upper
  242. * bound of the velocity curve.\n
  243. * The points (x1, y1) and (x2, y2) used as the two intermediate control points
  244. * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
  245. * Some example values for these prefs can be found at\n
  246. * http://mxr.mozilla.org/mozilla-central/source/layout/style/nsStyleStruct.cpp?rev=21282be9ad95#2462
  247. *
  248. * \li\b apz.fling_friction
  249. * Amount of friction applied during flings.
  250. *
  251. *
  252. * \li\b apz.fling_repaint_interval
  253. * Maximum amount of time flinging before sending a viewport change. This will
  254. * asynchronously repaint the page.\n
  255. * Units: milliseconds
  256. *
  257. * \li\b apz.fling_stop_on_tap_threshold
  258. * When flinging, if the velocity is above this number, then a tap on the
  259. * screen will stop the fling without dispatching a tap to content. If the
  260. * velocity is below this threshold a tap will also be dispatched.
  261. * Note: when modifying this pref be sure to run the APZC gtests as some of
  262. * them depend on the value of this pref.\n
  263. * Units: screen pixels per millisecond
  264. *
  265. * \li\b apz.fling_stopped_threshold
  266. * When flinging, if the velocity goes below this number, we just stop the
  267. * animation completely. This is to prevent asymptotically approaching 0
  268. * velocity and rerendering unnecessarily.\n
  269. * Units: screen pixels per millisecond
  270. *
  271. * \li\b apz.max_velocity_inches_per_ms
  272. * Maximum velocity. Velocity will be capped at this value if a faster fling
  273. * occurs. Negative values indicate unlimited velocity.\n
  274. * Units: (real-world, i.e. screen) inches per millisecond
  275. *
  276. * \li\b apz.max_velocity_queue_size
  277. * Maximum size of velocity queue. The queue contains last N velocity records.
  278. * On touch end we calculate the average velocity in order to compensate
  279. * touch/mouse drivers misbehaviour.
  280. *
  281. * \li\b apz.min_skate_speed
  282. * Minimum amount of speed along an axis before we switch to "skate" multipliers
  283. * rather than using the "stationary" multipliers.\n
  284. * Units: CSS pixels per millisecond
  285. *
  286. * \li\b apz.num_paint_duration_samples
  287. * Number of samples to store of how long it took to paint after the previous
  288. *
  289. * \li\b apz.overscroll.enabled
  290. * Pref that enables overscrolling. If this is disabled, excess scroll that
  291. * cannot be handed off is discarded.
  292. *
  293. * \li\b apz.overscroll.min_pan_distance_ratio
  294. * The minimum ratio of the pan distance along one axis to the pan distance
  295. * along the other axis needed to initiate overscroll along the first axis
  296. * during panning.
  297. *
  298. * \li\b apz.overscroll.stretch_factor
  299. * How much overscrolling can stretch content along an axis.
  300. * The maximum stretch along an axis is a factor of (1 + kStretchFactor).
  301. * (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
  302. * is 1, you can stretch at most by a factor of 2).
  303. *
  304. * \li\b apz.overscroll.spring_stiffness
  305. * The stiffness of the spring used in the physics model for the overscroll
  306. * animation.
  307. *
  308. * \li\b apz.overscroll.spring_friction
  309. * The friction of the spring used in the physics model for the overscroll
  310. * animation.
  311. * Even though a realistic physics model would dictate that this be the same
  312. * as \b apz.fling_friction, we allow it to be set to be something different,
  313. * because in practice we want flings to skate smoothly (low friction), while
  314. * we want the overscroll bounce-back to oscillate few times (high friction).
  315. *
  316. * \li\b apz.overscroll.stop_distance_threshold
  317. * \li\b apz.overscroll.stop_velocity_threshold
  318. * Thresholds for stopping the overscroll animation. When both the distance
  319. * and the velocity fall below their thresholds, we stop oscillating.\n
  320. * Units: screen pixels (for distance)
  321. * screen pixels per millisecond (for velocity)
  322. *
  323. * \li\b apz.pan_repaint_interval
  324. * Maximum amount of time while panning before sending a viewport change. This
  325. * will asynchronously repaint the page. It is also forced when panning stops.
  326. *
  327. * \li\b apz.smooth_scroll_repaint_interval
  328. * Maximum amount of time doing a smooth scroll before sending a viewport
  329. * change. This will asynchronously repaint the page.\n
  330. * Units: milliseconds
  331. *
  332. * \li\b apz.test.logging_enabled
  333. * Enable logging of APZ test data (see bug 961289).
  334. *
  335. * \li\b apz.touch_start_tolerance
  336. * Constant describing the tolerance in distance we use, multiplied by the
  337. * device DPI, before we start panning the screen. This is to prevent us from
  338. * accidentally processing taps as touch moves, and from very short/accidental
  339. * touches moving the screen.\n
  340. * Units: (real-world, i.e. screen) inches
  341. *
  342. * \li\b apz.use_paint_duration
  343. * Whether or not to use the estimated paint duration as a factor when projecting
  344. * the displayport in the direction of scrolling. If this value is set to false,
  345. * a constant 50ms paint time is used; the projection can be scaled as desired
  346. * using the \b apz.velocity_bias pref below.
  347. *
  348. * \li\b apz.velocity_bias
  349. * How much to adjust the displayport in the direction of scrolling. This value
  350. * is multiplied by the velocity and added to the displayport offset.
  351. *
  352. * \li\b apz.velocity_relevance_time_ms
  353. * When computing a fling velocity from the most recently stored velocity
  354. * information, only velocities within the most X milliseconds are used.
  355. * This pref controls the value of X.\n
  356. * Units: ms
  357. *
  358. * \li\b apz.x_skate_size_multiplier
  359. * \li\b apz.y_skate_size_multiplier
  360. * The multiplier we apply to the displayport size if it is skating (current
  361. * velocity is above \b apz.min_skate_speed). We prefer to increase the size of
  362. * the Y axis because it is more natural in the case that a user is reading a
  363. * page page that scrolls up/down. Note that one, both or neither of these may be
  364. * used at any instant.\n
  365. * In general we want \b apz.[xy]_skate_size_multiplier to be smaller than the corresponding
  366. * stationary size multiplier because when panning fast we would like to paint
  367. * less and get faster, more predictable paint times. When panning slowly we
  368. * can afford to paint more even though it's slower.
  369. *
  370. * \li\b apz.x_stationary_size_multiplier
  371. * \li\b apz.y_stationary_size_multiplier
  372. * The multiplier we apply to the displayport size if it is not skating (see
  373. * documentation for the skate size multipliers above).
  374. *
  375. * \li\b apz.zoom_animation_duration_ms
  376. * This controls how long the zoom-to-rect animation takes.\n
  377. * Units: ms
  378. */
  379.  
  380. /**
  381. * Computed time function used for sampling frames of a zoom to animation.
  382. */
  383. StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
  384.  
  385. /**
  386. * Computed time function used for curving up velocity when it gets high.
  387. */
  388. StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
  389.  
  390. /**
  391. * Maximum zoom amount, always used, even if a page asks for higher.
  392. */
  393. static const CSSToParentLayerScale MAX_ZOOM(8.0f);
  394.  
  395. /**
  396. * Minimum zoom amount, always used, even if a page asks for lower.
  397. */
  398. static const CSSToParentLayerScale MIN_ZOOM(0.125f);
  399.  
  400. /**
  401. * Is aAngle within the given threshold of the horizontal axis?
  402. * @param aAngle an angle in radians in the range [0, pi]
  403. * @param aThreshold an angle in radians in the range [0, pi/2]
  404. */
  405. static bool IsCloseToHorizontal(float aAngle, float aThreshold)
  406. {
  407. return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
  408. }
  409.  
  410. // As above, but for the vertical axis.
  411. static bool IsCloseToVertical(float aAngle, float aThreshold)
  412. {
  413. return (fabs(aAngle - (M_PI / 2)) < aThreshold);
  414. }
  415.  
  416. template <typename Units>
  417. static bool IsZero(const gfx::PointTyped<Units>& aPoint)
  418. {
  419. return FuzzyEqualsMultiplicative(aPoint.x, 0.0f)
  420. && FuzzyEqualsMultiplicative(aPoint.y, 0.0f);
  421. }
  422.  
  423. static inline void LogRendertraceRect(const ScrollableLayerGuid& aGuid, const char* aDesc, const char* aColor, const CSSRect& aRect)
  424. {
  425. #ifdef APZC_ENABLE_RENDERTRACE
  426. static const TimeStamp sRenderStart = TimeStamp::Now();
  427. TimeDuration delta = TimeStamp::Now() - sRenderStart;
  428. printf_stderr("(%llu,%lu,%llu)%s RENDERTRACE %f rect %s %f %f %f %f\n",
  429. aGuid.mLayersId, aGuid.mPresShellId, aGuid.mScrollId,
  430. aDesc, delta.ToMilliseconds(), aColor,
  431. aRect.x, aRect.y, aRect.width, aRect.height);
  432. #endif
  433. }
  434.  
  435. static TimeStamp sFrameTime;
  436. static bool sThreadAssertionsEnabled = true;
  437. static PRThread* sControllerThread;
  438.  
  439. // Counter used to give each APZC a unique id
  440. static uint32_t sAsyncPanZoomControllerCount = 0;
  441.  
  442. static TimeStamp
  443. GetFrameTime() {
  444. if (sFrameTime.IsNull()) {
  445. return TimeStamp::Now();
  446. }
  447. return sFrameTime;
  448. }
  449.  
  450. class MOZ_STACK_CLASS StateChangeNotificationBlocker {
  451. public:
  452. explicit StateChangeNotificationBlocker(AsyncPanZoomController* aApzc)
  453. : mApzc(aApzc)
  454. {
  455. ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
  456. mInitialState = mApzc->mState;
  457. mApzc->mNotificationBlockers++;
  458. }
  459.  
  460. ~StateChangeNotificationBlocker()
  461. {
  462. AsyncPanZoomController::PanZoomState newState;
  463. {
  464. ReentrantMonitorAutoEnter lock(mApzc->mMonitor);
  465. mApzc->mNotificationBlockers--;
  466. newState = mApzc->mState;
  467. }
  468. mApzc->DispatchStateChangeNotification(mInitialState, newState);
  469. }
  470.  
  471. private:
  472. AsyncPanZoomController* mApzc;
  473. AsyncPanZoomController::PanZoomState mInitialState;
  474. };
  475.  
  476. class FlingAnimation: public AsyncPanZoomAnimation {
  477. public:
  478. FlingAnimation(AsyncPanZoomController& aApzc,
  479. const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
  480. bool aApplyAcceleration)
  481. : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
  482. , mApzc(aApzc)
  483. , mOverscrollHandoffChain(aOverscrollHandoffChain)
  484. {
  485. MOZ_ASSERT(mOverscrollHandoffChain);
  486. TimeStamp now = GetFrameTime();
  487.  
  488. // Drop any velocity on axes where we don't have room to scroll anyways.
  489. // This ensures that we don't take the 'overscroll' path in Sample()
  490. // on account of one axis which can't scroll having a velocity.
  491. {
  492. ReentrantMonitorAutoEnter lock(mApzc.mMonitor);
  493. if (!mApzc.mX.CanScroll()) {
  494. mApzc.mX.SetVelocity(0);
  495. }
  496. if (!mApzc.mY.CanScroll()) {
  497. mApzc.mY.SetVelocity(0);
  498. }
  499. }
  500.  
  501. ParentLayerPoint velocity = mApzc.GetVelocityVector();
  502.  
  503. // If the last fling was very recent and in the same direction as this one,
  504. // boost the velocity to be the sum of the two. Check separate axes separately
  505. // because we could have two vertical flings with small horizontal components
  506. // on the opposite side of zero, and we still want the y-fling to get accelerated.
  507. // Note that the acceleration code is only applied on the APZC that initiates
  508. // the fling; the accelerated velocities are then handed off using the
  509. // normal DispatchFling codepath.
  510. if (aApplyAcceleration && !mApzc.mLastFlingTime.IsNull()
  511. && (now - mApzc.mLastFlingTime).ToMilliseconds() < gfxPrefs::APZFlingAccelInterval()) {
  512. if (SameDirection(velocity.x, mApzc.mLastFlingVelocity.x)) {
  513. velocity.x = Accelerate(velocity.x, mApzc.mLastFlingVelocity.x);
  514. APZC_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
  515. &mApzc, mApzc.mX.GetVelocity(), velocity.x, mApzc.mLastFlingVelocity.x);
  516. mApzc.mX.SetVelocity(velocity.x);
  517. }
  518. if (SameDirection(velocity.y, mApzc.mLastFlingVelocity.y)) {
  519. velocity.y = Accelerate(velocity.y, mApzc.mLastFlingVelocity.y);
  520. APZC_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
  521. &mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
  522. mApzc.mY.SetVelocity(velocity.y);
  523. }
  524. }
  525.  
  526. mApzc.mLastFlingTime = now;
  527. mApzc.mLastFlingVelocity = velocity;
  528. }
  529.  
  530. /**
  531. * Advances a fling by an interpolated amount based on the passed in |aDelta|.
  532. * This should be called whenever sampling the content transform for this
  533. * frame. Returns true if the fling animation should be advanced by one frame,
  534. * or false if there is no fling or the fling has ended.
  535. */
  536. virtual bool DoSample(FrameMetrics& aFrameMetrics,
  537. const TimeDuration& aDelta) MOZ_OVERRIDE
  538. {
  539. float friction = gfxPrefs::APZFlingFriction();
  540. float threshold = gfxPrefs::APZFlingStoppedThreshold();
  541.  
  542. bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta, friction, threshold),
  543. shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta, friction, threshold);
  544. // If we shouldn't continue the fling, let's just stop and repaint.
  545. if (!shouldContinueFlingX && !shouldContinueFlingY) {
  546. APZC_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, mApzc.IsOverscrolled());
  547. // This APZC or an APZC further down the handoff chain may be be overscrolled.
  548. // Start a snap-back animation on the overscrolled APZC.
  549. // Note:
  550. // This needs to be a deferred task even though it can safely run
  551. // while holding mMonitor, because otherwise, if the overscrolled APZC
  552. // is this one, then the SetState(NOTHING) in UpdateAnimation will
  553. // stomp on the SetState(SNAP_BACK) it does.
  554. mDeferredTasks.append(NewRunnableMethod(mOverscrollHandoffChain.get(),
  555. &OverscrollHandoffChain::SnapBackOverscrolledApzc,
  556. &mApzc));
  557. return false;
  558. }
  559.  
  560. // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
  561. // Since we need to hand off the velocity to the tree manager in such a case,
  562. // we save it here. Would be ParentLayerVector instead of ParentLayerPoint
  563. // if we had vector classes.
  564. ParentLayerPoint velocity = mApzc.GetVelocityVector();
  565.  
  566. ParentLayerPoint offset = velocity * aDelta.ToMilliseconds();
  567.  
  568. // Ordinarily we might need to do a ScheduleComposite if either of
  569. // the following AdjustDisplacement calls returns true, but this
  570. // is already running as part of a FlingAnimation, so we'll be compositing
  571. // per frame of animation anyway.
  572. ParentLayerPoint overscroll;
  573. ParentLayerPoint adjustedOffset;
  574. mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
  575. mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
  576.  
  577. aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
  578.  
  579. // The fling may have caused us to reach the end of our scroll range.
  580. if (!IsZero(overscroll)) {
  581. // Hand off the fling to the next APZC in the overscroll handoff chain.
  582.  
  583. // We may have reached the end of the scroll range along one axis but
  584. // not the other. In such a case we only want to hand off the relevant
  585. // component of the fling.
  586. // TODO(botond): If our intention is to continue the other component
  587. // in this APZC, we should not be returning 'false'.
  588. if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
  589. velocity.x = 0;
  590. } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
  591. velocity.y = 0;
  592. }
  593.  
  594. // To hand off the fling, we attempt to find a target APZC and start a new
  595. // fling with the same velocity on that APZC. For simplicity, the actual
  596. // overscroll of the current sample is discarded rather than being handed
  597. // off. The compositor should sample animations sufficiently frequently
  598. // that this is not noticeable. The target APZC is chosen by seeing if
  599. // there is an APZC further in the handoff chain which is pannable; if
  600. // there isn't, we take the new fling ourselves, entering an overscrolled
  601. // state.
  602. // Note: APZC is holding mMonitor, so directly calling
  603. // HandleFlingOverscroll() (which acquires the tree lock) would violate
  604. // the lock ordering. Instead we schedule HandleFlingOverscroll() to be
  605. // called after mMonitor is released.
  606. mDeferredTasks.append(NewRunnableMethod(&mApzc,
  607. &AsyncPanZoomController::HandleFlingOverscroll,
  608. velocity,
  609. mOverscrollHandoffChain));
  610.  
  611. return false;
  612. }
  613.  
  614. return true;
  615. }
  616.  
  617. private:
  618. static bool SameDirection(float aVelocity1, float aVelocity2)
  619. {
  620. return (aVelocity1 == 0.0f)
  621. || (aVelocity2 == 0.0f)
  622. || (IsNegative(aVelocity1) == IsNegative(aVelocity2));
  623. }
  624.  
  625. static float Accelerate(float aBase, float aSupplemental)
  626. {
  627. return (aBase * gfxPrefs::APZFlingAccelBaseMultiplier())
  628. + (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
  629. }
  630.  
  631. AsyncPanZoomController& mApzc;
  632. nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
  633. };
  634.  
  635. class ZoomAnimation: public AsyncPanZoomAnimation {
  636. public:
  637. ZoomAnimation(CSSPoint aStartOffset, CSSToParentLayerScale aStartZoom,
  638. CSSPoint aEndOffset, CSSToParentLayerScale aEndZoom)
  639. : mTotalDuration(TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
  640. , mStartOffset(aStartOffset)
  641. , mStartZoom(aStartZoom)
  642. , mEndOffset(aEndOffset)
  643. , mEndZoom(aEndZoom)
  644. {}
  645.  
  646. virtual bool DoSample(FrameMetrics& aFrameMetrics,
  647. const TimeDuration& aDelta) MOZ_OVERRIDE
  648. {
  649. mDuration += aDelta;
  650. double animPosition = mDuration / mTotalDuration;
  651.  
  652. if (animPosition >= 1.0) {
  653. aFrameMetrics.SetZoom(mEndZoom);
  654. aFrameMetrics.SetScrollOffset(mEndOffset);
  655. return false;
  656. }
  657.  
  658. // Sample the zoom at the current time point. The sampled zoom
  659. // will affect the final computed resolution.
  660. float sampledPosition = gZoomAnimationFunction->GetValue(animPosition);
  661.  
  662. // We scale the scrollOffset linearly with sampledPosition, so the zoom
  663. // needs to scale inversely to match.
  664. aFrameMetrics.SetZoom(CSSToParentLayerScale(1 /
  665. (sampledPosition / mEndZoom.scale +
  666. (1 - sampledPosition) / mStartZoom.scale)));
  667.  
  668. aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
  669. mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
  670. mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
  671. )));
  672.  
  673. return true;
  674. }
  675.  
  676. private:
  677. TimeDuration mDuration;
  678. const TimeDuration mTotalDuration;
  679.  
  680. // Old metrics from before we started a zoom animation. This is only valid
  681. // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
  682. // interpolate between the start and end frames. We only use the
  683. // |mViewportScrollOffset| and |mResolution| fields on this.
  684. CSSPoint mStartOffset;
  685. CSSToParentLayerScale mStartZoom;
  686.  
  687. // Target metrics for a zoom to animation. This is only valid when we are in
  688. // the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
  689. // |mResolution| fields on this.
  690. CSSPoint mEndOffset;
  691. CSSToParentLayerScale mEndZoom;
  692. };
  693.  
  694. class OverscrollAnimation: public AsyncPanZoomAnimation {
  695. public:
  696. explicit OverscrollAnimation(AsyncPanZoomController& aApzc, const ParentLayerPoint& aVelocity)
  697. : mApzc(aApzc)
  698. {
  699. mApzc.mX.SetVelocity(aVelocity.x);
  700. mApzc.mY.SetVelocity(aVelocity.y);
  701. }
  702.  
  703. virtual bool DoSample(FrameMetrics& aFrameMetrics,
  704. const TimeDuration& aDelta) MOZ_OVERRIDE
  705. {
  706. // Can't inline these variables due to short-circuit evaluation.
  707. bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
  708. bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
  709. return continueX || continueY;
  710. }
  711. private:
  712. AsyncPanZoomController& mApzc;
  713. };
  714.  
  715. class SmoothScrollAnimation : public AsyncPanZoomAnimation {
  716. public:
  717. SmoothScrollAnimation(AsyncPanZoomController& aApzc,
  718. const nsPoint &aInitialPosition,
  719. const nsPoint &aInitialVelocity,
  720. const nsPoint& aDestination, double aSpringConstant,
  721. double aDampingRatio)
  722. : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(
  723. gfxPrefs::APZSmoothScrollRepaintInterval()))
  724. , mApzc(aApzc)
  725. , mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
  726. aSpringConstant, aDampingRatio)
  727. , mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
  728. aSpringConstant, aDampingRatio)
  729. {
  730. }
  731.  
  732. /**
  733. * Advances a smooth scroll simulation based on the time passed in |aDelta|.
  734. * This should be called whenever sampling the content transform for this
  735. * frame. Returns true if the smooth scroll should be advanced by one frame,
  736. * or false if the smooth scroll has ended.
  737. */
  738. bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
  739. if (mXAxisModel.IsFinished() && mYAxisModel.IsFinished()) {
  740. return false;
  741. }
  742.  
  743. mXAxisModel.Simulate(aDelta);
  744. mYAxisModel.Simulate(aDelta);
  745.  
  746. CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
  747. mYAxisModel.GetPosition()));
  748. CSSPoint css_velocity = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetVelocity(),
  749. mYAxisModel.GetVelocity()));
  750.  
  751. // Convert from points/second to points/ms
  752. ParentLayerPoint velocity = ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
  753.  
  754. // Keep the velocity updated for the Axis class so that any animations
  755. // chained off of the smooth scroll will inherit it.
  756. if (mXAxisModel.IsFinished()) {
  757. mApzc.mX.SetVelocity(0);
  758. } else {
  759. mApzc.mX.SetVelocity(velocity.x);
  760. }
  761. if (mYAxisModel.IsFinished()) {
  762. mApzc.mY.SetVelocity(0);
  763. } else {
  764. mApzc.mY.SetVelocity(velocity.y);
  765. }
  766. // If we overscroll, hand off to a fling animation that will complete the
  767. // spring back.
  768. CSSToParentLayerScale zoom = aFrameMetrics.GetZoom();
  769. ParentLayerPoint displacement = (position - aFrameMetrics.GetScrollOffset()) * zoom;
  770.  
  771. ParentLayerPoint overscroll;
  772. ParentLayerPoint adjustedOffset;
  773. mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
  774. mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
  775.  
  776. aFrameMetrics.ScrollBy(adjustedOffset / zoom);
  777.  
  778. // The smooth scroll may have caused us to reach the end of our scroll range.
  779. // This can happen if either the layout.css.scroll-behavior.damping-ratio
  780. // preference is set to less than 1 (underdamped) or if a smooth scroll
  781. // inherits velocity from a fling gesture.
  782. if (!IsZero(overscroll)) {
  783.  
  784. // Hand off a fling with the remaining momentum to the next APZC in the
  785. // overscroll handoff chain.
  786.  
  787. // We may have reached the end of the scroll range along one axis but
  788. // not the other. In such a case we only want to hand off the relevant
  789. // component of the fling.
  790. if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
  791. velocity.x = 0;
  792. } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
  793. velocity.y = 0;
  794. }
  795.  
  796. // To hand off the fling, we attempt to find a target APZC and start a new
  797. // fling with the same velocity on that APZC. For simplicity, the actual
  798. // overscroll of the current sample is discarded rather than being handed
  799. // off. The compositor should sample animations sufficiently frequently
  800. // that this is not noticeable. The target APZC is chosen by seeing if
  801. // there is an APZC further in the handoff chain which is pannable; if
  802. // there isn't, we take the new fling ourselves, entering an overscrolled
  803. // state.
  804. // Note: APZC is holding mMonitor, so directly calling
  805. // HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
  806. // the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
  807. // called after mMonitor is released.
  808. mDeferredTasks.append(NewRunnableMethod(&mApzc,
  809. &AsyncPanZoomController::HandleSmoothScrollOverscroll,
  810. velocity));
  811.  
  812. return false;
  813. }
  814.  
  815. return true;
  816. }
  817. private:
  818. AsyncPanZoomController& mApzc;
  819. AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
  820. };
  821.  
  822. void
  823. AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
  824. sFrameTime = aTime;
  825. }
  826.  
  827. void
  828. AsyncPanZoomController::SetThreadAssertionsEnabled(bool aEnabled) {
  829. sThreadAssertionsEnabled = aEnabled;
  830. }
  831.  
  832. bool
  833. AsyncPanZoomController::GetThreadAssertionsEnabled() {
  834. return sThreadAssertionsEnabled;
  835. }
  836.  
  837. void
  838. AsyncPanZoomController::AssertOnControllerThread() {
  839. if (!GetThreadAssertionsEnabled()) {
  840. return;
  841. }
  842.  
  843. static bool sControllerThreadDetermined = false;
  844. if (!sControllerThreadDetermined) {
  845. // Technically this may not actually pick up the correct controller thread,
  846. // if the first call to this method happens from a non-controller thread.
  847. // If the assertion below fires, it is possible that it is because
  848. // sControllerThread is not actually the controller thread.
  849. sControllerThread = PR_GetCurrentThread();
  850. sControllerThreadDetermined = true;
  851. }
  852. MOZ_ASSERT(sControllerThread == PR_GetCurrentThread());
  853. }
  854.  
  855. void
  856. AsyncPanZoomController::AssertOnCompositorThread()
  857. {
  858. if (GetThreadAssertionsEnabled()) {
  859. Compositor::AssertOnCompositorThread();
  860. }
  861. }
  862.  
  863. /*static*/ void
  864. AsyncPanZoomController::InitializeGlobalState()
  865. {
  866. MOZ_ASSERT(NS_IsMainThread());
  867.  
  868. static bool sInitialized = false;
  869. if (sInitialized)
  870. return;
  871. sInitialized = true;
  872.  
  873. gZoomAnimationFunction = new ComputedTimingFunction();
  874. gZoomAnimationFunction->Init(
  875. nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
  876. ClearOnShutdown(&gZoomAnimationFunction);
  877. gVelocityCurveFunction = new ComputedTimingFunction();
  878. gVelocityCurveFunction->Init(
  879. nsTimingFunction(gfxPrefs::APZCurveFunctionX1(),
  880. gfxPrefs::APZCurveFunctionY2(),
  881. gfxPrefs::APZCurveFunctionX2(),
  882. gfxPrefs::APZCurveFunctionY2()));
  883. ClearOnShutdown(&gVelocityCurveFunction);
  884. }
  885.  
  886. AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
  887. APZCTreeManager* aTreeManager,
  888. const nsRefPtr<InputQueue>& aInputQueue,
  889. GeckoContentController* aGeckoContentController,
  890. GestureBehavior aGestures)
  891. : mLayersId(aLayersId),
  892. mPaintThrottler(GetFrameTime(), TimeDuration::FromMilliseconds(500)),
  893. mGeckoContentController(aGeckoContentController),
  894. mRefPtrMonitor("RefPtrMonitor"),
  895. mSharingFrameMetricsAcrossProcesses(false),
  896. mMonitor("AsyncPanZoomController"),
  897. mX(MOZ_THIS_IN_INITIALIZER_LIST()),
  898. mY(MOZ_THIS_IN_INITIALIZER_LIST()),
  899. mPanDirRestricted(false),
  900. mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
  901. mLastSampleTime(GetFrameTime()),
  902. mLastAsyncScrollTime(GetFrameTime()),
  903. mLastAsyncScrollOffset(0, 0),
  904. mCurrentAsyncScrollOffset(0, 0),
  905. mAsyncScrollTimeoutTask(nullptr),
  906. mState(NOTHING),
  907. mNotificationBlockers(0),
  908. mInputQueue(aInputQueue),
  909. mTreeManager(aTreeManager),
  910. mAPZCId(sAsyncPanZoomControllerCount++),
  911. mSharedLock(nullptr),
  912. mAsyncTransformAppliedToContent(false)
  913. {
  914. if (aGestures == USE_GESTURE_DETECTOR) {
  915. mGestureEventListener = new GestureEventListener(this);
  916. }
  917. }
  918.  
  919. AsyncPanZoomController::~AsyncPanZoomController() {
  920. }
  921.  
  922. PCompositorParent*
  923. AsyncPanZoomController::GetSharedFrameMetricsCompositor()
  924. {
  925. AssertOnCompositorThread();
  926.  
  927. if (mSharingFrameMetricsAcrossProcesses) {
  928. const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(mLayersId);
  929. // |state| may be null here if the CrossProcessCompositorParent has already been destroyed.
  930. return state ? state->mCrossProcessParent : nullptr;
  931. }
  932. return mCompositorParent.get();
  933. }
  934.  
  935. already_AddRefed<GeckoContentController>
  936. AsyncPanZoomController::GetGeckoContentController() const {
  937. MonitorAutoLock lock(mRefPtrMonitor);
  938. nsRefPtr<GeckoContentController> controller = mGeckoContentController;
  939. return controller.forget();
  940. }
  941.  
  942. already_AddRefed<GestureEventListener>
  943. AsyncPanZoomController::GetGestureEventListener() const {
  944. MonitorAutoLock lock(mRefPtrMonitor);
  945. nsRefPtr<GestureEventListener> listener = mGestureEventListener;
  946. return listener.forget();
  947. }
  948.  
  949. const nsRefPtr<InputQueue>&
  950. AsyncPanZoomController::GetInputQueue() const {
  951. return mInputQueue;
  952. }
  953.  
  954. void
  955. AsyncPanZoomController::Destroy()
  956. {
  957. AssertOnCompositorThread();
  958.  
  959. CancelAnimation();
  960.  
  961. { // scope the lock
  962. MonitorAutoLock lock(mRefPtrMonitor);
  963. mGeckoContentController = nullptr;
  964. mGestureEventListener = nullptr;
  965. }
  966. mPrevSibling = nullptr;
  967. mLastChild = nullptr;
  968. mParent = nullptr;
  969. mTreeManager = nullptr;
  970.  
  971. PCompositorParent* compositor = GetSharedFrameMetricsCompositor();
  972. // Only send the release message if the SharedFrameMetrics has been created.
  973. if (compositor && mSharedFrameMetricsBuffer) {
  974. unused << compositor->SendReleaseSharedCompositorFrameMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
  975. }
  976.  
  977. { // scope the lock
  978. ReentrantMonitorAutoEnter lock(mMonitor);
  979. mSharedFrameMetricsBuffer = nullptr;
  980. delete mSharedLock;
  981. mSharedLock = nullptr;
  982. }
  983. }
  984.  
  985. bool
  986. AsyncPanZoomController::IsDestroyed() const
  987. {
  988. return mTreeManager == nullptr;
  989. }
  990.  
  991. /* static */ScreenCoord
  992. AsyncPanZoomController::GetTouchStartTolerance()
  993. {
  994. return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
  995. }
  996.  
  997. /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
  998. {
  999. return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
  1000. }
  1001.  
  1002. bool
  1003. AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
  1004. if (aTouchPoints == 0) {
  1005. // Cant' do anything with zero touch points
  1006. return false;
  1007. }
  1008.  
  1009. // This logic is simplified, erring on the side of returning true
  1010. // if we're not sure. It's safer to pretend that we can consume the
  1011. // event and then not be able to than vice-versa.
  1012. // We could probably enhance this logic to determine things like "we're
  1013. // not pannable, so we can only zoom in, and the zoom is already maxed
  1014. // out, so we're not zoomable either" but no need for that at this point.
  1015.  
  1016. bool pannable = aBlock->GetOverscrollHandoffChain()->CanBePanned(this);
  1017. bool zoomable = mZoomConstraints.mAllowZoom;
  1018.  
  1019. pannable &= (aBlock->TouchActionAllowsPanningX() || aBlock->TouchActionAllowsPanningY());
  1020. zoomable &= (aBlock->TouchActionAllowsPinchZoom());
  1021.  
  1022. // XXX once we fix bug 1031443, consumable should be assigned
  1023. // pannable || zoomable if aTouchPoints > 1.
  1024. bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
  1025. if (!consumable) {
  1026. return false;
  1027. }
  1028.  
  1029. return true;
  1030. }
  1031.  
  1032. nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
  1033. AssertOnControllerThread();
  1034.  
  1035. nsEventStatus rv = nsEventStatus_eIgnore;
  1036.  
  1037. switch (aEvent.mInputType) {
  1038. case MULTITOUCH_INPUT: {
  1039. const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
  1040.  
  1041. nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
  1042. if (listener) {
  1043. rv = listener->HandleInputEvent(multiTouchInput);
  1044. if (rv == nsEventStatus_eConsumeNoDefault) {
  1045. return rv;
  1046. }
  1047. }
  1048.  
  1049. switch (multiTouchInput.mType) {
  1050. case MultiTouchInput::MULTITOUCH_START: rv = OnTouchStart(multiTouchInput); break;
  1051. case MultiTouchInput::MULTITOUCH_MOVE: rv = OnTouchMove(multiTouchInput); break;
  1052. case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
  1053. case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
  1054. default: NS_WARNING("Unhandled multitouch"); break;
  1055. }
  1056. break;
  1057. }
  1058. case PANGESTURE_INPUT: {
  1059. const PanGestureInput& panGestureInput = aEvent.AsPanGestureInput();
  1060. switch (panGestureInput.mType) {
  1061. case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
  1062. case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
  1063. case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
  1064. case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
  1065. case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
  1066. case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
  1067. case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
  1068. case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
  1069. default: NS_WARNING("Unhandled pan gesture"); break;
  1070. }
  1071. break;
  1072. }
  1073. case SCROLLWHEEL_INPUT: {
  1074. const ScrollWheelInput& scrollInput = aEvent.AsScrollWheelInput();
  1075. rv = OnScrollWheel(scrollInput);
  1076. break;
  1077. }
  1078. default: return HandleGestureEvent(aEvent);
  1079. }
  1080.  
  1081. return rv;
  1082. }
  1083.  
  1084. nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
  1085. {
  1086. AssertOnControllerThread();
  1087.  
  1088. nsEventStatus rv = nsEventStatus_eIgnore;
  1089.  
  1090. switch (aEvent.mInputType) {
  1091. case PINCHGESTURE_INPUT: {
  1092. const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
  1093. switch (pinchGestureInput.mType) {
  1094. case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
  1095. case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
  1096. case PinchGestureInput::PINCHGESTURE_END: rv = OnScaleEnd(pinchGestureInput); break;
  1097. default: NS_WARNING("Unhandled pinch gesture"); break;
  1098. }
  1099. break;
  1100. }
  1101. case TAPGESTURE_INPUT: {
  1102. const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
  1103. switch (tapGestureInput.mType) {
  1104. case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
  1105. case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
  1106. case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
  1107. case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
  1108. case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
  1109. case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
  1110. default: NS_WARNING("Unhandled tap gesture"); break;
  1111. }
  1112. break;
  1113. }
  1114. default: NS_WARNING("Unhandled input event"); break;
  1115. }
  1116.  
  1117. return rv;
  1118. }
  1119.  
  1120. nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
  1121. APZC_LOG("%p got a touch-start in state %d\n", this, mState);
  1122. mPanDirRestricted = false;
  1123. ParentLayerPoint point = GetFirstTouchPoint(aEvent);
  1124.  
  1125. switch (mState) {
  1126. case FLING:
  1127. case ANIMATING_ZOOM:
  1128. case SMOOTH_SCROLL:
  1129. CurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations();
  1130. // Fall through.
  1131. case NOTHING: {
  1132. mX.StartTouch(point.x, aEvent.mTime);
  1133. mY.StartTouch(point.y, aEvent.mTime);
  1134. if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1135. controller->NotifyAPZStateChange(
  1136. GetGuid(), APZStateChange::StartTouch,
  1137. CurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(this));
  1138. }
  1139. SetState(TOUCHING);
  1140. break;
  1141. }
  1142. case TOUCHING:
  1143. case PANNING:
  1144. case PANNING_LOCKED_X:
  1145. case PANNING_LOCKED_Y:
  1146. case CROSS_SLIDING_X:
  1147. case CROSS_SLIDING_Y:
  1148. case PINCHING:
  1149. NS_WARNING("Received impossible touch in OnTouchStart");
  1150. break;
  1151. default:
  1152. NS_WARNING("Unhandled case in OnTouchStart");
  1153. break;
  1154. }
  1155.  
  1156. return nsEventStatus_eConsumeNoDefault;
  1157. }
  1158.  
  1159. nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
  1160. APZC_LOG("%p got a touch-move in state %d\n", this, mState);
  1161. switch (mState) {
  1162. case FLING:
  1163. case SMOOTH_SCROLL:
  1164. case NOTHING:
  1165. case ANIMATING_ZOOM:
  1166. // May happen if the user double-taps and drags without lifting after the
  1167. // second tap. Ignore the move if this happens.
  1168. return nsEventStatus_eIgnore;
  1169.  
  1170. case CROSS_SLIDING_X:
  1171. case CROSS_SLIDING_Y:
  1172. // While cross-sliding, we don't want to consume any touchmove events for
  1173. // panning or zooming, and let the caller handle them instead.
  1174. return nsEventStatus_eIgnore;
  1175.  
  1176. case TOUCHING: {
  1177. ScreenCoord panThreshold = GetTouchStartTolerance();
  1178. UpdateWithTouchAtDevicePoint(aEvent);
  1179.  
  1180. if (PanDistance() < panThreshold) {
  1181. return nsEventStatus_eIgnore;
  1182. }
  1183.  
  1184. if (gfxPrefs::TouchActionEnabled() && CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
  1185. // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
  1186. // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
  1187. // status immediately to trigger cancel event further. It should happen independent of
  1188. // the parent type (whether it is scrolling or not).
  1189. StartPanning(aEvent);
  1190. return nsEventStatus_eConsumeNoDefault;
  1191. }
  1192.  
  1193. return StartPanning(aEvent);
  1194. }
  1195.  
  1196. case PANNING:
  1197. case PANNING_LOCKED_X:
  1198. case PANNING_LOCKED_Y:
  1199. TrackTouch(aEvent);
  1200. return nsEventStatus_eConsumeNoDefault;
  1201.  
  1202. case PINCHING:
  1203. // The scale gesture listener should have handled this.
  1204. NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
  1205. return nsEventStatus_eIgnore;
  1206.  
  1207. case OVERSCROLL_ANIMATION:
  1208. // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
  1209. // as touch blocks that begin in an overscrolled state
  1210. // are ignored.
  1211. NS_WARNING("Received impossible touch in OnTouchMove");
  1212. break;
  1213. }
  1214.  
  1215. return nsEventStatus_eConsumeNoDefault;
  1216. }
  1217.  
  1218. nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
  1219. APZC_LOG("%p got a touch-end in state %d\n", this, mState);
  1220.  
  1221. OnTouchEndOrCancel();
  1222.  
  1223. // In case no touch behavior triggered previously we can avoid sending
  1224. // scroll events or requesting content repaint. This condition is added
  1225. // to make tests consistent - in case touch-action is NONE (and therefore
  1226. // no pans/zooms can be performed) we expected neither scroll or repaint
  1227. // events.
  1228. if (mState != NOTHING) {
  1229. ReentrantMonitorAutoEnter lock(mMonitor);
  1230. SendAsyncScrollEvent();
  1231. }
  1232.  
  1233. switch (mState) {
  1234. case FLING:
  1235. // Should never happen.
  1236. NS_WARNING("Received impossible touch end in OnTouchEnd.");
  1237. // Fall through.
  1238. case ANIMATING_ZOOM:
  1239. case SMOOTH_SCROLL:
  1240. case NOTHING:
  1241. // May happen if the user double-taps and drags without lifting after the
  1242. // second tap. Ignore if this happens.
  1243. return nsEventStatus_eIgnore;
  1244.  
  1245. case TOUCHING:
  1246. case CROSS_SLIDING_X:
  1247. case CROSS_SLIDING_Y:
  1248. // We may have some velocity stored on the axis from move events
  1249. // that were not big enough to trigger scrolling. Clear that out.
  1250. mX.SetVelocity(0);
  1251. mY.SetVelocity(0);
  1252. SetState(NOTHING);
  1253. return nsEventStatus_eIgnore;
  1254.  
  1255. case PANNING:
  1256. case PANNING_LOCKED_X:
  1257. case PANNING_LOCKED_Y:
  1258. {
  1259. CurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
  1260. mX.EndTouch(aEvent.mTime);
  1261. mY.EndTouch(aEvent.mTime);
  1262. ParentLayerPoint flingVelocity = GetVelocityVector();
  1263. // Clear our velocities; if DispatchFling() gives the fling to us,
  1264. // the fling velocity gets *added* to our existing velocity in
  1265. // AcceptFling().
  1266. mX.SetVelocity(0);
  1267. mY.SetVelocity(0);
  1268. // Clear our state so that we don't stay in the PANNING state
  1269. // if DispatchFling() gives the fling to somone else. However,
  1270. // don't send the state change notification until we've determined
  1271. // what our final state is to avoid notification churn.
  1272. StateChangeNotificationBlocker blocker(this);
  1273. SetState(NOTHING);
  1274. APZC_LOG("%p starting a fling animation\n", this);
  1275. // Make a local copy of the tree manager pointer and check that it's not
  1276. // null before calling DispatchFling(). This is necessary because Destroy(),
  1277. // which nulls out mTreeManager, could be called concurrently.
  1278. if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
  1279. treeManagerLocal->DispatchFling(this,
  1280. flingVelocity,
  1281. CurrentTouchBlock()->GetOverscrollHandoffChain(),
  1282. false /* not handoff */);
  1283. }
  1284. return nsEventStatus_eConsumeNoDefault;
  1285. }
  1286. case PINCHING:
  1287. SetState(NOTHING);
  1288. // Scale gesture listener should have handled this.
  1289. NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
  1290. return nsEventStatus_eIgnore;
  1291.  
  1292. case OVERSCROLL_ANIMATION:
  1293. // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
  1294. // as touch blocks that begin in an overscrolled state
  1295. // are ignored.
  1296. NS_WARNING("Received impossible touch in OnTouchEnd");
  1297. break;
  1298. }
  1299.  
  1300. return nsEventStatus_eConsumeNoDefault;
  1301. }
  1302.  
  1303. nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
  1304. APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
  1305. OnTouchEndOrCancel();
  1306. mX.CancelTouch();
  1307. mY.CancelTouch();
  1308. CancelAnimation();
  1309. return nsEventStatus_eConsumeNoDefault;
  1310. }
  1311.  
  1312. nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
  1313. APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
  1314.  
  1315. // Note that there may not be a touch block at this point, if we received the
  1316. // PinchGestureEvent directly from widget code without any touch events.
  1317. if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
  1318. return nsEventStatus_eIgnore;
  1319. }
  1320.  
  1321. if (!mZoomConstraints.mAllowZoom) {
  1322. return nsEventStatus_eConsumeNoDefault;
  1323. }
  1324.  
  1325. SetState(PINCHING);
  1326. mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.mCompositionBounds.TopLeft();
  1327.  
  1328. return nsEventStatus_eConsumeNoDefault;
  1329. }
  1330.  
  1331. nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
  1332. APZC_LOG("%p got a scale in state %d\n", this, mState);
  1333.  
  1334. if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
  1335. return nsEventStatus_eIgnore;
  1336. }
  1337.  
  1338. if (mState != PINCHING) {
  1339. return nsEventStatus_eConsumeNoDefault;
  1340. }
  1341.  
  1342. float prevSpan = aEvent.mPreviousSpan;
  1343. if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
  1344. // We're still handling it; we've just decided to throw this event away.
  1345. return nsEventStatus_eConsumeNoDefault;
  1346. }
  1347.  
  1348. float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
  1349.  
  1350. {
  1351. ReentrantMonitorAutoEnter lock(mMonitor);
  1352.  
  1353. CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom();
  1354. ParentLayerPoint focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.mCompositionBounds.TopLeft();
  1355. CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
  1356.  
  1357. CSSPoint focusChange = (mLastZoomFocus - focusPoint) / userZoom;
  1358. // If displacing by the change in focus point will take us off page bounds,
  1359. // then reduce the displacement such that it doesn't.
  1360. focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
  1361. focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
  1362. ScrollBy(focusChange);
  1363.  
  1364. // When we zoom in with focus, we can zoom too much towards the boundaries
  1365. // that we actually go over them. These are the needed displacements along
  1366. // either axis such that we don't overscroll the boundaries when zooming.
  1367. CSSPoint neededDisplacement;
  1368.  
  1369. CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
  1370. CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
  1371. realMinZoom.scale = std::max(realMinZoom.scale,
  1372. mFrameMetrics.mCompositionBounds.width / mFrameMetrics.mScrollableRect.width);
  1373. realMinZoom.scale = std::max(realMinZoom.scale,
  1374. mFrameMetrics.mCompositionBounds.height / mFrameMetrics.mScrollableRect.height);
  1375. if (realMaxZoom < realMinZoom) {
  1376. realMaxZoom = realMinZoom;
  1377. }
  1378.  
  1379. bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
  1380. (spanRatio < 1.0 && userZoom > realMinZoom);
  1381.  
  1382. if (doScale) {
  1383. spanRatio = clamped(spanRatio,
  1384. realMinZoom.scale / userZoom.scale,
  1385. realMaxZoom.scale / userZoom.scale);
  1386.  
  1387. // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
  1388. // up above we clamped it.
  1389. neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
  1390. neededDisplacement.y = -mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
  1391.  
  1392. ScaleWithFocus(spanRatio, cssFocusPoint);
  1393.  
  1394. if (neededDisplacement != CSSPoint()) {
  1395. ScrollBy(neededDisplacement);
  1396. }
  1397.  
  1398. ScheduleComposite();
  1399. // We don't want to redraw on every scale, so don't use
  1400. // RequestContentRepaint()
  1401. UpdateSharedCompositorFrameMetrics();
  1402. }
  1403.  
  1404. mLastZoomFocus = focusPoint;
  1405. }
  1406.  
  1407. return nsEventStatus_eConsumeNoDefault;
  1408. }
  1409.  
  1410. nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
  1411. APZC_LOG("%p got a scale-end in state %d\n", this, mState);
  1412.  
  1413. if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
  1414. return nsEventStatus_eIgnore;
  1415. }
  1416.  
  1417. SetState(NOTHING);
  1418.  
  1419. {
  1420. ReentrantMonitorAutoEnter lock(mMonitor);
  1421.  
  1422. // We can get into a situation where we are overscrolled at the end of a
  1423. // pinch if we go into overscroll with a two-finger pan, and then turn
  1424. // that into a pinch by increasing the span sufficiently. In such a case,
  1425. // there is no snap-back animation to get us out of overscroll, so we need
  1426. // to get out of it somehow.
  1427. // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
  1428. // further up in the handoff chain rather than on the current APZC, so
  1429. // we need to clear overscroll along the entire handoff chain.
  1430. if (HasReadyTouchBlock()) {
  1431. CurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
  1432. } else {
  1433. ClearOverscroll();
  1434. }
  1435.  
  1436. ScheduleComposite();
  1437. RequestContentRepaint();
  1438. UpdateSharedCompositorFrameMetrics();
  1439. }
  1440.  
  1441. return nsEventStatus_eConsumeNoDefault;
  1442. }
  1443.  
  1444. bool
  1445. AsyncPanZoomController::ConvertToGecko(const ParentLayerPoint& aPoint, CSSPoint* aOut)
  1446. {
  1447. if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
  1448. Matrix4x4 transformToGecko = treeManagerLocal->GetApzcToGeckoTransform(this);
  1449. // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
  1450. // for this coordinate space and it maps the closest to LayoutDevicePoint.
  1451. LayoutDevicePoint layoutPoint = TransformTo<LayoutDevicePixel>(transformToGecko, aPoint);
  1452. { // scoped lock to access mFrameMetrics
  1453. ReentrantMonitorAutoEnter lock(mMonitor);
  1454. *aOut = layoutPoint / mFrameMetrics.GetMDevPixelsPerCSSPixel();
  1455. }
  1456. return true;
  1457. }
  1458. return false;
  1459. }
  1460.  
  1461. nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
  1462. {
  1463. double deltaX = aEvent.mDeltaX;
  1464. double deltaY = aEvent.mDeltaY;
  1465. switch (aEvent.mDeltaType) {
  1466. case ScrollWheelInput::SCROLLDELTA_LINE: {
  1467. LayoutDeviceIntSize scrollAmount = mFrameMetrics.GetLineScrollAmount();
  1468. deltaX *= scrollAmount.width;
  1469. deltaY *= scrollAmount.height;
  1470. break;
  1471. }
  1472. default:
  1473. MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
  1474. return nsEventStatus_eConsumeNoDefault;
  1475. }
  1476.  
  1477. switch (aEvent.mScrollMode) {
  1478. case ScrollWheelInput::SCROLLMODE_INSTANT: {
  1479. // Decompose into pan events for simplicity.
  1480. PanGestureInput start(PanGestureInput::PANGESTURE_START, aEvent.mTime, aEvent.mTimeStamp,
  1481. aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
  1482. start.mLocalPanStartPoint = aEvent.mLocalOrigin;
  1483. OnPanBegin(start);
  1484.  
  1485. // Pan gestures use natural directions which are inverted from scroll
  1486. // wheel and touchpad scroll gestures, so we invert x/y here. Since the
  1487. // zoom includes any device : css pixel zoom, we convert to CSS pixels
  1488. // before applying the zoom.
  1489. LayoutDevicePoint devicePixelDelta(-deltaX, -deltaY);
  1490. ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.GetMDevPixelsPerCSSPixel()) *
  1491. mFrameMetrics.GetZoom();
  1492.  
  1493. PanGestureInput move(PanGestureInput::PANGESTURE_PAN, aEvent.mTime, aEvent.mTimeStamp,
  1494. aEvent.mOrigin,
  1495. ToScreenCoordinates(delta, aEvent.mLocalOrigin),
  1496. aEvent.modifiers);
  1497. move.mLocalPanStartPoint = aEvent.mLocalOrigin;
  1498. move.mLocalPanDisplacement = delta;
  1499. OnPan(move, false);
  1500.  
  1501. PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp,
  1502. aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
  1503. end.mLocalPanStartPoint = aEvent.mLocalOrigin;
  1504. OnPanEnd(start);
  1505. break;
  1506. }
  1507.  
  1508. case ScrollWheelInput::SCROLLMODE_SMOOTH: {
  1509. CSSPoint delta = LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.GetMDevPixelsPerCSSPixel();
  1510.  
  1511. // If we're already in a smooth scroll animation, don't cancel it. This
  1512. // lets us preserve the existing scrolling velocity.
  1513. if (mState != SMOOTH_SCROLL) {
  1514. CancelAnimation();
  1515. mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetScrollOffset() + delta);
  1516. } else {
  1517. mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta);
  1518. }
  1519. StartSmoothScroll();
  1520. break;
  1521. }
  1522. }
  1523.  
  1524. return nsEventStatus_eConsumeNoDefault;
  1525. }
  1526.  
  1527. nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
  1528. APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
  1529.  
  1530. mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
  1531. mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
  1532. if (mPanGestureState) {
  1533. mPanGestureState->GetOverscrollHandoffChain()->CancelAnimations();
  1534. } else {
  1535. CancelAnimation();
  1536. }
  1537.  
  1538. return nsEventStatus_eConsumeNoDefault;
  1539. }
  1540.  
  1541. nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
  1542. APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
  1543.  
  1544. mX.CancelTouch();
  1545. mY.CancelTouch();
  1546.  
  1547. return nsEventStatus_eConsumeNoDefault;
  1548. }
  1549.  
  1550.  
  1551. nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
  1552. APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
  1553.  
  1554. if (mState == SMOOTH_SCROLL) {
  1555. // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
  1556. CancelAnimation();
  1557. }
  1558.  
  1559. mPanGestureState = MakeUnique<InputBlockState>(this, true);
  1560.  
  1561. mX.StartTouch(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
  1562. mY.StartTouch(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
  1563.  
  1564. if (GetAxisLockMode() == FREE) {
  1565. SetState(PANNING);
  1566. return nsEventStatus_eConsumeNoDefault;
  1567. }
  1568.  
  1569. float dx = aEvent.mPanDisplacement.x, dy = aEvent.mPanDisplacement.y;
  1570. double angle = atan2(dy, dx); // range [-pi, pi]
  1571. angle = fabs(angle); // range [0, pi]
  1572.  
  1573. HandlePanning(angle);
  1574.  
  1575. return nsEventStatus_eConsumeNoDefault;
  1576. }
  1577.  
  1578. nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
  1579. APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
  1580.  
  1581. if (mState == SMOOTH_SCROLL) {
  1582. if (aEvent.mType == PanGestureInput::PANGESTURE_MOMENTUMPAN) {
  1583. // When a SMOOTH_SCROLL scroll is being processed on a frame, mouse
  1584. // wheel and trackpad momentum scroll position updates will not cancel the
  1585. // SMOOTH_SCROLL scroll animations, enabling scripts that depend on
  1586. // them to be responsive without forcing the user to wait for the momentum
  1587. // scrolling to completely stop.
  1588. return nsEventStatus_eConsumeNoDefault;
  1589. } else {
  1590. // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
  1591. CancelAnimation();
  1592. }
  1593. }
  1594.  
  1595. // We need to update the axis velocity in order to get a useful display port
  1596. // size and position. We need to do so even if this is a momentum pan (i.e.
  1597. // aFingersOnTouchpad == false); in that case the "with touch" part is not
  1598. // really appropriate, so we may want to rethink this at some point.
  1599. mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.x, aEvent.mTime);
  1600. mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalPanStartPoint.y, aEvent.mTime);
  1601.  
  1602. HandlePanningUpdate(aEvent.mPanDisplacement);
  1603.  
  1604. // TODO: Handle pan events sent without pan begin / pan end events properly.
  1605. if (mPanGestureState) {
  1606. ScreenPoint panDistance(fabs(aEvent.mPanDisplacement.x), fabs(aEvent.mPanDisplacement.y));
  1607. OverscrollHandoffState handoffState(
  1608. *mPanGestureState->GetOverscrollHandoffChain(), panDistance);
  1609. CallDispatchScroll(aEvent.mLocalPanStartPoint,
  1610. aEvent.mLocalPanStartPoint + aEvent.mLocalPanDisplacement,
  1611. handoffState);
  1612. }
  1613.  
  1614. return nsEventStatus_eConsumeNoDefault;
  1615. }
  1616.  
  1617. nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
  1618. APZC_LOG("%p got a pan-end in state %d\n", this, mState);
  1619.  
  1620. mPanGestureState = nullptr;
  1621.  
  1622. mX.EndTouch(aEvent.mTime);
  1623. mY.EndTouch(aEvent.mTime);
  1624. RequestContentRepaint();
  1625.  
  1626. return nsEventStatus_eConsumeNoDefault;
  1627. }
  1628.  
  1629. nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
  1630. APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
  1631.  
  1632. if (mState == SMOOTH_SCROLL) {
  1633. // SMOOTH_SCROLL scrolls are cancelled by pan gestures.
  1634. CancelAnimation();
  1635. }
  1636.  
  1637. mPanGestureState = MakeUnique<InputBlockState>(this, true);
  1638.  
  1639. return nsEventStatus_eConsumeNoDefault;
  1640. }
  1641.  
  1642. nsEventStatus AsyncPanZoomController::OnPanMomentumEnd(const PanGestureInput& aEvent) {
  1643. APZC_LOG("%p got a pan-momentumend in state %d\n", this, mState);
  1644.  
  1645. mPanGestureState = nullptr;
  1646.  
  1647. // We need to reset the velocity to zero. We don't really have a "touch"
  1648. // here because the touch has already ended long before the momentum
  1649. // animation started, but I guess it doesn't really matter for now.
  1650. mX.CancelTouch();
  1651. mY.CancelTouch();
  1652.  
  1653. RequestContentRepaint();
  1654.  
  1655. return nsEventStatus_eConsumeNoDefault;
  1656. }
  1657.  
  1658. nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
  1659. APZC_LOG("%p got a long-press in state %d\n", this, mState);
  1660. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1661. if (controller) {
  1662. int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
  1663. CSSPoint geckoScreenPoint;
  1664. if (ConvertToGecko(aEvent.mLocalPoint, &geckoScreenPoint)) {
  1665. uint64_t blockId = GetInputQueue()->InjectNewTouchBlock(this);
  1666. controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid(), blockId);
  1667. return nsEventStatus_eConsumeNoDefault;
  1668. }
  1669. }
  1670. return nsEventStatus_eIgnore;
  1671. }
  1672.  
  1673. nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
  1674. APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
  1675. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1676. if (controller) {
  1677. int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
  1678. CSSPoint geckoScreenPoint;
  1679. if (ConvertToGecko(aEvent.mLocalPoint, &geckoScreenPoint)) {
  1680. controller->HandleLongTapUp(geckoScreenPoint, modifiers, GetGuid());
  1681. return nsEventStatus_eConsumeNoDefault;
  1682. }
  1683. }
  1684. return nsEventStatus_eIgnore;
  1685. }
  1686.  
  1687. nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ParentLayerPoint& aPoint, mozilla::Modifiers aModifiers) {
  1688. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1689. if (controller) {
  1690. CSSPoint geckoScreenPoint;
  1691. if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
  1692. if (!CurrentTouchBlock()->SetSingleTapOccurred()) {
  1693. return nsEventStatus_eIgnore;
  1694. }
  1695. // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
  1696. // calling controller->HandleSingleTap directly might mean that content receives
  1697. // the single tap message before the corresponding touch-up. To avoid that we
  1698. // schedule the singletap message to run on the next spin of the event loop.
  1699. // See bug 965381 for the issue this was causing.
  1700. controller->PostDelayedTask(
  1701. NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
  1702. geckoScreenPoint, WidgetModifiersToDOMModifiers(aModifiers),
  1703. GetGuid()),
  1704. 0);
  1705. return nsEventStatus_eConsumeNoDefault;
  1706. }
  1707. }
  1708. return nsEventStatus_eIgnore;
  1709. }
  1710.  
  1711. void AsyncPanZoomController::OnTouchEndOrCancel() {
  1712. if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1713. controller->NotifyAPZStateChange(
  1714. GetGuid(), APZStateChange::EndTouch, CurrentTouchBlock()->SingleTapOccurred());
  1715. }
  1716. }
  1717.  
  1718. nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
  1719. APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
  1720. // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
  1721. // sending event to content
  1722. if (!(mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
  1723. return GenerateSingleTap(aEvent.mLocalPoint, aEvent.modifiers);
  1724. }
  1725. return nsEventStatus_eIgnore;
  1726. }
  1727.  
  1728. nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
  1729. APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
  1730. return GenerateSingleTap(aEvent.mLocalPoint, aEvent.modifiers);
  1731. }
  1732.  
  1733. nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
  1734. APZC_LOG("%p got a double-tap in state %d\n", this, mState);
  1735. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  1736. if (controller) {
  1737. if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
  1738. int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
  1739. CSSPoint geckoScreenPoint;
  1740. if (ConvertToGecko(aEvent.mLocalPoint, &geckoScreenPoint)) {
  1741. controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
  1742. }
  1743. }
  1744. return nsEventStatus_eConsumeNoDefault;
  1745. }
  1746. return nsEventStatus_eIgnore;
  1747. }
  1748.  
  1749. nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
  1750. APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
  1751. // XXX: Implement this.
  1752. return nsEventStatus_eIgnore;
  1753. }
  1754.  
  1755.  
  1756. ScreenPoint AsyncPanZoomController::ToScreenCoordinates(const ParentLayerPoint& aVector,
  1757. const ParentLayerPoint& aAnchor) const {
  1758. if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
  1759. Matrix4x4 apzcToScreen = treeManagerLocal->GetScreenToApzcTransform(this).Inverse();
  1760. return TransformVector<ScreenPixel>(apzcToScreen, aVector, aAnchor);
  1761. }
  1762. return ViewAs<ScreenPixel>(aVector, PixelCastJustification::TransformNotAvailable);
  1763. }
  1764.  
  1765. ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPoint& aVector,
  1766. const ScreenPoint& aAnchor) const {
  1767. if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
  1768. Matrix4x4 transform = treeManagerLocal->GetScreenToApzcTransform(this);
  1769. return TransformVector<ParentLayerPixel>(transform, aVector, aAnchor);
  1770. }
  1771. return ViewAs<ParentLayerPixel>(aVector, PixelCastJustification::TransformNotAvailable);
  1772. }
  1773.  
  1774. ScreenCoord AsyncPanZoomController::PanDistance() const {
  1775. ParentLayerPoint panVector;
  1776. ParentLayerPoint panStart;
  1777. {
  1778. ReentrantMonitorAutoEnter lock(mMonitor);
  1779. panVector = ParentLayerPoint(mX.PanDistance(), mY.PanDistance());
  1780. panStart = PanStart();
  1781. }
  1782. return ToScreenCoordinates(panVector, panStart).Length();
  1783. }
  1784.  
  1785. ParentLayerPoint AsyncPanZoomController::PanStart() const {
  1786. return ParentLayerPoint(mX.PanStart(), mY.PanStart());
  1787. }
  1788.  
  1789. const ParentLayerPoint AsyncPanZoomController::GetVelocityVector() const {
  1790. return ParentLayerPoint(mX.GetVelocity(), mY.GetVelocity());
  1791. }
  1792.  
  1793. void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
  1794. // Handling of cross sliding will need to be added in this method after touch-action released
  1795. // enabled by default.
  1796. if (CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
  1797. if (mX.CanScrollNow() && mY.CanScrollNow()) {
  1798. if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
  1799. mY.SetAxisLocked(true);
  1800. SetState(PANNING_LOCKED_X);
  1801. } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
  1802. mX.SetAxisLocked(true);
  1803. SetState(PANNING_LOCKED_Y);
  1804. } else {
  1805. SetState(PANNING);
  1806. }
  1807. } else if (mX.CanScrollNow() || mY.CanScrollNow()) {
  1808. SetState(PANNING);
  1809. } else {
  1810. SetState(NOTHING);
  1811. }
  1812. } else if (CurrentTouchBlock()->TouchActionAllowsPanningX()) {
  1813. // Using bigger angle for panning to keep behavior consistent
  1814. // with IE.
  1815. if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
  1816. mY.SetAxisLocked(true);
  1817. SetState(PANNING_LOCKED_X);
  1818. mPanDirRestricted = true;
  1819. } else {
  1820. // Don't treat these touches as pan/zoom movements since 'touch-action' value
  1821. // requires it.
  1822. SetState(NOTHING);
  1823. }
  1824. } else if (CurrentTouchBlock()->TouchActionAllowsPanningY()) {
  1825. if (IsCloseToVertical(aAngle, gfxPrefs::APZAllowedDirectPanAngle())) {
  1826. mX.SetAxisLocked(true);
  1827. SetState(PANNING_LOCKED_Y);
  1828. mPanDirRestricted = true;
  1829. } else {
  1830. SetState(NOTHING);
  1831. }
  1832. } else {
  1833. SetState(NOTHING);
  1834. }
  1835. }
  1836.  
  1837. void AsyncPanZoomController::HandlePanning(double aAngle) {
  1838. ReentrantMonitorAutoEnter lock(mMonitor);
  1839. if (!gfxPrefs::APZCrossSlideEnabled() && (!mX.CanScrollNow() || !mY.CanScrollNow())) {
  1840. SetState(PANNING);
  1841. } else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
  1842. mY.SetAxisLocked(true);
  1843. if (mX.CanScrollNow()) {
  1844. SetState(PANNING_LOCKED_X);
  1845. } else {
  1846. SetState(CROSS_SLIDING_X);
  1847. mX.SetAxisLocked(true);
  1848. }
  1849. } else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
  1850. mX.SetAxisLocked(true);
  1851. if (mY.CanScrollNow()) {
  1852. SetState(PANNING_LOCKED_Y);
  1853. } else {
  1854. SetState(CROSS_SLIDING_Y);
  1855. mY.SetAxisLocked(true);
  1856. }
  1857. } else {
  1858. SetState(PANNING);
  1859. }
  1860. }
  1861.  
  1862. void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
  1863. // If we're axis-locked, check if the user is trying to break the lock
  1864. if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
  1865.  
  1866. double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi]
  1867. angle = fabs(angle); // range [0, pi]
  1868.  
  1869. float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
  1870.  
  1871. if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
  1872. if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
  1873. if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
  1874. mY.SetAxisLocked(false);
  1875. SetState(PANNING);
  1876. }
  1877. } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
  1878. if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) {
  1879. mX.SetAxisLocked(false);
  1880. SetState(PANNING);
  1881. }
  1882. }
  1883. }
  1884. }
  1885. }
  1886.  
  1887. nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
  1888. ReentrantMonitorAutoEnter lock(mMonitor);
  1889.  
  1890. ParentLayerPoint point = GetFirstTouchPoint(aEvent);
  1891. float dx = mX.PanDistance(point.x);
  1892. float dy = mY.PanDistance(point.y);
  1893.  
  1894. // When the touch move breaks through the pan threshold, reposition the touch down origin
  1895. // so the page won't jump when we start panning.
  1896. mX.StartTouch(point.x, aEvent.mTime);
  1897. mY.StartTouch(point.y, aEvent.mTime);
  1898.  
  1899. double angle = atan2(dy, dx); // range [-pi, pi]
  1900. angle = fabs(angle); // range [0, pi]
  1901.  
  1902. if (gfxPrefs::TouchActionEnabled()) {
  1903. HandlePanningWithTouchAction(angle);
  1904. } else {
  1905. if (GetAxisLockMode() == FREE) {
  1906. SetState(PANNING);
  1907. } else {
  1908. HandlePanning(angle);
  1909. }
  1910. }
  1911.  
  1912. if (IsInPanningState()) {
  1913. if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  1914. controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
  1915. }
  1916. return nsEventStatus_eConsumeNoDefault;
  1917. }
  1918. // Don't consume an event that didn't trigger a panning.
  1919. return nsEventStatus_eIgnore;
  1920. }
  1921.  
  1922. void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
  1923. ParentLayerPoint point = GetFirstTouchPoint(aEvent);
  1924. mX.UpdateWithTouchAtDevicePoint(point.x, aEvent.mTime);
  1925. mY.UpdateWithTouchAtDevicePoint(point.y, aEvent.mTime);
  1926. }
  1927.  
  1928. bool AsyncPanZoomController::AttemptScroll(const ParentLayerPoint& aStartPoint,
  1929. const ParentLayerPoint& aEndPoint,
  1930. OverscrollHandoffState& aOverscrollHandoffState) {
  1931.  
  1932. // "start - end" rather than "end - start" because e.g. moving your finger
  1933. // down (*positive* direction along y axis) causes the vertical scroll offset
  1934. // to *decrease* as the page follows your finger.
  1935. ParentLayerPoint displacement = aStartPoint - aEndPoint;
  1936.  
  1937. ParentLayerPoint overscroll; // will be used outside monitor block
  1938. {
  1939. ReentrantMonitorAutoEnter lock(mMonitor);
  1940.  
  1941. ParentLayerPoint adjustedDisplacement;
  1942. bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
  1943. bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y);
  1944. if (xChanged || yChanged) {
  1945. ScheduleComposite();
  1946. }
  1947.  
  1948. if (!IsZero(adjustedDisplacement)) {
  1949. ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
  1950. ScheduleCompositeAndMaybeRepaint();
  1951. UpdateSharedCompositorFrameMetrics();
  1952. }
  1953. }
  1954.  
  1955. // If we consumed the entire displacement as a normal scroll, great.
  1956. if (IsZero(overscroll)) {
  1957. return true;
  1958. }
  1959.  
  1960. // If there is overscroll, first try to hand it off to an APZC later
  1961. // in the handoff chain to consume (either as a normal scroll or as
  1962. // overscroll).
  1963. // Note: "+ overscroll" rather than "- overscroll" because "overscroll"
  1964. // is what's left of "displacement", and "displacement" is "start - end".
  1965. ++aOverscrollHandoffState.mChainIndex;
  1966. if (CallDispatchScroll(aEndPoint + overscroll, aEndPoint,
  1967. aOverscrollHandoffState)) {
  1968. return true;
  1969. }
  1970.  
  1971. // If there is no APZC later in the handoff chain that accepted the
  1972. // overscroll, try to accept it ourselves. We only accept it if we
  1973. // are pannable.
  1974. APZC_LOG("%p taking overscroll during panning\n", this);
  1975. return OverscrollForPanning(overscroll, aOverscrollHandoffState.mPanDistance);
  1976. }
  1977.  
  1978. bool AsyncPanZoomController::OverscrollForPanning(ParentLayerPoint aOverscroll,
  1979. const ScreenPoint& aPanDistance) {
  1980. // Only allow entering overscroll along an axis if the pan distance along
  1981. // that axis is greater than the pan distance along the other axis by a
  1982. // configurable factor. If we are already overscrolled, don't check this.
  1983. if (!IsOverscrolled()) {
  1984. if (aPanDistance.x < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.y) {
  1985. aOverscroll.x = 0;
  1986. }
  1987. if (aPanDistance.y < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.x) {
  1988. aOverscroll.y = 0;
  1989. }
  1990. }
  1991.  
  1992. return OverscrollBy(aOverscroll);
  1993. }
  1994.  
  1995. bool AsyncPanZoomController::OverscrollBy(const ParentLayerPoint& aOverscroll) {
  1996. if (!gfxPrefs::APZOverscrollEnabled()) {
  1997. return false;
  1998. }
  1999.  
  2000. ReentrantMonitorAutoEnter lock(mMonitor);
  2001. // Do not go into overscroll in a direction in which we have no room to
  2002. // scroll to begin with.
  2003. bool xCanScroll = mX.CanScroll();
  2004. bool yCanScroll = mY.CanScroll();
  2005. if (xCanScroll) {
  2006. mX.OverscrollBy(aOverscroll.x);
  2007. }
  2008. if (yCanScroll) {
  2009. mY.OverscrollBy(aOverscroll.y);
  2010. }
  2011. if (xCanScroll || yCanScroll) {
  2012. ScheduleComposite();
  2013. return true;
  2014. }
  2015. // TODO(botond): If one of the x- or y-overscroll was not accepted, we
  2016. // may want to propagate that one to an APZC earlier in the handoff chain.
  2017. return false;
  2018. }
  2019.  
  2020. nsRefPtr<const OverscrollHandoffChain> AsyncPanZoomController::BuildOverscrollHandoffChain() {
  2021. if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
  2022. return treeManagerLocal->BuildOverscrollHandoffChain(this);
  2023. }
  2024.  
  2025. // This APZC IsDestroyed(). To avoid callers having to special-case this
  2026. // scenario, just build a 1-element chain containing ourselves.
  2027. OverscrollHandoffChain* result = new OverscrollHandoffChain;
  2028. result->Add(this);
  2029. return result;
  2030. }
  2031.  
  2032. void AsyncPanZoomController::AcceptFling(const ParentLayerPoint& aVelocity,
  2033. const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
  2034. bool aHandoff) {
  2035. // We may have a pre-existing velocity for whatever reason (for example,
  2036. // a previously handed off fling). We don't want to clobber that.
  2037. mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
  2038. mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
  2039. SetState(FLING);
  2040. StartAnimation(new FlingAnimation(*this,
  2041. aOverscrollHandoffChain,
  2042. !aHandoff)); // only apply acceleration if this is an initial fling
  2043. }
  2044.  
  2045. bool AsyncPanZoomController::AttemptFling(ParentLayerPoint aVelocity,
  2046. const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
  2047. bool aHandoff) {
  2048. // If we are pannable, take over the fling ourselves.
  2049. if (IsPannable()) {
  2050. AcceptFling(aVelocity,
  2051. aOverscrollHandoffChain,
  2052. aHandoff);
  2053. return true;
  2054. }
  2055.  
  2056. return false;
  2057. }
  2058.  
  2059. void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelocity,
  2060. const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain) {
  2061. APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
  2062. if (!(treeManagerLocal && treeManagerLocal->DispatchFling(this,
  2063. aVelocity,
  2064. aOverscrollHandoffChain,
  2065. true /* handoff */))) {
  2066. // No one wanted the fling, so we "take it" ourselves by entering an
  2067. // overscroll animation starting with the fling's velocity.
  2068. if (IsPannable() && gfxPrefs::APZOverscrollEnabled()) {
  2069. StartOverscrollAnimation(aVelocity);
  2070. }
  2071. }
  2072. }
  2073.  
  2074. void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity) {
  2075. // We must call BuildOverscrollHandoffChain from this deferred callback
  2076. // function in order to avoid a deadlock when acquiring the tree lock.
  2077. HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain());
  2078. }
  2079.  
  2080. void AsyncPanZoomController::StartSmoothScroll() {
  2081. SetState(SMOOTH_SCROLL);
  2082. nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
  2083. // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
  2084. // appunits/second
  2085. nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
  2086. mY.GetVelocity())) * 1000.0f;
  2087. nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset());
  2088.  
  2089. StartAnimation(new SmoothScrollAnimation(*this,
  2090. initialPosition, initialVelocity,
  2091. destination,
  2092. gfxPrefs::ScrollBehaviorSpringConstant(),
  2093. gfxPrefs::ScrollBehaviorDampingRatio()));
  2094. }
  2095.  
  2096. void AsyncPanZoomController::StartOverscrollAnimation(const ParentLayerPoint& aVelocity) {
  2097. SetState(OVERSCROLL_ANIMATION);
  2098. StartAnimation(new OverscrollAnimation(*this, aVelocity));
  2099. }
  2100.  
  2101. bool AsyncPanZoomController::CallDispatchScroll(const ParentLayerPoint& aStartPoint,
  2102. const ParentLayerPoint& aEndPoint,
  2103. OverscrollHandoffState& aOverscrollHandoffState) {
  2104. // Make a local copy of the tree manager pointer and check if it's not
  2105. // null before calling DispatchScroll(). This is necessary because
  2106. // Destroy(), which nulls out mTreeManager, could be called concurrently.
  2107. APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
  2108. return treeManagerLocal
  2109. && treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
  2110. aOverscrollHandoffState);
  2111. }
  2112.  
  2113. void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
  2114. ParentLayerPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
  2115. ParentLayerPoint touchPoint = GetFirstTouchPoint(aEvent);
  2116.  
  2117. ScreenPoint panDistance = ToScreenCoordinates(
  2118. ParentLayerPoint(mX.PanDistance(touchPoint.x),
  2119. mY.PanDistance(touchPoint.y)),
  2120. PanStart());
  2121. HandlePanningUpdate(panDistance);
  2122.  
  2123. UpdateWithTouchAtDevicePoint(aEvent);
  2124.  
  2125. if (prevTouchPoint != touchPoint) {
  2126. OverscrollHandoffState handoffState(
  2127. *CurrentTouchBlock()->GetOverscrollHandoffChain(), panDistance);
  2128. CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
  2129. }
  2130. }
  2131.  
  2132. ParentLayerPoint AsyncPanZoomController::GetFirstTouchPoint(const MultiTouchInput& aEvent) {
  2133. return ((SingleTouchData&)aEvent.mTouches[0]).mLocalScreenPoint;
  2134. }
  2135.  
  2136. void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
  2137. {
  2138. ReentrantMonitorAutoEnter lock(mMonitor);
  2139. mAnimation = aAnimation;
  2140. mLastSampleTime = GetFrameTime();
  2141. ScheduleComposite();
  2142. }
  2143.  
  2144. void AsyncPanZoomController::CancelAnimation() {
  2145. ReentrantMonitorAutoEnter lock(mMonitor);
  2146. APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
  2147. SetState(NOTHING);
  2148. mAnimation = nullptr;
  2149. // Since there is no animation in progress now the axes should
  2150. // have no velocity either.
  2151. mX.SetVelocity(0);
  2152. mY.SetVelocity(0);
  2153. // Setting the state to nothing and cancelling the animation can
  2154. // preempt normal mechanisms for relieving overscroll, so we need to clear
  2155. // overscroll here.
  2156. if (mX.IsOverscrolled() || mY.IsOverscrolled()) {
  2157. ClearOverscroll();
  2158. RequestContentRepaint();
  2159. ScheduleComposite();
  2160. UpdateSharedCompositorFrameMetrics();
  2161. }
  2162. }
  2163.  
  2164. void AsyncPanZoomController::ClearOverscroll() {
  2165. ReentrantMonitorAutoEnter lock(mMonitor);
  2166. mX.ClearOverscroll();
  2167. mY.ClearOverscroll();
  2168. }
  2169.  
  2170. void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorParent) {
  2171. mCompositorParent = aCompositorParent;
  2172. }
  2173.  
  2174. void AsyncPanZoomController::ShareFrameMetricsAcrossProcesses() {
  2175. mSharingFrameMetricsAcrossProcesses = true;
  2176. }
  2177.  
  2178. void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
  2179. mFrameMetrics.ScrollBy(aOffset);
  2180. }
  2181.  
  2182. void AsyncPanZoomController::ScaleWithFocus(float aScale,
  2183. const CSSPoint& aFocus) {
  2184. mFrameMetrics.ZoomBy(aScale);
  2185. // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
  2186. // at the same position on the screen before and after the change in zoom. The below code
  2187. // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
  2188. // in-depth explanation of how.
  2189. mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
  2190. }
  2191.  
  2192. /**
  2193. * Enlarges the displayport along both axes based on the velocity.
  2194. */
  2195. static CSSSize
  2196. CalculateDisplayPortSize(const CSSSize& aCompositionSize,
  2197. const CSSPoint& aVelocity)
  2198. {
  2199. float xMultiplier = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed()
  2200. ? gfxPrefs::APZXStationarySizeMultiplier()
  2201. : gfxPrefs::APZXSkateSizeMultiplier();
  2202. float yMultiplier = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed()
  2203. ? gfxPrefs::APZYStationarySizeMultiplier()
  2204. : gfxPrefs::APZYSkateSizeMultiplier();
  2205.  
  2206. // Ensure that it is at least as large as the visible area inflated by the
  2207. // danger zone. If this is not the case then the "AboutToCheckerboard"
  2208. // function in TiledContentClient.cpp will return true even in the stable
  2209. // state.
  2210. float xSize = std::max(aCompositionSize.width * xMultiplier,
  2211. aCompositionSize.width + (2 * gfxPrefs::APZDangerZoneX()));
  2212. float ySize = std::max(aCompositionSize.height * yMultiplier,
  2213. aCompositionSize.height + (2 * gfxPrefs::APZDangerZoneY()));
  2214.  
  2215. return CSSSize(xSize, ySize);
  2216. }
  2217.  
  2218. /**
  2219. * Attempts to redistribute any area in the displayport that would get clipped
  2220. * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
  2221. * other axis, while maintaining total displayport area.
  2222. */
  2223. static void
  2224. RedistributeDisplayPortExcess(CSSSize& aDisplayPortSize,
  2225. const CSSRect& aScrollableRect)
  2226. {
  2227. float xSlack = std::max(0.0f, aDisplayPortSize.width - aScrollableRect.width);
  2228. float ySlack = std::max(0.0f, aDisplayPortSize.height - aScrollableRect.height);
  2229.  
  2230. if (ySlack > 0) {
  2231. // Reassign wasted y-axis displayport to the x-axis
  2232. aDisplayPortSize.height -= ySlack;
  2233. float xExtra = ySlack * aDisplayPortSize.width / aDisplayPortSize.height;
  2234. aDisplayPortSize.width += xExtra;
  2235. } else if (xSlack > 0) {
  2236. // Reassign wasted x-axis displayport to the y-axis
  2237. aDisplayPortSize.width -= xSlack;
  2238. float yExtra = xSlack * aDisplayPortSize.height / aDisplayPortSize.width;
  2239. aDisplayPortSize.height += yExtra;
  2240. }
  2241. }
  2242.  
  2243. /* static */
  2244. const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
  2245. const FrameMetrics& aFrameMetrics,
  2246. const ParentLayerPoint& aVelocity,
  2247. double aEstimatedPaintDuration)
  2248. {
  2249. CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
  2250. CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
  2251. CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
  2252. CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
  2253.  
  2254. // Calculate the displayport size based on how fast we're moving along each axis.
  2255. CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
  2256.  
  2257. if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
  2258. RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
  2259. }
  2260.  
  2261. // Offset the displayport, depending on how fast we're moving and the
  2262. // estimated time it takes to paint, to try to minimise checkerboarding.
  2263. float estimatedPaintDurationMillis = (float)(aEstimatedPaintDuration * 1000.0);
  2264. float paintFactor = (gfxPrefs::APZUsePaintDuration() ? estimatedPaintDurationMillis : 50.0f);
  2265. CSSRect displayPort = CSSRect(scrollOffset + (velocity * paintFactor * gfxPrefs::APZVelocityBias()),
  2266. displayPortSize);
  2267.  
  2268. // Re-center the displayport based on its expansion over the composition size.
  2269. displayPort.MoveBy((compositionSize.width - displayPort.width)/2.0f,
  2270. (compositionSize.height - displayPort.height)/2.0f);
  2271.  
  2272. // Make sure the displayport remains within the scrollable rect.
  2273. displayPort = displayPort.ForceInside(scrollableRect) - scrollOffset;
  2274.  
  2275. APZC_LOG_FM(aFrameMetrics,
  2276. "Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
  2277. displayPort.x, displayPort.y, displayPort.width, displayPort.height,
  2278. ToString(aVelocity).c_str(), (float)estimatedPaintDurationMillis);
  2279.  
  2280. CSSMargin cssMargins;
  2281. cssMargins.left = -displayPort.x;
  2282. cssMargins.top = -displayPort.y;
  2283. cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
  2284. cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
  2285.  
  2286. return cssMargins * aFrameMetrics.DisplayportPixelsPerCSSPixel();
  2287. }
  2288.  
  2289. void AsyncPanZoomController::ScheduleComposite() {
  2290. if (mCompositorParent) {
  2291. mCompositorParent->ScheduleRenderOnCompositorThread();
  2292. }
  2293. }
  2294.  
  2295. void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
  2296. ScheduleComposite();
  2297.  
  2298. TimeDuration timePaintDelta = mPaintThrottler.TimeSinceLastRequest(GetFrameTime());
  2299. if (timePaintDelta.ToMilliseconds() > gfxPrefs::APZPanRepaintInterval()) {
  2300. RequestContentRepaint();
  2301. }
  2302. }
  2303.  
  2304. void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
  2305. ReentrantMonitorAutoEnter lock(mMonitor);
  2306. RequestContentRepaint();
  2307. UpdateSharedCompositorFrameMetrics();
  2308. }
  2309.  
  2310. void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
  2311. APZC_LOG("%p flushing repaint for new input block\n", this);
  2312.  
  2313. ReentrantMonitorAutoEnter lock(mMonitor);
  2314. // We need to send a new repaint request unthrottled, but that
  2315. // will obsolete any pending repaint request in the paint throttler.
  2316. // Therefore we should clear out the pending task and restore the
  2317. // state of mLastPaintRequestMetrics to what it was before the
  2318. // pending task was queued.
  2319. mPaintThrottler.CancelPendingTask();
  2320. mLastPaintRequestMetrics = mLastDispatchedPaintMetrics;
  2321.  
  2322. RequestContentRepaint(mFrameMetrics, false /* not throttled */);
  2323. UpdateSharedCompositorFrameMetrics();
  2324. }
  2325.  
  2326. bool AsyncPanZoomController::SnapBackIfOverscrolled() {
  2327. ReentrantMonitorAutoEnter lock(mMonitor);
  2328. if (IsOverscrolled()) {
  2329. APZC_LOG("%p is overscrolled, starting snap-back\n", this);
  2330. StartOverscrollAnimation(ParentLayerPoint(0, 0));
  2331. return true;
  2332. }
  2333. return false;
  2334. }
  2335.  
  2336. bool AsyncPanZoomController::IsMovingFast() const {
  2337. ReentrantMonitorAutoEnter lock(mMonitor);
  2338. if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
  2339. APZC_LOG("%p is moving fast\n", this);
  2340. return true;
  2341. }
  2342. return false;
  2343. }
  2344.  
  2345. bool AsyncPanZoomController::IsPannable() const {
  2346. ReentrantMonitorAutoEnter lock(mMonitor);
  2347. return mX.CanScroll() || mY.CanScroll();
  2348. }
  2349.  
  2350. int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
  2351. nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
  2352. return listener ? listener->GetLastTouchIdentifier() : -1;
  2353. }
  2354.  
  2355. void AsyncPanZoomController::RequestContentRepaint() {
  2356. RequestContentRepaint(mFrameMetrics);
  2357. }
  2358.  
  2359. void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics, bool aThrottled) {
  2360. aFrameMetrics.SetDisplayPortMargins(
  2361. CalculatePendingDisplayPort(aFrameMetrics,
  2362. GetVelocityVector(),
  2363. mPaintThrottler.AverageDuration().ToSeconds()));
  2364. aFrameMetrics.SetUseDisplayPortMargins();
  2365.  
  2366. // If we're trying to paint what we already think is painted, discard this
  2367. // request since it's a pointless paint.
  2368. ScreenMargin marginDelta = (mLastPaintRequestMetrics.GetDisplayPortMargins()
  2369. - aFrameMetrics.GetDisplayPortMargins());
  2370. if (fabsf(marginDelta.left) < EPSILON &&
  2371. fabsf(marginDelta.top) < EPSILON &&
  2372. fabsf(marginDelta.right) < EPSILON &&
  2373. fabsf(marginDelta.bottom) < EPSILON &&
  2374. fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
  2375. aFrameMetrics.GetScrollOffset().x) < EPSILON &&
  2376. fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
  2377. aFrameMetrics.GetScrollOffset().y) < EPSILON &&
  2378. aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
  2379. fabsf(aFrameMetrics.GetViewport().width - mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
  2380. fabsf(aFrameMetrics.GetViewport().height - mLastPaintRequestMetrics.GetViewport().height) < EPSILON) {
  2381. return;
  2382. }
  2383.  
  2384. SendAsyncScrollEvent();
  2385. if (aThrottled) {
  2386. mPaintThrottler.PostTask(
  2387. FROM_HERE,
  2388. UniquePtr<CancelableTask>(NewRunnableMethod(this,
  2389. &AsyncPanZoomController::DispatchRepaintRequest,
  2390. aFrameMetrics)),
  2391. GetFrameTime());
  2392. } else {
  2393. DispatchRepaintRequest(aFrameMetrics);
  2394. }
  2395.  
  2396. aFrameMetrics.SetPresShellId(mLastContentPaintMetrics.GetPresShellId());
  2397. mLastPaintRequestMetrics = aFrameMetrics;
  2398. }
  2399.  
  2400. /*static*/ CSSRect
  2401. GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
  2402. {
  2403. // This computation is based on what happens in CalculatePendingDisplayPort. If that
  2404. // changes then this might need to change too
  2405. CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
  2406. aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
  2407. baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
  2408. return baseRect;
  2409. }
  2410.  
  2411. void
  2412. AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
  2413. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  2414. if (controller) {
  2415. APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
  2416. LogRendertraceRect(GetGuid(), "requested displayport", "yellow", GetDisplayPortRect(aFrameMetrics));
  2417.  
  2418. if (NS_IsMainThread()) {
  2419. controller->RequestContentRepaint(aFrameMetrics);
  2420. } else {
  2421. NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<FrameMetrics>(
  2422. controller, &GeckoContentController::RequestContentRepaint, aFrameMetrics));
  2423. }
  2424. mLastDispatchedPaintMetrics = aFrameMetrics;
  2425. }
  2426. }
  2427.  
  2428. void
  2429. AsyncPanZoomController::FireAsyncScrollOnTimeout()
  2430. {
  2431. if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  2432. ReentrantMonitorAutoEnter lock(mMonitor);
  2433. SendAsyncScrollEvent();
  2434. }
  2435. mAsyncScrollTimeoutTask = nullptr;
  2436. }
  2437.  
  2438. bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
  2439. Vector<Task*>* aOutDeferredTasks)
  2440. {
  2441. AssertOnCompositorThread();
  2442.  
  2443. // This function may get called multiple with the same sample time, because
  2444. // there may be multiple layers with this APZC, and each layer invokes this
  2445. // function during composition. However we only want to do one animation step
  2446. // per composition so we need to deduplicate these calls first.
  2447. if (mLastSampleTime == aSampleTime) {
  2448. return false;
  2449. }
  2450. TimeDuration sampleTimeDelta = aSampleTime - mLastSampleTime;
  2451. mLastSampleTime = aSampleTime;
  2452.  
  2453. if (mAnimation) {
  2454. bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta);
  2455. *aOutDeferredTasks = mAnimation->TakeDeferredTasks();
  2456. if (continueAnimation) {
  2457. if (mPaintThrottler.TimeSinceLastRequest(aSampleTime) >
  2458. mAnimation->mRepaintInterval) {
  2459. RequestContentRepaint();
  2460. }
  2461. } else {
  2462. mAnimation = nullptr;
  2463. SetState(NOTHING);
  2464. SendAsyncScrollEvent();
  2465. RequestContentRepaint();
  2466. }
  2467. UpdateSharedCompositorFrameMetrics();
  2468. return true;
  2469. }
  2470. return false;
  2471. }
  2472.  
  2473. Matrix4x4 AsyncPanZoomController::GetOverscrollTransform() const {
  2474. ReentrantMonitorAutoEnter lock(mMonitor);
  2475. if (!IsOverscrolled()) {
  2476. return Matrix4x4();
  2477. }
  2478.  
  2479. // The overscroll effect is a uniform stretch along the overscrolled axis,
  2480. // with the edge of the content where we have reached the end of the
  2481. // scrollable area pinned into place.
  2482.  
  2483. // The kStretchFactor parameter determines how much overscroll can stretch the
  2484. // content.
  2485. const float kStretchFactor = gfxPrefs::APZOverscrollStretchFactor();
  2486.  
  2487. // Compute the amount of the stretch along each axis. The stretch is
  2488. // proportional to the amount by which we are overscrolled along that axis.
  2489. ParentLayerSize compositionSize(mX.GetCompositionLength(), mY.GetCompositionLength());
  2490. float scaleX = 1 + kStretchFactor * fabsf(mX.GetOverscroll()) / mX.GetCompositionLength();
  2491. float scaleY = 1 + kStretchFactor * fabsf(mY.GetOverscroll()) / mY.GetCompositionLength();
  2492.  
  2493. // If an axis is in underscroll, the interpretation of its overscroll
  2494. // amount changes: instead of stretching along that axis, we compress.
  2495. if (mX.IsInUnderscroll()) {
  2496. scaleX = 1 / scaleX;
  2497. }
  2498. if (mY.IsInUnderscroll()) {
  2499. scaleY = 1 / scaleY;
  2500. }
  2501.  
  2502. // The scale is applied relative to the origin of the composition bounds, i.e.
  2503. // it keeps the top-left corner of the content in place. This is fine if we
  2504. // are overscrolling at the top or on the left, but if we are overscrolling
  2505. // at the bottom or on the right, we want the bottom or right edge of the
  2506. // content to stay in place instead, so we add a translation to compensate.
  2507. ParentLayerPoint translation;
  2508. bool overscrolledOnRight = (mX.GetOverscroll() > 0 && !mX.IsInUnderscroll())
  2509. || (mX.GetOverscroll() < 0 && mX.IsInUnderscroll());
  2510. if (overscrolledOnRight) {
  2511. ParentLayerCoord overscrolledCompositionWidth = scaleX * compositionSize.width;
  2512. ParentLayerCoord extraCompositionWidth = overscrolledCompositionWidth - compositionSize.width;
  2513. translation.x = -extraCompositionWidth;
  2514. }
  2515. bool overscrolledAtBottom = (mY.GetOverscroll() > 0 && !mY.IsInUnderscroll())
  2516. || (mY.GetOverscroll() < 0 && mY.IsInUnderscroll());
  2517. if (overscrolledAtBottom) {
  2518. ParentLayerCoord overscrolledCompositionHeight = scaleY * compositionSize.height;
  2519. ParentLayerCoord extraCompositionHeight = overscrolledCompositionHeight - compositionSize.height;
  2520. translation.y = -extraCompositionHeight;
  2521. }
  2522.  
  2523. // Combine the transformations into a matrix.
  2524. return Matrix4x4::Scaling(scaleX, scaleY, 1)
  2525. .PostTranslate(translation.x, translation.y, 0);
  2526. }
  2527.  
  2528. bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
  2529. {
  2530. AssertOnCompositorThread();
  2531.  
  2532. // Don't send any state-change notifications until the end of the function,
  2533. // because we may go through some intermediate states while we finish
  2534. // animations and start new ones.
  2535. StateChangeNotificationBlocker blocker(this);
  2536.  
  2537. // The eventual return value of this function. The compositor needs to know
  2538. // whether or not to advance by a frame as soon as it can. For example, if a
  2539. // fling is happening, it has to keep compositing so that the animation is
  2540. // smooth. If an animation frame is requested, it is the compositor's
  2541. // responsibility to schedule a composite.
  2542. mAsyncTransformAppliedToContent = false;
  2543. bool requestAnimationFrame = false;
  2544. Vector<Task*> deferredTasks;
  2545.  
  2546. {
  2547. ReentrantMonitorAutoEnter lock(mMonitor);
  2548.  
  2549. requestAnimationFrame = UpdateAnimation(aSampleTime, &deferredTasks);
  2550.  
  2551. LogRendertraceRect(GetGuid(), "viewport", "red",
  2552. CSSRect(mFrameMetrics.GetScrollOffset(),
  2553. mFrameMetrics.CalculateCompositedSizeInCssPixels()));
  2554.  
  2555. mCurrentAsyncScrollOffset = mFrameMetrics.GetScrollOffset();
  2556. }
  2557.  
  2558. // Execute any deferred tasks queued up by mAnimation's Sample() (called by
  2559. // UpdateAnimation()). This needs to be done after the monitor is released
  2560. // since the tasks are allowed to call APZCTreeManager methods which can grab
  2561. // the tree lock.
  2562. for (uint32_t i = 0; i < deferredTasks.length(); ++i) {
  2563. deferredTasks[i]->Run();
  2564. delete deferredTasks[i];
  2565. }
  2566.  
  2567. // One of the deferred tasks may have started a new animation. In this case,
  2568. // we want to ask the compositor to schedule a new composite.
  2569. requestAnimationFrame |= (mAnimation != nullptr);
  2570.  
  2571. // Cancel the mAsyncScrollTimeoutTask because we will fire a
  2572. // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
  2573. if (mAsyncScrollTimeoutTask) {
  2574. mAsyncScrollTimeoutTask->Cancel();
  2575. mAsyncScrollTimeoutTask = nullptr;
  2576. }
  2577. // Fire the mozbrowserasyncscroll event immediately if it's been
  2578. // sAsyncScrollThrottleTime ms since the last time we fired the event and the
  2579. // current scroll offset is different than the mLastAsyncScrollOffset we sent
  2580. // with the last event.
  2581. // Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
  2582. TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
  2583. if (delta.ToMilliseconds() > gfxPrefs::APZAsyncScrollThrottleTime() &&
  2584. mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
  2585. ReentrantMonitorAutoEnter lock(mMonitor);
  2586. mLastAsyncScrollTime = aSampleTime;
  2587. mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
  2588. SendAsyncScrollEvent();
  2589. } else {
  2590. mAsyncScrollTimeoutTask =
  2591. NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
  2592. MessageLoop::current()->PostDelayedTask(FROM_HERE,
  2593. mAsyncScrollTimeoutTask,
  2594. gfxPrefs::APZAsyncScrollTimeout());
  2595. }
  2596.  
  2597. return requestAnimationFrame;
  2598. }
  2599.  
  2600. void AsyncPanZoomController::SampleContentTransformForFrame(ViewTransform* aOutTransform,
  2601. ParentLayerPoint& aScrollOffset)
  2602. {
  2603. ReentrantMonitorAutoEnter lock(mMonitor);
  2604.  
  2605. aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
  2606. *aOutTransform = GetCurrentAsyncTransform();
  2607. }
  2608.  
  2609. ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() const {
  2610. ReentrantMonitorAutoEnter lock(mMonitor);
  2611.  
  2612. CSSPoint lastPaintScrollOffset;
  2613. if (mLastContentPaintMetrics.IsScrollable()) {
  2614. lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
  2615. }
  2616.  
  2617. CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
  2618. mTestAsyncScrollOffset;
  2619.  
  2620. // If checkerboarding has been disallowed, clamp the scroll position to stay
  2621. // within rendered content.
  2622. if (!gfxPrefs::APZAllowCheckerboarding() &&
  2623. !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
  2624. CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
  2625. CSSPoint maxScrollOffset = lastPaintScrollOffset +
  2626. CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedSize.width,
  2627. mLastContentPaintMetrics.mDisplayPort.YMost() - compositedSize.height);
  2628. CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
  2629.  
  2630. if (minScrollOffset.x < maxScrollOffset.x) {
  2631. currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
  2632. }
  2633. if (minScrollOffset.y < maxScrollOffset.y) {
  2634. currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
  2635. }
  2636. }
  2637.  
  2638. LayerToParentLayerScale scale(mFrameMetrics.mPresShellResolution // non-transient portion
  2639. * mFrameMetrics.GetAsyncZoom().scale); // transient portion
  2640. ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
  2641. * mFrameMetrics.GetZoom();
  2642.  
  2643. return ViewTransform(scale, -translation);
  2644. }
  2645.  
  2646. Matrix4x4 AsyncPanZoomController::GetNontransientAsyncTransform() const {
  2647. ReentrantMonitorAutoEnter lock(mMonitor);
  2648. return Matrix4x4::Scaling(mLastContentPaintMetrics.mPresShellResolution,
  2649. mLastContentPaintMetrics.mPresShellResolution,
  2650. 1.0f);
  2651. }
  2652.  
  2653. Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
  2654. ReentrantMonitorAutoEnter lock(mMonitor);
  2655.  
  2656. LayerPoint scrollChange =
  2657. (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
  2658. * mLastContentPaintMetrics.GetMDevPixelsPerCSSPixel()
  2659. * mLastContentPaintMetrics.mCumulativeResolution
  2660. // This transform ("LD" in the terminology of the comment above
  2661. // GetScreenToApzcTransform() in APZCTreeManager.h) is applied in a
  2662. // coordinate space that includes the APZC's CSS transform ("LC").
  2663. // This CSS transform is the identity unless this APZC sets a pres-shell
  2664. // resolution, in which case the transform has a post-scale that cancels
  2665. // out the pres-shell resolution. We simulate applying the "LC" transform
  2666. // by dividing by the pres-shell resolution. This will go away once
  2667. // bug 1076192 is fixed.
  2668. / mLastContentPaintMetrics.mPresShellResolution;
  2669.  
  2670. float zoomChange = mLastContentPaintMetrics.GetZoom().scale / mLastDispatchedPaintMetrics.GetZoom().scale;
  2671.  
  2672. return Matrix4x4::Translation(scrollChange.x, scrollChange.y, 0).
  2673. PostScale(zoomChange, zoomChange, 1);
  2674. }
  2675.  
  2676. bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
  2677. ReentrantMonitorAutoEnter lock(mMonitor);
  2678.  
  2679. if (!gfxPrefs::APZAllowCheckerboarding()) {
  2680. return false;
  2681. }
  2682.  
  2683. CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
  2684. CSSRect painted = mLastContentPaintMetrics.mDisplayPort + mLastContentPaintMetrics.GetScrollOffset();
  2685. painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1))); // fuzz for rounding error
  2686. CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
  2687. return !painted.Contains(visible);
  2688. }
  2689.  
  2690. void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
  2691. AssertOnCompositorThread();
  2692.  
  2693. ReentrantMonitorAutoEnter lock(mMonitor);
  2694. bool isDefault = mFrameMetrics.IsDefault();
  2695.  
  2696. mLastContentPaintMetrics = aLayerMetrics;
  2697.  
  2698. mFrameMetrics.SetMayHaveTouchListeners(aLayerMetrics.GetMayHaveTouchListeners());
  2699. mFrameMetrics.SetMayHaveTouchCaret(aLayerMetrics.GetMayHaveTouchCaret());
  2700. mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
  2701. APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
  2702.  
  2703. LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
  2704. LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
  2705. aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
  2706. if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
  2707. LogRendertraceRect(GetGuid(), "painted critical displayport", "darkgreen",
  2708. aLayerMetrics.mCriticalDisplayPort + aLayerMetrics.GetScrollOffset());
  2709. }
  2710.  
  2711. mPaintThrottler.TaskComplete(GetFrameTime());
  2712. bool needContentRepaint = false;
  2713. if (FuzzyEqualsAdditive(aLayerMetrics.mCompositionBounds.width, mFrameMetrics.mCompositionBounds.width) &&
  2714. FuzzyEqualsAdditive(aLayerMetrics.mCompositionBounds.height, mFrameMetrics.mCompositionBounds.height)) {
  2715. // Remote content has sync'd up to the composition geometry
  2716. // change, so we can accept the viewport it's calculated.
  2717. if (mFrameMetrics.GetViewport().width != aLayerMetrics.GetViewport().width ||
  2718. mFrameMetrics.GetViewport().height != aLayerMetrics.GetViewport().height) {
  2719. needContentRepaint = true;
  2720. }
  2721. mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
  2722. }
  2723.  
  2724. // If the layers update was not triggered by our own repaint request, then
  2725. // we want to take the new scroll offset. Check the scroll generation as well
  2726. // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
  2727. // update message.
  2728. bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
  2729. && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
  2730.  
  2731. bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
  2732. && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
  2733.  
  2734. if (aIsFirstPaint || isDefault) {
  2735. // Initialize our internal state to something sane when the content
  2736. // that was just painted is something we knew nothing about previously
  2737. mPaintThrottler.ClearHistory();
  2738. mPaintThrottler.SetMaxDurations(gfxPrefs::APZNumPaintDurationSamples());
  2739.  
  2740. CancelAnimation();
  2741.  
  2742. mFrameMetrics = aLayerMetrics;
  2743. mLastDispatchedPaintMetrics = aLayerMetrics;
  2744. ShareCompositorFrameMetrics();
  2745.  
  2746. if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) {
  2747. // A non-zero display port margin here indicates a displayport has
  2748. // been set by a previous APZC for the content at this guid. The
  2749. // scrollable rect may have changed since then, making the margins
  2750. // wrong, so we need to calculate a new display port.
  2751. APZC_LOG("%p detected non-empty margins which probably need updating\n", this);
  2752. needContentRepaint = true;
  2753. }
  2754. } else {
  2755. // If we're not taking the aLayerMetrics wholesale we still need to pull
  2756. // in some things into our local mFrameMetrics because these things are
  2757. // determined by Gecko and our copy in mFrameMetrics may be stale.
  2758.  
  2759. if (FuzzyEqualsAdditive(mFrameMetrics.mCompositionBounds.width, aLayerMetrics.mCompositionBounds.width) &&
  2760. mFrameMetrics.GetMDevPixelsPerCSSPixel() == aLayerMetrics.GetMDevPixelsPerCSSPixel()) {
  2761. // Any change to the pres shell resolution was requested by APZ and is
  2762. // already included in our zoom; however, other components of the
  2763. // cumulative resolution (a parent document's pres-shell resolution, or
  2764. // the css-driven resolution) may have changed, and we need to update
  2765. // our zoom to reflect that. Note that we can't just take
  2766. // aLayerMetrics.mZoom because the APZ may have additional async zoom
  2767. // since the repaint request.
  2768. float totalResolutionChange = aLayerMetrics.mCumulativeResolution.scale
  2769. / mFrameMetrics.mCumulativeResolution.scale;
  2770. float presShellResolutionChange = aLayerMetrics.mPresShellResolution
  2771. / mFrameMetrics.mPresShellResolution;
  2772. mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
  2773. } else {
  2774. // Take the new zoom as either device scale or composition width or both
  2775. // got changed (e.g. due to orientation change).
  2776. mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
  2777. mFrameMetrics.GetMDevPixelsPerCSSPixel().scale = aLayerMetrics.GetMDevPixelsPerCSSPixel().scale;
  2778. }
  2779. if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aLayerMetrics.mScrollableRect)) {
  2780. mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
  2781. needContentRepaint = true;
  2782. }
  2783. mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
  2784. mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
  2785. mFrameMetrics.mPresShellResolution = aLayerMetrics.mPresShellResolution;
  2786. mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
  2787. mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab());
  2788.  
  2789. if (scrollOffsetUpdated) {
  2790. APZC_LOG("%p updating scroll offset from %s to %s\n", this,
  2791. ToString(mFrameMetrics.GetScrollOffset()).c_str(),
  2792. ToString(aLayerMetrics.GetScrollOffset()).c_str());
  2793.  
  2794. mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
  2795.  
  2796. // Cancel the animation (which will also trigger a repaint request)
  2797. // after we update the scroll offset above. Otherwise we can be left
  2798. // in a state where things are out of sync.
  2799. CancelAnimation();
  2800.  
  2801. // Because of the scroll offset update, any inflight paint requests are
  2802. // going to be ignored by layout, and so mLastDispatchedPaintMetrics
  2803. // becomes incorrect for the purposes of calculating the LD transform. To
  2804. // correct this we need to update mLastDispatchedPaintMetrics to be the
  2805. // last thing we know was painted by Gecko.
  2806. mLastDispatchedPaintMetrics = aLayerMetrics;
  2807. }
  2808. }
  2809.  
  2810. if (smoothScrollRequested) {
  2811. // A smooth scroll has been requested for animation on the compositor
  2812. // thread. This flag will be reset by the main thread when it receives
  2813. // the scroll update acknowledgement.
  2814.  
  2815. APZC_LOG("%p smooth scrolling from %s to %s\n", this,
  2816. Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
  2817. Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
  2818.  
  2819. mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
  2820. CancelAnimation();
  2821. mLastDispatchedPaintMetrics = aLayerMetrics;
  2822. StartSmoothScroll();
  2823.  
  2824. scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
  2825. }
  2826.  
  2827. if (scrollOffsetUpdated) {
  2828. // Once layout issues a scroll offset update, it becomes impervious to
  2829. // scroll offset updates from APZ until we acknowledge the update it sent.
  2830. // This prevents APZ updates from clobbering scroll updates from other
  2831. // more "legitimate" sources like content scripts.
  2832. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  2833. if (controller) {
  2834. APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration());
  2835. controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
  2836. aLayerMetrics.GetScrollGeneration());
  2837. }
  2838. }
  2839.  
  2840. if (needContentRepaint) {
  2841. RequestContentRepaint();
  2842. }
  2843. UpdateSharedCompositorFrameMetrics();
  2844. }
  2845.  
  2846. const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const {
  2847. mMonitor.AssertCurrentThreadIn();
  2848. return mFrameMetrics;
  2849. }
  2850.  
  2851. APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
  2852. mMonitor.AssertNotCurrentThreadIn();
  2853. return mTreeManager;
  2854. }
  2855.  
  2856. void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
  2857. if (!aRect.IsFinite()) {
  2858. NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...\n");
  2859. return;
  2860. }
  2861.  
  2862. SetState(ANIMATING_ZOOM);
  2863.  
  2864. {
  2865. ReentrantMonitorAutoEnter lock(mMonitor);
  2866.  
  2867. ParentLayerRect compositionBounds = mFrameMetrics.mCompositionBounds;
  2868. CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
  2869. CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
  2870. CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoom();
  2871. CSSToParentLayerScale targetZoom;
  2872.  
  2873. // The minimum zoom to prevent over-zoom-out.
  2874. // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
  2875. // then the CSS content rect, in layers pixels, will be smaller than the
  2876. // composition bounds. If this happens, we can't fill the target composited
  2877. // area with this frame.
  2878. CSSToParentLayerScale localMinZoom(std::max(mZoomConstraints.mMinZoom.scale,
  2879. std::max(compositionBounds.width / cssPageRect.width,
  2880. compositionBounds.height / cssPageRect.height)));
  2881. CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom;
  2882.  
  2883. if (!aRect.IsEmpty()) {
  2884. // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
  2885. aRect = aRect.Intersect(cssPageRect);
  2886. targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  2887. compositionBounds.height / aRect.height));
  2888. }
  2889. // 1. If the rect is empty, request received from browserElementScrolling.js
  2890. // 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
  2891. // 3. currentZoom is equal to localMinZoom and user still double-tapping it
  2892. // Treat these three cases as a request to zoom out as much as possible.
  2893. if (aRect.IsEmpty() ||
  2894. (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
  2895. (currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
  2896. CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
  2897. float y = scrollOffset.y;
  2898. float newHeight =
  2899. cssPageRect.width * (compositedSize.height / compositedSize.width);
  2900. float dh = compositedSize.height - newHeight;
  2901.  
  2902. aRect = CSSRect(0.0f,
  2903. y + dh/2,
  2904. cssPageRect.width,
  2905. newHeight);
  2906. aRect = aRect.Intersect(cssPageRect);
  2907. targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
  2908. compositionBounds.height / aRect.height));
  2909. }
  2910.  
  2911. targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
  2912. FrameMetrics endZoomToMetrics = mFrameMetrics;
  2913. endZoomToMetrics.SetZoom(targetZoom);
  2914.  
  2915. // Adjust the zoomToRect to a sensible position to prevent overscrolling.
  2916. CSSSize sizeAfterZoom = endZoomToMetrics.CalculateCompositedSizeInCssPixels();
  2917.  
  2918. // If either of these conditions are met, the page will be
  2919. // overscrolled after zoomed
  2920. if (aRect.y + sizeAfterZoom.height > cssPageRect.height) {
  2921. aRect.y = cssPageRect.height - sizeAfterZoom.height;
  2922. aRect.y = aRect.y > 0 ? aRect.y : 0;
  2923. }
  2924. if (aRect.x + sizeAfterZoom.width > cssPageRect.width) {
  2925. aRect.x = cssPageRect.width - sizeAfterZoom.width;
  2926. aRect.x = aRect.x > 0 ? aRect.x : 0;
  2927. }
  2928.  
  2929. endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
  2930. endZoomToMetrics.SetDisplayPortMargins(
  2931. CalculatePendingDisplayPort(endZoomToMetrics,
  2932. ParentLayerPoint(0,0),
  2933. 0));
  2934. endZoomToMetrics.SetUseDisplayPortMargins();
  2935.  
  2936. StartAnimation(new ZoomAnimation(
  2937. mFrameMetrics.GetScrollOffset(),
  2938. mFrameMetrics.GetZoom(),
  2939. endZoomToMetrics.GetScrollOffset(),
  2940. endZoomToMetrics.GetZoom()));
  2941.  
  2942. // Schedule a repaint now, so the new displayport will be painted before the
  2943. // animation finishes.
  2944. RequestContentRepaint(endZoomToMetrics);
  2945. }
  2946. }
  2947.  
  2948. bool
  2949. AsyncPanZoomController::NeedToWaitForContent() const
  2950. {
  2951. return (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret());
  2952. }
  2953.  
  2954. TouchBlockState*
  2955. AsyncPanZoomController::CurrentTouchBlock()
  2956. {
  2957. return GetInputQueue()->CurrentTouchBlock();
  2958. }
  2959.  
  2960. void
  2961. AsyncPanZoomController::ResetInputState()
  2962. {
  2963. SetState(NOTHING);
  2964. // Also clear the state in the gesture event listener
  2965. nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
  2966. if (listener) {
  2967. MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
  2968. listener->HandleInputEvent(cancel);
  2969. }
  2970. }
  2971.  
  2972. bool
  2973. AsyncPanZoomController::HasReadyTouchBlock()
  2974. {
  2975. return GetInputQueue()->HasReadyTouchBlock();
  2976. }
  2977.  
  2978. AsyncPanZoomController::TouchBehaviorFlags
  2979. AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
  2980. // Here we need to perform a hit testing over the touch-action regions attached to the
  2981. // layer associated with current apzc.
  2982. // Currently they are in progress, for more info see bug 928833.
  2983. return AllowedTouchBehavior::UNKNOWN;
  2984. }
  2985.  
  2986. void AsyncPanZoomController::SetState(PanZoomState aNewState)
  2987. {
  2988. PanZoomState oldState;
  2989.  
  2990. // Intentional scoping for mutex
  2991. {
  2992. ReentrantMonitorAutoEnter lock(mMonitor);
  2993. oldState = mState;
  2994. mState = aNewState;
  2995. }
  2996.  
  2997. DispatchStateChangeNotification(oldState, aNewState);
  2998. }
  2999.  
  3000. void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldState,
  3001. PanZoomState aNewState)
  3002. {
  3003. { // scope the lock
  3004. ReentrantMonitorAutoEnter lock(mMonitor);
  3005. if (mNotificationBlockers > 0) {
  3006. return;
  3007. }
  3008. }
  3009.  
  3010. if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
  3011. if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
  3012. controller->NotifyAPZStateChange(
  3013. GetGuid(), APZStateChange::TransformBegin);
  3014. } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
  3015. controller->NotifyAPZStateChange(
  3016. GetGuid(), APZStateChange::TransformEnd);
  3017. }
  3018. }
  3019. }
  3020.  
  3021. bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
  3022. return !(aState == NOTHING || aState == TOUCHING);
  3023. }
  3024.  
  3025. bool AsyncPanZoomController::IsInPanningState() const {
  3026. return (mState == PANNING || mState == PANNING_LOCKED_X || mState == PANNING_LOCKED_Y);
  3027. }
  3028.  
  3029. void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
  3030. APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
  3031. aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
  3032. if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
  3033. NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
  3034. return;
  3035. }
  3036. // inf float values and other bad cases should be sanitized by the code below.
  3037. mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
  3038. mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
  3039. mZoomConstraints.mMinZoom = (MIN_ZOOM > aConstraints.mMinZoom ? MIN_ZOOM : aConstraints.mMinZoom);
  3040. mZoomConstraints.mMaxZoom = (MAX_ZOOM > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : MAX_ZOOM);
  3041. if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
  3042. mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
  3043. }
  3044. }
  3045.  
  3046. ZoomConstraints
  3047. AsyncPanZoomController::GetZoomConstraints() const
  3048. {
  3049. return mZoomConstraints;
  3050. }
  3051.  
  3052.  
  3053. void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
  3054. AssertOnControllerThread();
  3055. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  3056. if (controller) {
  3057. controller->PostDelayedTask(aTask, aDelayMs);
  3058. }
  3059. }
  3060.  
  3061. void AsyncPanZoomController::SendAsyncScrollEvent() {
  3062. nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
  3063. if (!controller) {
  3064. return;
  3065. }
  3066.  
  3067. bool isRoot;
  3068. CSSRect contentRect;
  3069. CSSSize scrollableSize;
  3070. {
  3071. ReentrantMonitorAutoEnter lock(mMonitor);
  3072.  
  3073. isRoot = mFrameMetrics.GetIsRoot();
  3074. scrollableSize = mFrameMetrics.mScrollableRect.Size();
  3075. contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
  3076. contentRect.MoveTo(mCurrentAsyncScrollOffset);
  3077. }
  3078.  
  3079. controller->SendAsyncScrollDOMEvent(isRoot, contentRect, scrollableSize);
  3080. }
  3081.  
  3082. bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
  3083. {
  3084. return aGuid == GetGuid();
  3085. }
  3086.  
  3087. void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const
  3088. {
  3089. if (aGuidOut) {
  3090. *aGuidOut = GetGuid();
  3091. }
  3092. }
  3093.  
  3094. ScrollableLayerGuid AsyncPanZoomController::GetGuid() const
  3095. {
  3096. return ScrollableLayerGuid(mLayersId, mFrameMetrics);
  3097. }
  3098.  
  3099. void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics()
  3100. {
  3101. mMonitor.AssertCurrentThreadIn();
  3102.  
  3103. FrameMetrics* frame = mSharedFrameMetricsBuffer ?
  3104. static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory()) : nullptr;
  3105.  
  3106. if (frame && mSharedLock && gfxPlatform::GetPlatform()->UseProgressivePaint()) {
  3107. mSharedLock->Lock();
  3108. *frame = mFrameMetrics.MakePODObject();
  3109. mSharedLock->Unlock();
  3110. }
  3111. }
  3112.  
  3113. void AsyncPanZoomController::ShareCompositorFrameMetrics() {
  3114.  
  3115. PCompositorParent* compositor = GetSharedFrameMetricsCompositor();
  3116.  
  3117. // Only create the shared memory buffer if it hasn't already been created,
  3118. // we are using progressive tile painting, and we have a
  3119. // compositor to pass the shared memory back to the content process/thread.
  3120. if (!mSharedFrameMetricsBuffer && compositor && gfxPlatform::GetPlatform()->UseProgressivePaint()) {
  3121.  
  3122. // Create shared memory and initialize it with the current FrameMetrics value
  3123. mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
  3124. FrameMetrics* frame = nullptr;
  3125. mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
  3126. mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
  3127. frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
  3128.  
  3129. if (frame) {
  3130.  
  3131. { // scope the monitor, only needed to copy the FrameMetrics.
  3132. ReentrantMonitorAutoEnter lock(mMonitor);
  3133. *frame = mFrameMetrics;
  3134. }
  3135.  
  3136. // Get the process id of the content process
  3137. base::ProcessHandle processHandle = compositor->OtherProcess();
  3138. ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
  3139.  
  3140. // Get the shared memory handle to share with the content process
  3141. mSharedFrameMetricsBuffer->ShareToProcess(processHandle, &mem);
  3142.  
  3143. // Get the cross process mutex handle to share with the content process
  3144. mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
  3145. CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(processHandle);
  3146.  
  3147. // Send the shared memory handle and cross process handle to the content
  3148. // process by an asynchronous ipc call. Include the APZC unique ID
  3149. // so the content process know which APZC sent this shared FrameMetrics.
  3150. if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mAPZCId)) {
  3151. APZC_LOG("%p failed to share FrameMetrics with content process.", this);
  3152. }
  3153. }
  3154. }
  3155. }
  3156.  
  3157. }
  3158. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement