Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "AsyncPanZoomController.h" // for AsyncPanZoomController, etc
8
9
#include <math.h> // for fabsf, fabs, atan2
10
#include <stdint.h> // for uint32_t, uint64_t
11
#include <sys/types.h> // for int32_t
12
#include <algorithm> // for max, min
13
14
#include "APZCTreeManager.h" // for APZCTreeManager
15
#include "AsyncPanZoomAnimation.h" // for AsyncPanZoomAnimation
16
#include "AutoDirWheelDeltaAdjuster.h" // for APZAutoDirWheelDeltaAdjuster
17
#include "AutoscrollAnimation.h" // for AutoscrollAnimation
18
#include "Axis.h" // for AxisX, AxisY, Axis, etc
19
#include "CheckerboardEvent.h" // for CheckerboardEvent
20
#include "Compositor.h" // for Compositor
21
#include "DesktopFlingPhysics.h" // for DesktopFlingPhysics
22
#include "FrameMetrics.h" // for FrameMetrics, etc
23
#include "GenericFlingAnimation.h" // for GenericFlingAnimation
24
#include "GestureEventListener.h" // for GestureEventListener
25
#include "HitTestingTreeNode.h" // for HitTestingTreeNode
26
#include "InputData.h" // for MultiTouchInput, etc
27
#include "InputBlockState.h" // for InputBlockState, TouchBlockState
28
#include "InputQueue.h" // for InputQueue
29
#include "Overscroll.h" // for OverscrollAnimation
30
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
31
#include "SimpleVelocityTracker.h" // for SimpleVelocityTracker
32
#include "Units.h" // for CSSRect, CSSPoint, etc
33
#include "UnitTransforms.h" // for TransformTo
34
#include "base/message_loop.h" // for MessageLoop
35
#include "base/task.h" // for NewRunnableMethod, etc
36
#include "gfxTypes.h" // for gfxFloat
37
#include "LayersLogging.h" // for print_stderr
38
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
39
#include "mozilla/BasicEvents.h" // for Modifiers, MODIFIER_*
40
#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
41
#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
42
#include "mozilla/EventForwards.h" // for nsEventStatus_*
43
#include "mozilla/EventStateManager.h" // for EventStateManager
44
#include "mozilla/MouseEvents.h" // for WidgetWheelEvent
45
#include "mozilla/Preferences.h" // for Preferences
46
#include "mozilla/RecursiveMutex.h" // for RecursiveMutexAutoLock, etc
47
#include "mozilla/RefPtr.h" // for RefPtr
48
#include "mozilla/StaticPrefs_apz.h"
49
#include "mozilla/StaticPrefs_general.h"
50
#include "mozilla/StaticPrefs_gfx.h"
51
#include "mozilla/StaticPrefs_mousewheel.h"
52
#include "mozilla/StaticPrefs_layers.h"
53
#include "mozilla/StaticPrefs_layout.h"
54
#include "mozilla/StaticPrefs_slider.h"
55
#include "mozilla/StaticPrefs_test.h"
56
#include "mozilla/StaticPrefs_toolkit.h"
57
#include "mozilla/Telemetry.h" // for Telemetry
58
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
59
#include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
60
// note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
61
#include "mozilla/dom/Touch.h" // for Touch
62
#include "mozilla/gfx/BasePoint.h" // for BasePoint
63
#include "mozilla/gfx/BaseRect.h" // for BaseRect
64
#include "mozilla/gfx/Point.h" // for Point, RoundedToInt, etc
65
#include "mozilla/gfx/Rect.h" // for RoundedIn
66
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
67
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
68
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
69
#include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
70
#include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
71
#include "mozilla/layers/CompositorController.h" // for CompositorController
72
#include "mozilla/layers/DirectionUtils.h" // for GetAxis{Start,End,Length,Scale}
73
#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
74
#include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
75
#include "mozilla/mozalloc.h" // for operator new, etc
76
#include "mozilla/Unused.h" // for unused
77
#include "mozilla/FloatingPoint.h" // for FuzzyEquals*
78
#include "nsAlgorithm.h" // for clamped
79
#include "nsCOMPtr.h" // for already_AddRefed
80
#include "nsDebug.h" // for NS_WARNING
81
#include "nsMathUtils.h" // for NS_hypot
82
#include "nsPoint.h" // for nsIntPoint
83
#include "nsStyleConsts.h"
84
#include "nsTimingFunction.h"
85
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
86
#include "nsThreadUtils.h" // for NS_IsMainThread
87
#include "nsViewportInfo.h" // for kViewportMinScale, kViewportMaxScale
88
#include "prsystem.h" // for PR_GetPhysicalMemorySize
89
#include "SharedMemoryBasic.h" // for SharedMemoryBasic
90
#include "ScrollSnap.h" // for ScrollSnapUtils
91
#include "ScrollAnimationPhysics.h" // for ComputeAcceleratedWheelDelta
92
#include "WheelScrollAnimation.h"
93
#include "KeyboardScrollAnimation.h"
94
#if defined(MOZ_WIDGET_ANDROID)
95
# include "AndroidAPZ.h"
96
# include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
97
#endif // defined(MOZ_WIDGET_ANDROID)
98
99
#define ENABLE_APZC_LOGGING 0
100
// #define ENABLE_APZC_LOGGING 1
101
102
#if ENABLE_APZC_LOGGING
103
# define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
104
# define APZC_LOG_FM(fm, prefix, ...) \
105
{ \
106
std::stringstream ss; \
107
ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
108
AppendToString(ss, fm, ":", "", true); \
109
APZC_LOG("%s\n", ss.str().c_str()); \
110
}
111
#else
112
# define APZC_LOG(...)
113
# define APZC_LOG_FM(fm, prefix, ...)
114
#endif
115
116
namespace mozilla {
117
namespace layers {
118
119
typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
120
typedef GeckoContentController::APZStateChange APZStateChange;
121
typedef GeckoContentController::TapType TapType;
122
typedef mozilla::gfx::Point Point;
123
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
124
using mozilla::gfx::PointTyped;
125
126
// Choose between platform-specific implementations.
127
#ifdef MOZ_WIDGET_ANDROID
128
typedef WidgetOverscrollEffect OverscrollEffect;
129
typedef AndroidSpecificState PlatformSpecificState;
130
#else
131
typedef GenericOverscrollEffect OverscrollEffect;
132
typedef PlatformSpecificStateBase
133
PlatformSpecificState; // no extra state, just use the base class
134
#endif
135
136
/**
137
* \page APZCPrefs APZ preferences
138
*
139
* The following prefs are used to control the behaviour of the APZC.
140
* The default values are provided in StaticPrefs.yaml.
141
*
142
* \li\b apz.allow_double_tap_zooming
143
* Pref that allows or disallows double tap to zoom
144
*
145
* \li\b apz.allow_immediate_handoff
146
* If set to true, scroll can be handed off from one APZC to another within
147
* a single input block. If set to false, a single input block can only
148
* scroll one APZC.
149
*
150
* \li\b apz.android.chrome_fling_physics.enabled
151
* If set to true, APZ uses a fling physical model similar to Chrome's
152
* on Android, rather than Android's StackScroller.
153
*
154
* \li\b apz.android.chrome_fling_physics.friction
155
* A tunable parameter for Chrome fling physics on Android that governs
156
* how quickly a fling animation slows down due to friction (and therefore
157
* also how far it reaches). Should be in the range [0-1].
158
*
159
* \li\b apz.android.chrome_fling_physics.inflexion
160
* A tunable parameter for Chrome fling physics on Android that governs
161
* the shape of the fling curve. Should be in the range [0-1].
162
*
163
* \li\b apz.android.chrome_fling_physics.stop_threshold
164
* A tunable parameter for Chrome fling physics on Android that governs
165
* how close the fling animation has to get to its target destination
166
* before it stops.
167
* Units: ParentLayer pixels
168
*
169
* \li\b apz.autoscroll.enabled
170
* If set to true, autoscrolling is driven by APZ rather than the content
171
* process main thread.
172
*
173
* \li\b apz.axis_lock.mode
174
* The preferred axis locking style. See AxisLockMode for possible values.
175
*
176
* \li\b apz.axis_lock.lock_angle
177
* Angle from axis within which we stay axis-locked.\n
178
* Units: radians
179
*
180
* \li\b apz.axis_lock.breakout_threshold
181
* Distance in inches the user must pan before axis lock can be broken.\n
182
* Units: (real-world, i.e. screen) inches
183
*
184
* \li\b apz.axis_lock.breakout_angle
185
* Angle at which axis lock can be broken.\n
186
* Units: radians
187
*
188
* \li\b apz.axis_lock.direct_pan_angle
189
* If the angle from an axis to the line drawn by a pan move is less than
190
* this value, we can assume that panning can be done in the allowed direction
191
* (horizontal or vertical).\n
192
* Currently used only for touch-action css property stuff and was addded to
193
* keep behaviour consistent with IE.\n
194
* Units: radians
195
*
196
* \li\b apz.content_response_timeout
197
* Amount of time before we timeout response from content. For example, if
198
* content is being unruly/slow and we don't get a response back within this
199
* time, we will just pretend that content did not preventDefault any touch
200
* events we dispatched to it.\n
201
* Units: milliseconds
202
*
203
* \li\b apz.danger_zone_x
204
* \li\b apz.danger_zone_y
205
* When drawing high-res tiles, we drop down to drawing low-res tiles
206
* when we know we can't keep up with the scrolling. The way we determine
207
* this is by checking if we are entering the "danger zone", which is the
208
* boundary of the painted content. For example, if the painted content
209
* goes from y=0...1000 and the visible portion is y=250...750 then
210
* we're far from checkerboarding. If we get to y=490...990 though then we're
211
* only 10 pixels away from showing checkerboarding so we are probably in
212
* a state where we can't keep up with scrolling. The danger zone prefs specify
213
* how wide this margin is; in the above example a y-axis danger zone of 10
214
* pixels would make us drop to low-res at y=490...990.\n
215
* This value is in layer pixels.
216
*
217
* \li\b apz.disable_for_scroll_linked_effects
218
* Setting this pref to true will disable APZ scrolling on documents where
219
* scroll-linked effects are detected. A scroll linked effect is detected if
220
* positioning or transform properties are updated inside a scroll event
221
* dispatch; we assume that such an update is in response to the scroll event
222
* and is therefore a scroll-linked effect which will be laggy with APZ
223
* scrolling.
224
*
225
* \li\b apz.displayport_expiry_ms
226
* While a scrollable frame is scrolling async, we set a displayport on it
227
* to make sure it is layerized. However this takes up memory, so once the
228
* scrolling stops we want to remove the displayport. This pref controls how
229
* long after scrolling stops the displayport is removed. A value of 0 will
230
* disable the expiry behavior entirely.
231
* Units: milliseconds
232
*
233
* \li\b apz.drag.enabled
234
* Setting this pref to true will cause APZ to handle mouse-dragging of
235
* scrollbar thumbs.
236
*
237
* \li\b apz.drag.initial.enabled
238
* Setting this pref to true will cause APZ to try to handle mouse-dragging
239
* of scrollbar thumbs without an initial round-trip to content to start it
240
* if possible. Only has an effect if apz.drag.enabled is also true.
241
*
242
* \li\b apz.drag.touch.enabled
243
* Setting this pref to true will cause APZ to handle touch-dragging of
244
* scrollbar thumbs. Only has an effect if apz.drag.enabled is also true.
245
*
246
* \li\b apz.enlarge_displayport_when_clipped
247
* Pref that enables enlarging of the displayport along one axis when the
248
* generated displayport's size is beyond that of the scrollable rect on the
249
* opposite axis.
250
*
251
* \li\b apz.fling_accel_interval_ms
252
* The time that determines whether a second fling will be treated as
253
* accelerated. If two flings are started within this interval, the second one
254
* will be accelerated. Setting an interval of 0 means that acceleration will
255
* be disabled.\n
256
* Units: milliseconds
257
*
258
* \li\b apz.fling_accel_min_velocity
259
* The minimum velocity of the second fling for it to be considered for fling
260
* acceleration.
261
* Units: screen pixels per milliseconds
262
*
263
* \li\b apz.fling_accel_base_mult
264
* \li\b apz.fling_accel_supplemental_mult
265
* When applying an acceleration on a fling, the new computed velocity is
266
* (new_fling_velocity * base_mult) + (old_velocity * supplemental_mult).
267
* The base_mult and supplemental_mult multiplier values are controlled by
268
* these prefs. Note that "old_velocity" here is the initial velocity of the
269
* previous fling _after_ acceleration was applied to it (if applicable).
270
*
271
* \li\b apz.fling_curve_function_x1
272
* \li\b apz.fling_curve_function_y1
273
* \li\b apz.fling_curve_function_x2
274
* \li\b apz.fling_curve_function_y2
275
* \li\b apz.fling_curve_threshold_inches_per_ms
276
* These five parameters define a Bezier curve function and threshold used to
277
* increase the actual velocity relative to the user's finger velocity. When the
278
* finger velocity is below the threshold (or if the threshold is not positive),
279
* the velocity is used as-is. If the finger velocity exceeds the threshold
280
* velocity, then the function defined by the curve is applied on the part of
281
* the velocity that exceeds the threshold. Note that the upper bound of the
282
* velocity is still specified by the \b apz.max_velocity_inches_per_ms pref,
283
* and the function will smoothly curve the velocity from the threshold to the
284
* max. In general the function parameters chosen should define an ease-out
285
* curve in order to increase the velocity in this range, or an ease-in curve to
286
* decrease the velocity. A straight-line curve is equivalent to disabling the
287
* curve entirely by setting the threshold to -1. The max velocity pref must
288
* also be set in order for the curving to take effect, as it defines the upper
289
* bound of the velocity curve.\n
290
* The points (x1, y1) and (x2, y2) used as the two intermediate control points
291
* in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
292
* Some example values for these prefs can be found at\n
294
*
295
* \li\b apz.fling_friction
296
* Amount of friction applied during flings. This is used in the following
297
* formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
298
* for a new sample, v(t0) is the velocity at the previous sample, f is the
299
* value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
300
* that has elapsed between the two samples.\n
301
* NOTE: Not currently used in Android fling calculations.
302
*
303
* \li\b apz.fling_min_velocity_threshold
304
* Minimum velocity for a fling to actually kick off. If the user pans and lifts
305
* their finger such that the velocity is smaller than this amount, no fling
306
* is initiated.\n
307
* Units: screen pixels per millisecond
308
*
309
* \li\b apz.fling_stop_on_tap_threshold
310
* When flinging, if the velocity is above this number, then a tap on the
311
* screen will stop the fling without dispatching a tap to content. If the
312
* velocity is below this threshold a tap will also be dispatched.
313
* Note: when modifying this pref be sure to run the APZC gtests as some of
314
* them depend on the value of this pref.\n
315
* Units: screen pixels per millisecond
316
*
317
* \li\b apz.fling_stopped_threshold
318
* When flinging, if the velocity goes below this number, we just stop the
319
* animation completely. This is to prevent asymptotically approaching 0
320
* velocity and rerendering unnecessarily.\n
321
* Units: screen pixels per millisecond.\n
322
* NOTE: Should not be set to anything
323
* other than 0.0 for Android except for tests to disable flings.
324
*
325
* \li\b apz.frame_delay.enabled
326
* If this is set to true, changes to the async scroll offset and async zoom
327
* will not be immediately reflected in GetCurrentAsyncTransform() when called
328
* with |AsyncTransformConsumer::eForCompositing|. Rather, the transform will
329
* reflect the value of the async scroll offset and async zoom at the last time
330
* SampleCompositedAsyncTransform() was called.
331
*
332
* \li\b apz.keyboard.enabled
333
* Determines whether scrolling with the keyboard will be allowed to be handled
334
* by APZ.
335
*
336
* \li\b apz.keyboard.passive-listeners
337
* When enabled, APZ will interpret the passive event listener flag to mean
338
* that the event listener won't change the focused element or selection of
339
* the page. With this, web content can use passive key listeners and not have
340
* keyboard APZ disabled.
341
*
342
* \li\b apz.max_tap_time
343
* Maximum time for a touch on the screen and corresponding lift of the finger
344
* to be considered a tap. This also applies to double taps, except that it is
345
* used both for the interval between the first touchdown and first touchup,
346
* and for the interval between the first touchup and the second touchdown.\n
347
* Units: milliseconds.
348
*
349
* \li\b apz.max_velocity_inches_per_ms
350
* Maximum velocity. Velocity will be capped at this value if a faster fling
351
* occurs. Negative values indicate unlimited velocity.\n
352
* Units: (real-world, i.e. screen) inches per millisecond
353
*
354
* \li\b apz.max_velocity_queue_size
355
* Maximum size of velocity queue. The queue contains last N velocity records.
356
* On touch end we calculate the average velocity in order to compensate
357
* touch/mouse drivers misbehaviour.
358
*
359
* \li\b apz.min_skate_speed
360
* Minimum amount of speed along an axis before we switch to "skate" multipliers
361
* rather than using the "stationary" multipliers.\n
362
* Units: CSS pixels per millisecond
363
*
364
* \li\b apz.one_touch_pinch.enabled
365
* Whether or not the "one-touch-pinch" gesture (for zooming with one finger)
366
* is enabled or not.
367
*
368
* \li\b apz.overscroll.enabled
369
* Pref that enables overscrolling. If this is disabled, excess scroll that
370
* cannot be handed off is discarded.
371
*
372
* \li\b apz.overscroll.min_pan_distance_ratio
373
* The minimum ratio of the pan distance along one axis to the pan distance
374
* along the other axis needed to initiate overscroll along the first axis
375
* during panning.
376
*
377
* \li\b apz.overscroll.stretch_factor
378
* How much overscrolling can stretch content along an axis.
379
* The maximum stretch along an axis is a factor of (1 + kStretchFactor).
380
* (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
381
* is 1, you can stretch at most by a factor of 2).
382
*
383
* \li\b apz.overscroll.stop_distance_threshold
384
* \li\b apz.overscroll.stop_velocity_threshold
385
* Thresholds for stopping the overscroll animation. When both the distance
386
* and the velocity fall below their thresholds, we stop oscillating.\n
387
* Units: screen pixels (for distance)
388
* screen pixels per millisecond (for velocity)
389
*
390
* \li\b apz.paint_skipping.enabled
391
* When APZ is scrolling and sending repaint requests to the main thread, often
392
* the main thread doesn't actually need to do a repaint. This pref allows the
393
* main thread to skip doing those repaints in cases where it doesn't need to.
394
*
395
* \li\b apz.pinch_lock.mode
396
* The preferred pinch locking style. See PinchLockMode for possible values.
397
*
398
* \li\b apz.pinch_lock.scroll_lock_threshold
399
* Pinch locking is triggered if the user scrolls more than this distance
400
* and pinches less than apz.pinch_lock.span_lock_threshold.\n
401
* Units: (real-world, i.e. screen) inches
402
*
403
* \li\b apz.pinch_lock.span_breakout_threshold
404
* Distance in inches the user must pinch before lock can be broken.\n
405
* Units: (real-world, i.e. screen) inches measured between two touch points
406
*
407
* \li\b apz.pinch_lock.span_lock_threshold
408
* Pinch locking is triggered if the user pinches less than this distance
409
* and scrolls more than apz.pinch_lock.scroll_lock_threshold.\n
410
* Units: (real-world, i.e. screen) inches measured between two touch points
411
*
412
* \li\b apz.pinch_lock.buffer_max_age
413
* To ensure that pinch locking threshold calculations are not affected by
414
* variations in touch screen sensitivity, calculations draw from a buffer of
415
* recent events. This preference specifies the maximum time that events are
416
* held in this buffer.
417
* Units: milliseconds
418
*
419
* \li\b apz.popups.enabled
420
* Determines whether APZ is used for XUL popup widgets with remote content.
421
* Ideally, this should always be true, but it is currently not well tested, and
422
* has known issues, so needs to be prefable.
423
*
424
* \li\b apz.record_checkerboarding
425
* Whether or not to record detailed info on checkerboarding events.
426
*
427
* \li\b apz.second_tap_tolerance
428
* Constant describing the tolerance in distance we use, multiplied by the
429
* device DPI, within which a second tap is counted as part of a gesture
430
* continuing from the first tap. Making this larger allows the user more
431
* distance between the first and second taps in a "double tap" or "one touch
432
* pinch" gesture.\n
433
* Units: (real-world, i.e. screen) inches
434
*
435
* \li\b apz.test.logging_enabled
436
* Enable logging of APZ test data (see bug 961289).
437
*
438
* \li\b apz.touch_move_tolerance
439
* See the description for apz.touch_start_tolerance below. This is a similar
440
* threshold, except it is used to suppress touchmove events from being
441
* delivered to content for NON-scrollable frames (or more precisely, for APZCs
442
* where ArePointerEventsConsumable returns false).\n Units: (real-world, i.e.
443
* screen) inches
444
*
445
* \li\b apz.touch_start_tolerance
446
* Constant describing the tolerance in distance we use, multiplied by the
447
* device DPI, before we start panning the screen. This is to prevent us from
448
* accidentally processing taps as touch moves, and from very short/accidental
449
* touches moving the screen. touchmove events are also not delivered to content
450
* within this distance on scrollable frames.\n
451
* Units: (real-world, i.e. screen) inches
452
*
453
* \li\b apz.velocity_bias
454
* How much to adjust the displayport in the direction of scrolling. This value
455
* is multiplied by the velocity and added to the displayport offset.
456
*
457
* \li\b apz.velocity_relevance_time_ms
458
* When computing a fling velocity from the most recently stored velocity
459
* information, only velocities within the most X milliseconds are used.
460
* This pref controls the value of X.\n
461
* Units: ms
462
*
463
* \li\b apz.x_skate_size_multiplier
464
* \li\b apz.y_skate_size_multiplier
465
* The multiplier we apply to the displayport size if it is skating (current
466
* velocity is above \b apz.min_skate_speed). We prefer to increase the size of
467
* the Y axis because it is more natural in the case that a user is reading a
468
* page page that scrolls up/down. Note that one, both or neither of these may
469
* be used at any instant.\n In general we want \b
470
* apz.[xy]_skate_size_multiplier to be smaller than the corresponding
471
* stationary size multiplier because when panning fast we would like to paint
472
* less and get faster, more predictable paint times. When panning slowly we
473
* can afford to paint more even though it's slower.
474
*
475
* \li\b apz.x_stationary_size_multiplier
476
* \li\b apz.y_stationary_size_multiplier
477
* The multiplier we apply to the displayport size if it is not skating (see
478
* documentation for the skate size multipliers above).
479
*
480
* \li\b apz.x_skate_highmem_adjust
481
* \li\b apz.y_skate_highmem_adjust
482
* On high memory systems, we adjust the displayport during skating
483
* to be larger so we can reduce checkerboarding.
484
*
485
* \li\b apz.zoom_animation_duration_ms
486
* This controls how long the zoom-to-rect animation takes.\n
487
* Units: ms
488
*
489
* \li\b apz.scale_repaint_delay_ms
490
* How long to delay between repaint requests during a scale.
491
* A negative number prevents repaint requests during a scale.\n
492
* Units: ms
493
*
494
* \li\b apz.relative-update.enabled
495
* Whether to enable relative scroll offset updates or not. Relative scroll
496
* offset updates allow APZ and the main thread to concurrently update
497
* the scroll offset and merge the result.
498
*
499
*/
500
501
/**
502
* Computed time function used for sampling frames of a zoom to animation.
503
*/
504
StaticAutoPtr<ComputedTimingFunction> gZoomAnimationFunction;
505
506
/**
507
* Computed time function used for curving up velocity when it gets high.
508
*/
509
StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
510
511
/**
512
* The estimated duration of a paint for the purposes of calculating a new
513
* displayport, in milliseconds.
514
*/
515
static const double kDefaultEstimatedPaintDurationMs = 50;
516
517
/**
518
* Returns true if this is a high memory system and we can use
519
* extra memory for a larger displayport to reduce checkerboarding.
520
*/
521
static bool gIsHighMemSystem = false;
522
static bool IsHighMemSystem() { return gIsHighMemSystem; }
523
524
// Counter used to give each APZC a unique id
525
static uint32_t sAsyncPanZoomControllerCount = 0;
526
527
AsyncPanZoomAnimation* PlatformSpecificStateBase::CreateFlingAnimation(
528
AsyncPanZoomController& aApzc, const FlingHandoffState& aHandoffState,
529
float aPLPPI) {
530
return new GenericFlingAnimation<DesktopFlingPhysics>(
531
aApzc, aHandoffState.mChain, aHandoffState.mIsHandoff,
532
aHandoffState.mScrolledApzc, aPLPPI);
533
}
534
535
UniquePtr<VelocityTracker> PlatformSpecificStateBase::CreateVelocityTracker(
536
Axis* aAxis) {
537
return MakeUnique<SimpleVelocityTracker>(aAxis);
538
}
539
540
TimeStamp AsyncPanZoomController::GetFrameTime() const {
541
APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
542
return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
543
}
544
545
class MOZ_STACK_CLASS StateChangeNotificationBlocker final {
546
public:
547
explicit StateChangeNotificationBlocker(AsyncPanZoomController* aApzc)
548
: mApzc(aApzc) {
549
RecursiveMutexAutoLock lock(mApzc->mRecursiveMutex);
550
mInitialState = mApzc->mState;
551
mApzc->mNotificationBlockers++;
552
}
553
554
~StateChangeNotificationBlocker() {
555
AsyncPanZoomController::PanZoomState newState;
556
{
557
RecursiveMutexAutoLock lock(mApzc->mRecursiveMutex);
558
mApzc->mNotificationBlockers--;
559
newState = mApzc->mState;
560
}
561
mApzc->DispatchStateChangeNotification(mInitialState, newState);
562
}
563
564
private:
565
AsyncPanZoomController* mApzc;
566
AsyncPanZoomController::PanZoomState mInitialState;
567
};
568
569
/**
570
* An RAII class to temporarily apply async test attributes to the provided
571
* AsyncPanZoomController.
572
*
573
* This class should be used in the implementation of any AsyncPanZoomController
574
* method that queries the async scroll offset or async zoom (this includes
575
* the async layout viewport offset, since modifying the async scroll offset
576
* may result in the layout viewport moving as well).
577
*/
578
class MOZ_RAII AutoApplyAsyncTestAttributes final {
579
public:
580
explicit AutoApplyAsyncTestAttributes(const AsyncPanZoomController*);
581
~AutoApplyAsyncTestAttributes();
582
583
private:
584
AsyncPanZoomController* mApzc;
585
FrameMetrics mPrevFrameMetrics;
586
};
587
588
AutoApplyAsyncTestAttributes::AutoApplyAsyncTestAttributes(
589
const AsyncPanZoomController* aApzc)
590
// Having to use const_cast here seems less ugly than the alternatives
591
// of making several members of AsyncPanZoomController that
592
// ApplyAsyncTestAttributes() modifies |mutable|, or several methods that
593
// query the async transforms non-const.
594
: mApzc(const_cast<AsyncPanZoomController*>(aApzc)),
595
mPrevFrameMetrics(aApzc->Metrics()) {
596
mApzc->ApplyAsyncTestAttributes();
597
}
598
599
AutoApplyAsyncTestAttributes::~AutoApplyAsyncTestAttributes() {
600
mApzc->UnapplyAsyncTestAttributes(mPrevFrameMetrics);
601
}
602
603
class ZoomAnimation : public AsyncPanZoomAnimation {
604
public:
605
ZoomAnimation(AsyncPanZoomController& aApzc, const CSSPoint& aStartOffset,
606
const CSSToParentLayerScale2D& aStartZoom,
607
const CSSPoint& aEndOffset,
608
const CSSToParentLayerScale2D& aEndZoom)
609
: mApzc(aApzc),
610
mTotalDuration(TimeDuration::FromMilliseconds(
611
StaticPrefs::apz_zoom_animation_duration_ms())),
612
mStartOffset(aStartOffset),
613
mStartZoom(aStartZoom),
614
mEndOffset(aEndOffset),
615
mEndZoom(aEndZoom) {}
616
617
virtual bool DoSample(FrameMetrics& aFrameMetrics,
618
const TimeDuration& aDelta) override {
619
mDuration += aDelta;
620
double animPosition = mDuration / mTotalDuration;
621
622
if (animPosition >= 1.0) {
623
aFrameMetrics.SetZoom(mEndZoom);
624
mApzc.SetScrollOffset(mEndOffset);
625
return false;
626
}
627
628
// Sample the zoom at the current time point. The sampled zoom
629
// will affect the final computed resolution.
630
float sampledPosition = gZoomAnimationFunction->GetValue(
631
animPosition, ComputedTimingFunction::BeforeFlag::Unset);
632
633
// We scale the scrollOffset linearly with sampledPosition, so the zoom
634
// needs to scale inversely to match.
635
aFrameMetrics.SetZoom(CSSToParentLayerScale2D(
636
1 / (sampledPosition / mEndZoom.xScale +
637
(1 - sampledPosition) / mStartZoom.xScale),
638
1 / (sampledPosition / mEndZoom.yScale +
639
(1 - sampledPosition) / mStartZoom.yScale)));
640
641
mApzc.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
642
mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
643
mEndOffset.y * sampledPosition +
644
mStartOffset.y * (1 - sampledPosition))));
645
646
return true;
647
}
648
649
virtual bool WantsRepaints() override { return false; }
650
651
private:
652
AsyncPanZoomController& mApzc;
653
654
TimeDuration mDuration;
655
const TimeDuration mTotalDuration;
656
657
// Old metrics from before we started a zoom animation. This is only valid
658
// when we are in the "ANIMATED_ZOOM" state. This is used so that we can
659
// interpolate between the start and end frames. We only use the
660
// |mViewportScrollOffset| and |mResolution| fields on this.
661
CSSPoint mStartOffset;
662
CSSToParentLayerScale2D mStartZoom;
663
664
// Target metrics for a zoom to animation. This is only valid when we are in
665
// the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
666
// |mResolution| fields on this.
667
CSSPoint mEndOffset;
668
CSSToParentLayerScale2D mEndZoom;
669
};
670
671
class SmoothScrollAnimation : public AsyncPanZoomAnimation {
672
public:
673
SmoothScrollAnimation(AsyncPanZoomController& aApzc,
674
const nsPoint& aInitialPosition,
675
const nsPoint& aInitialVelocity,
676
const nsPoint& aDestination, double aSpringConstant,
677
double aDampingRatio)
678
: mApzc(aApzc),
679
mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
680
aSpringConstant, aDampingRatio),
681
mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
682
aSpringConstant, aDampingRatio) {}
683
684
/**
685
* Advances a smooth scroll simulation based on the time passed in |aDelta|.
686
* This should be called whenever sampling the content transform for this
687
* frame. Returns true if the smooth scroll should be advanced by one frame,
688
* or false if the smooth scroll has ended.
689
*/
690
bool DoSample(FrameMetrics& aFrameMetrics,
691
const TimeDuration& aDelta) override {
692
nsPoint oneParentLayerPixel =
693
CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
694
if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
695
mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
696
// Set the scroll offset to the exact destination. If we allow the scroll
697
// offset to end up being a bit off from the destination, we can get
698
// artefacts like "scroll to the next snap point in this direction"
699
// scrolling to the snap point we're already supposed to be at.
700
mApzc.ClampAndSetScrollOffset(CSSPoint::FromAppUnits(
701
nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination())));
702
return false;
703
}
704
705
mXAxisModel.Simulate(aDelta);
706
mYAxisModel.Simulate(aDelta);
707
708
CSSPoint position = CSSPoint::FromAppUnits(
709
nsPoint(mXAxisModel.GetPosition(), mYAxisModel.GetPosition()));
710
CSSPoint css_velocity = CSSPoint::FromAppUnits(
711
nsPoint(mXAxisModel.GetVelocity(), mYAxisModel.GetVelocity()));
712
713
// Convert from points/second to points/ms
714
ParentLayerPoint velocity =
715
ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
716
717
// Keep the velocity updated for the Axis class so that any animations
718
// chained off of the smooth scroll will inherit it.
719
if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) {
720
mApzc.mX.SetVelocity(0);
721
} else {
722
mApzc.mX.SetVelocity(velocity.x);
723
}
724
if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
725
mApzc.mY.SetVelocity(0);
726
} else {
727
mApzc.mY.SetVelocity(velocity.y);
728
}
729
// If we overscroll, hand off to a fling animation that will complete the
730
// spring back.
731
CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
732
ParentLayerPoint displacement =
733
(position - aFrameMetrics.GetScrollOffset()) * zoom;
734
735
ParentLayerPoint overscroll;
736
ParentLayerPoint adjustedOffset;
737
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
738
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
739
740
mApzc.ScrollBy(adjustedOffset / zoom);
741
742
// The smooth scroll may have caused us to reach the end of our scroll
743
// range. This can happen if either the
744
// layout.css.scroll-behavior.damping-ratio preference is set to less than 1
745
// (underdamped) or if a smooth scroll inherits velocity from a fling
746
// gesture.
747
if (!IsZero(overscroll)) {
748
// Hand off a fling with the remaining momentum to the next APZC in the
749
// overscroll handoff chain.
750
751
// We may have reached the end of the scroll range along one axis but
752
// not the other. In such a case we only want to hand off the relevant
753
// component of the fling.
754
if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
755
velocity.x = 0;
756
} else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
757
velocity.y = 0;
758
}
759
760
// To hand off the fling, we attempt to find a target APZC and start a new
761
// fling with the same velocity on that APZC. For simplicity, the actual
762
// overscroll of the current sample is discarded rather than being handed
763
// off. The compositor should sample animations sufficiently frequently
764
// that this is not noticeable. The target APZC is chosen by seeing if
765
// there is an APZC further in the handoff chain which is pannable; if
766
// there isn't, we take the new fling ourselves, entering an overscrolled
767
// state.
768
// Note: APZC is holding mRecursiveMutex, so directly calling
769
// HandleSmoothScrollOverscroll() (which acquires the tree lock) would
770
// violate the lock ordering. Instead we schedule
771
// HandleSmoothScrollOverscroll() to be called after mRecursiveMutex is
772
// released.
773
mDeferredTasks.AppendElement(NewRunnableMethod<ParentLayerPoint>(
774
"layers::AsyncPanZoomController::HandleSmoothScrollOverscroll",
775
&mApzc, &AsyncPanZoomController::HandleSmoothScrollOverscroll,
776
velocity));
777
return false;
778
}
779
780
return true;
781
}
782
783
void SetDestination(const nsPoint& aNewDestination) {
784
mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
785
mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
786
}
787
788
CSSPoint GetDestination() const {
789
return CSSPoint::FromAppUnits(
790
nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
791
}
792
793
SmoothScrollAnimation* AsSmoothScrollAnimation() override { return this; }
794
795
private:
796
AsyncPanZoomController& mApzc;
797
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
798
};
799
800
/*static*/
801
void AsyncPanZoomController::InitializeGlobalState() {
802
static bool sInitialized = false;
803
if (sInitialized) return;
804
sInitialized = true;
805
806
MOZ_ASSERT(NS_IsMainThread());
807
808
gZoomAnimationFunction =
809
new ComputedTimingFunction(nsTimingFunction(StyleTimingKeyword::Ease));
810
ClearOnShutdown(&gZoomAnimationFunction);
811
gVelocityCurveFunction = new ComputedTimingFunction(
812
nsTimingFunction(StaticPrefs::apz_fling_curve_function_x1_AtStartup(),
813
StaticPrefs::apz_fling_curve_function_y1_AtStartup(),
814
StaticPrefs::apz_fling_curve_function_x2_AtStartup(),
815
StaticPrefs::apz_fling_curve_function_y2_AtStartup()));
816
ClearOnShutdown(&gVelocityCurveFunction);
817
818
uint64_t sysmem = PR_GetPhysicalMemorySize();
819
uint64_t threshold = 1LL << 32; // 4 GB in bytes
820
gIsHighMemSystem = sysmem >= threshold;
821
822
PlatformSpecificState::InitializeGlobalState();
823
}
824
825
AsyncPanZoomController::AsyncPanZoomController(
826
LayersId aLayersId, APZCTreeManager* aTreeManager,
827
const RefPtr<InputQueue>& aInputQueue,
828
GeckoContentController* aGeckoContentController, wr::RenderRoot aRenderRoot,
829
GestureBehavior aGestures)
830
: mLayersId(aLayersId),
831
mRenderRoot(aRenderRoot),
832
mGeckoContentController(aGeckoContentController),
833
mRefPtrMonitor("RefPtrMonitor"),
834
// mTreeManager must be initialized before GetFrameTime() is called
835
mTreeManager(aTreeManager),
836
mRecursiveMutex("AsyncPanZoomController"),
837
mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
838
mX(this),
839
mY(this),
840
mPanDirRestricted(false),
841
mPinchLocked(false),
842
mPinchEventBuffer(TimeDuration::FromMilliseconds(
843
StaticPrefs::apz_pinch_lock_buffer_max_age_AtStartup())),
844
mZoomConstraints(false, false,
845
mScrollMetadata.GetMetrics().GetDevPixelsPerCSSPixel() *
846
kViewportMinScale / ParentLayerToScreenScale(1),
847
mScrollMetadata.GetMetrics().GetDevPixelsPerCSSPixel() *
848
kViewportMaxScale / ParentLayerToScreenScale(1)),
849
mLastSampleTime(GetFrameTime()),
850
mLastCheckerboardReport(GetFrameTime()),
851
mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
852
mState(NOTHING),
853
mNotificationBlockers(0),
854
mInputQueue(aInputQueue),
855
mPinchPaintTimerSet(false),
856
mAPZCId(sAsyncPanZoomControllerCount++),
857
mSharedLock(nullptr),
858
mTestAttributeAppliers(0),
859
mAsyncTransformAppliedToContent(false),
860
mTestHasAsyncKeyScrolled(false),
861
mCheckerboardEventLock("APZCBELock") {
862
if (aGestures == USE_GESTURE_DETECTOR) {
863
mGestureEventListener = new GestureEventListener(this);
864
}
865
}
866
867
AsyncPanZoomController::~AsyncPanZoomController() { MOZ_ASSERT(IsDestroyed()); }
868
869
PlatformSpecificStateBase* AsyncPanZoomController::GetPlatformSpecificState() {
870
if (!mPlatformSpecificState) {
871
mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
872
}
873
return mPlatformSpecificState.get();
874
}
875
876
already_AddRefed<GeckoContentController>
877
AsyncPanZoomController::GetGeckoContentController() const {
878
MonitorAutoLock lock(mRefPtrMonitor);
879
RefPtr<GeckoContentController> controller = mGeckoContentController;
880
return controller.forget();
881
}
882
883
already_AddRefed<GestureEventListener>
884
AsyncPanZoomController::GetGestureEventListener() const {
885
MonitorAutoLock lock(mRefPtrMonitor);
886
RefPtr<GestureEventListener> listener = mGestureEventListener;
887
return listener.forget();
888
}
889
890
const RefPtr<InputQueue>& AsyncPanZoomController::GetInputQueue() const {
891
return mInputQueue;
892
}
893
894
void AsyncPanZoomController::Destroy() {
895
AssertOnUpdaterThread();
896
897
CancelAnimation(CancelAnimationFlags::ScrollSnap);
898
899
{ // scope the lock
900
MonitorAutoLock lock(mRefPtrMonitor);
901
mGeckoContentController = nullptr;
902
mGestureEventListener = nullptr;
903
}
904
mParent = nullptr;
905
mTreeManager = nullptr;
906
907
// Only send the release message if the SharedFrameMetrics has been created.
908
if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
909
Unused << mMetricsSharingController->StopSharingMetrics(GetScrollId(),
910
mAPZCId);
911
}
912
913
{ // scope the lock
914
RecursiveMutexAutoLock lock(mRecursiveMutex);
915
mSharedFrameMetricsBuffer = nullptr;
916
delete mSharedLock;
917
mSharedLock = nullptr;
918
}
919
}
920
921
bool AsyncPanZoomController::IsDestroyed() const {
922
return mTreeManager == nullptr;
923
}
924
925
float AsyncPanZoomController::GetDPI() const {
926
if (APZCTreeManager* localPtr = mTreeManager) {
927
return localPtr->GetDPI();
928
}
929
// If this APZC has been destroyed then this value is not going to be
930
// used for anything that the user will end up seeing, so we can just
931
// return 0.
932
return 0.0;
933
}
934
935
ScreenCoord AsyncPanZoomController::GetTouchStartTolerance() const {
936
return (StaticPrefs::apz_touch_start_tolerance() * GetDPI());
937
}
938
939
ScreenCoord AsyncPanZoomController::GetTouchMoveTolerance() const {
940
return (StaticPrefs::apz_touch_move_tolerance() * GetDPI());
941
}
942
943
ScreenCoord AsyncPanZoomController::GetSecondTapTolerance() const {
944
return (StaticPrefs::apz_second_tap_tolerance() * GetDPI());
945
}
946
947
/* static */ AsyncPanZoomController::AxisLockMode
948
AsyncPanZoomController::GetAxisLockMode() {
949
return static_cast<AxisLockMode>(StaticPrefs::apz_axis_lock_mode());
950
}
951
952
/* static */ AsyncPanZoomController::PinchLockMode
953
AsyncPanZoomController::GetPinchLockMode() {
954
return static_cast<PinchLockMode>(StaticPrefs::apz_pinch_lock_mode());
955
}
956
957
bool AsyncPanZoomController::ArePointerEventsConsumable(
958
TouchBlockState* aBlock, const MultiTouchInput& aInput) {
959
uint32_t touchPoints = aInput.mTouches.Length();
960
if (touchPoints == 0) {
961
// Cant' do anything with zero touch points
962
return false;
963
}
964
965
// This logic is simplified, erring on the side of returning true if we're
966
// not sure. It's safer to pretend that we can consume the event and then
967
// not be able to than vice-versa. But at the same time, we should try hard
968
// to return an accurate result, because returning true can trigger a
969
// pointercancel event to web content, which can break certain features
970
// that are using touch-action and handling the pointermove events.
971
//
972
// We could probably enhance this logic to determine things like "we're
973
// not pannable, so we can only zoom in, and the zoom is already maxed
974
// out, so we're not zoomable either" but no need for that at this point.
975
976
bool pannableX = aBlock->TouchActionAllowsPanningX() &&
977
aBlock->GetOverscrollHandoffChain()->CanScrollInDirection(
978
this, ScrollDirection::eHorizontal);
979
bool pannableY = aBlock->TouchActionAllowsPanningY() &&
980
aBlock->GetOverscrollHandoffChain()->CanScrollInDirection(
981
this, ScrollDirection::eVertical);
982
983
bool pannable;
984
985
Maybe<ScrollDirection> panDirection =
986
aBlock->GetBestGuessPanDirection(aInput);
987
if (panDirection == Some(ScrollDirection::eVertical)) {
988
pannable = pannableY;
989
} else if (panDirection == Some(ScrollDirection::eHorizontal)) {
990
pannable = pannableX;
991
} else {
992
// If we don't have a guessed pan direction, err on the side of returning
993
// true.
994
pannable = pannableX || pannableY;
995
}
996
997
if (touchPoints == 1) {
998
return pannable;
999
}
1000
1001
bool zoomable = mZoomConstraints.mAllowZoom;
1002
zoomable &= (aBlock->TouchActionAllowsPinchZoom());
1003
1004
return pannable || zoomable;
1005
}
1006
1007
nsEventStatus AsyncPanZoomController::HandleDragEvent(
1008
const MouseInput& aEvent, const AsyncDragMetrics& aDragMetrics,
1009
CSSCoord aInitialThumbPos) {
1010
if (!StaticPrefs::apz_drag_enabled()) {
1011
return nsEventStatus_eIgnore;
1012
}
1013
1014
if (!GetApzcTreeManager()) {
1015
return nsEventStatus_eConsumeNoDefault;
1016
}
1017
1018
if (aEvent.mType == MouseInput::MouseType::MOUSE_UP) {
1019
APZC_LOG("%p ending drag\n", this);
1020
SetState(NOTHING);
1021
ScrollSnap();
1022
return nsEventStatus_eConsumeNoDefault;
1023
}
1024
1025
HitTestingTreeNodeAutoLock node;
1026
GetApzcTreeManager()->FindScrollThumbNode(aDragMetrics, node);
1027
if (!node) {
1028
APZC_LOG("%p unable to find scrollthumb node with viewid %" PRIu64 "\n",
1029
this, aDragMetrics.mViewId);
1030
return nsEventStatus_eConsumeNoDefault;
1031
}
1032
1033
if (aEvent.mType == MouseInput::MouseType::MOUSE_DOWN) {
1034
APZC_LOG("%p starting scrollbar drag\n", this);
1035
SetState(SCROLLBAR_DRAG);
1036
}
1037
1038
if (aEvent.mType != MouseInput::MouseType::MOUSE_MOVE) {
1039
APZC_LOG("%p discarding event of type %d\n", this, aEvent.mType);
1040
return nsEventStatus_eConsumeNoDefault;
1041
}
1042
1043
const ScrollbarData& scrollbarData = node->GetScrollbarData();
1044
MOZ_ASSERT(scrollbarData.mScrollbarLayerType ==
1045
layers::ScrollbarLayerType::Thumb);
1046
MOZ_ASSERT(scrollbarData.mDirection.isSome());
1047
ScrollDirection direction = *scrollbarData.mDirection;
1048
1049
bool isMouseAwayFromThumb = false;
1050
if (int snapMultiplier = StaticPrefs::slider_snapMultiplier_AtStartup()) {
1051
// It's fine to ignore the async component of the thumb's transform,
1052
// because any async transform of the thumb will be in the direction of
1053
// scrolling, but here we're interested in the other direction.
1054
ParentLayerRect thumbRect =
1055
(node->GetTransform() * AsyncTransformMatrix())
1056
.TransformBounds(LayerRect(node->GetVisibleRegion().GetBounds()));
1057
ScrollDirection otherDirection = GetPerpendicularDirection(direction);
1058
ParentLayerCoord distance =
1059
GetAxisStart(otherDirection, thumbRect.DistanceTo(aEvent.mLocalOrigin));
1060
ParentLayerCoord thumbWidth = GetAxisLength(otherDirection, thumbRect);
1061
// Avoid triggering this condition spuriously when the thumb is
1062
// offscreen and its visible region is therefore empty.
1063
if (thumbWidth > 0 && thumbWidth * snapMultiplier < distance) {
1064
isMouseAwayFromThumb = true;
1065
APZC_LOG("%p determined mouse is away from thumb, will snap\n", this);
1066
}
1067
}
1068
1069
RecursiveMutexAutoLock lock(mRecursiveMutex);
1070
CSSCoord thumbPosition;
1071
if (isMouseAwayFromThumb) {
1072
thumbPosition = aInitialThumbPos;
1073
} else {
1074
thumbPosition = ConvertScrollbarPoint(aEvent.mLocalOrigin, scrollbarData) -
1075
aDragMetrics.mScrollbarDragOffset;
1076
}
1077
1078
CSSCoord maxThumbPos = scrollbarData.mScrollTrackLength;
1079
maxThumbPos -= scrollbarData.mThumbLength;
1080
1081
float scrollPercent =
1082
maxThumbPos.value == 0.0f ? 0.0f : (float)(thumbPosition / maxThumbPos);
1083
APZC_LOG("%p scrollbar dragged to %f percent\n", this, scrollPercent);
1084
1085
CSSCoord minScrollPosition =
1086
GetAxisStart(direction, Metrics().GetScrollableRect().TopLeft());
1087
CSSCoord maxScrollPosition =
1088
GetAxisStart(direction, Metrics().GetScrollableRect().BottomRight()) -
1089
GetAxisLength(
1090
direction,
1091
Metrics()
1092
.CalculateCompositionBoundsInCssPixelsOfSurroundingContent());
1093
CSSCoord scrollPosition =
1094
minScrollPosition +
1095
(scrollPercent * (maxScrollPosition - minScrollPosition));
1096
1097
scrollPosition = std::max(scrollPosition, minScrollPosition);
1098
scrollPosition = std::min(scrollPosition, maxScrollPosition);
1099
1100
CSSPoint scrollOffset = Metrics().GetScrollOffset();
1101
if (direction == ScrollDirection::eHorizontal) {
1102
scrollOffset.x = scrollPosition;
1103
} else {
1104
scrollOffset.y = scrollPosition;
1105
}
1106
APZC_LOG("%p set scroll offset to %s from scrollbar drag\n", this,
1107
Stringify(scrollOffset).c_str());
1108
SetScrollOffset(scrollOffset);
1109
ScheduleCompositeAndMaybeRepaint();
1110
UpdateSharedCompositorFrameMetrics();
1111
1112
return nsEventStatus_eConsumeNoDefault;
1113
}
1114
1115
nsEventStatus AsyncPanZoomController::HandleInputEvent(
1116
const InputData& aEvent,
1117
const ScreenToParentLayerMatrix4x4& aTransformToApzc) {
1118
APZThreadUtils::AssertOnControllerThread();
1119
1120
nsEventStatus rv = nsEventStatus_eIgnore;
1121
1122
switch (aEvent.mInputType) {
1123
case MULTITOUCH_INPUT: {
1124
MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
1125
RefPtr<GestureEventListener> listener = GetGestureEventListener();
1126
if (listener) {
1127
// We only care about screen coordinates in the gesture listener,
1128
// so we don't bother transforming the event to parent layer coordinates
1129
rv = listener->HandleInputEvent(multiTouchInput);
1130
if (rv == nsEventStatus_eConsumeNoDefault) {
1131
return rv;
1132
}
1133
}
1134
1135
if (!multiTouchInput.TransformToLocal(aTransformToApzc)) {
1136
return rv;
1137
}
1138
1139
switch (multiTouchInput.mType) {
1140
case MultiTouchInput::MULTITOUCH_START:
1141
rv = OnTouchStart(multiTouchInput);
1142
break;
1143
case MultiTouchInput::MULTITOUCH_MOVE:
1144
rv = OnTouchMove(multiTouchInput);
1145
break;
1146
case MultiTouchInput::MULTITOUCH_END:
1147
rv = OnTouchEnd(multiTouchInput);
1148
break;
1149
case MultiTouchInput::MULTITOUCH_CANCEL:
1150
rv = OnTouchCancel(multiTouchInput);
1151
break;
1152
}
1153
break;
1154
}
1155
case PANGESTURE_INPUT: {
1156
PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
1157
if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
1158
return rv;
1159
}
1160
1161
switch (panGestureInput.mType) {
1162
case PanGestureInput::PANGESTURE_MAYSTART:
1163
rv = OnPanMayBegin(panGestureInput);
1164
break;
1165
case PanGestureInput::PANGESTURE_CANCELLED:
1166
rv = OnPanCancelled(panGestureInput);
1167
break;
1168
case PanGestureInput::PANGESTURE_START:
1169
rv = OnPanBegin(panGestureInput);
1170
break;
1171
case PanGestureInput::PANGESTURE_PAN:
1172
rv = OnPan(panGestureInput, true);
1173
break;
1174
case PanGestureInput::PANGESTURE_END:
1175
rv = OnPanEnd(panGestureInput);
1176
break;
1177
case PanGestureInput::PANGESTURE_MOMENTUMSTART:
1178
rv = OnPanMomentumStart(panGestureInput);
1179
break;
1180
case PanGestureInput::PANGESTURE_MOMENTUMPAN:
1181
rv = OnPan(panGestureInput, false);
1182
break;
1183
case PanGestureInput::PANGESTURE_MOMENTUMEND:
1184
rv = OnPanMomentumEnd(panGestureInput);
1185
break;
1186
}
1187
break;
1188
}
1189
case MOUSE_INPUT: {
1190
MouseInput mouseInput = aEvent.AsMouseInput();
1191
if (!mouseInput.TransformToLocal(aTransformToApzc)) {
1192
return rv;
1193
}
1194
break;
1195
}
1196
case SCROLLWHEEL_INPUT: {
1197
ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
1198
if (!scrollInput.TransformToLocal(aTransformToApzc)) {
1199
return rv;
1200
}
1201
1202
rv = OnScrollWheel(scrollInput);
1203
break;
1204
}
1205
case PINCHGESTURE_INPUT: {
1206
// The APZCTreeManager should take care of ensuring that only root-content
1207
// APZCs get pinch inputs.
1208
MOZ_ASSERT(IsRootContent());
1209
PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
1210
if (!pinchInput.TransformToLocal(aTransformToApzc)) {
1211
return rv;
1212
}
1213
1214
rv = HandleGestureEvent(pinchInput);
1215
break;
1216
}
1217
case TAPGESTURE_INPUT: {
1218
TapGestureInput tapInput = aEvent.AsTapGestureInput();
1219
if (!tapInput.TransformToLocal(aTransformToApzc)) {
1220
return rv;
1221
}
1222
1223
rv = HandleGestureEvent(tapInput);
1224
break;
1225
}
1226
case KEYBOARD_INPUT: {
1227
const KeyboardInput& keyInput = aEvent.AsKeyboardInput();
1228
rv = OnKeyboard(keyInput);
1229
break;
1230
}
1231
}
1232
1233
return rv;
1234
}
1235
1236
nsEventStatus AsyncPanZoomController::HandleGestureEvent(
1237
const InputData& aEvent) {
1238
APZThreadUtils::AssertOnControllerThread();
1239
1240
nsEventStatus rv = nsEventStatus_eIgnore;
1241
1242
switch (aEvent.mInputType) {
1243
case PINCHGESTURE_INPUT: {
1244
// This may be invoked via a one-touch-pinch gesture from
1245
// GestureEventListener. In that case we want redirect it to the enclosing
1246
// root-content APZC.
1247
if (!IsRootContent()) {
1248
if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
1249
if (RefPtr<AsyncPanZoomController> root =
1250
treeManagerLocal->FindZoomableApzc(this)) {
1251
rv = root->HandleGestureEvent(aEvent);
1252
}
1253
}
1254
break;
1255
}
1256
PinchGestureInput pinchGestureInput = aEvent.AsPinchGestureInput();
1257
pinchGestureInput.TransformToLocal(GetTransformToThis());
1258
switch (pinchGestureInput.mType) {
1259
case PinchGestureInput::PINCHGESTURE_START:
1260
rv = OnScaleBegin(pinchGestureInput);
1261
break;
1262
case PinchGestureInput::PINCHGESTURE_SCALE:
1263
rv = OnScale(pinchGestureInput);
1264
break;
1265
case PinchGestureInput::PINCHGESTURE_END:
1266
rv = OnScaleEnd(pinchGestureInput);
1267
break;
1268
}
1269
break;
1270
}
1271
case TAPGESTURE_INPUT: {
1272
TapGestureInput tapGestureInput = aEvent.AsTapGestureInput();
1273
tapGestureInput.TransformToLocal(GetTransformToThis());
1274
switch (tapGestureInput.mType) {
1275
case TapGestureInput::TAPGESTURE_LONG:
1276
rv = OnLongPress(tapGestureInput);
1277
break;
1278
case TapGestureInput::TAPGESTURE_LONG_UP:
1279
rv = OnLongPressUp(tapGestureInput);
1280
break;
1281
case TapGestureInput::TAPGESTURE_UP:
1282
rv = OnSingleTapUp(tapGestureInput);
1283
break;
1284
case TapGestureInput::TAPGESTURE_CONFIRMED:
1285
rv = OnSingleTapConfirmed(tapGestureInput);
1286
break;
1287
case TapGestureInput::TAPGESTURE_DOUBLE:
1288
rv = OnDoubleTap(tapGestureInput);
1289
break;
1290
case TapGestureInput::TAPGESTURE_SECOND:
1291
rv = OnSecondTap(tapGestureInput);
1292
break;
1293
case TapGestureInput::TAPGESTURE_CANCEL:
1294
rv = OnCancelTap(tapGestureInput);
1295
break;
1296
}
1297
break;
1298
}
1299
default:
1300
MOZ_ASSERT_UNREACHABLE("Unhandled input event");
1301
break;
1302
}
1303
1304
return rv;
1305
}
1306
1307
void AsyncPanZoomController::HandleDynamicToolbarMovement(
1308
uint32_t aStartTimestampMs, uint32_t aEndTimestampMs,
1309
ParentLayerCoord aDeltaY) {
1310
mY.HandleDynamicToolbarMovement(aStartTimestampMs, aEndTimestampMs, aDeltaY);
1311
}
1312
1313
void AsyncPanZoomController::StartAutoscroll(const ScreenPoint& aPoint) {
1314
// Cancel any existing animation.
1315
CancelAnimation();
1316
1317
SetState(AUTOSCROLL);
1318
StartAnimation(new AutoscrollAnimation(*this, aPoint));
1319
}
1320
1321
void AsyncPanZoomController::StopAutoscroll() {
1322
if (mState == AUTOSCROLL) {
1323
CancelAnimation(TriggeredExternally);
1324
}
1325
}
1326
1327
nsEventStatus AsyncPanZoomController::OnTouchStart(
1328
const MultiTouchInput& aEvent) {
1329
APZC_LOG("%p got a touch-start in state %d\n", this, mState);
1330
mPanDirRestricted = false;
1331
1332
switch (mState) {
1333
case FLING:
1334
case ANIMATING_ZOOM:
1335
case SMOOTH_SCROLL:
1336
case OVERSCROLL_ANIMATION:
1337
case WHEEL_SCROLL:
1338
case KEYBOARD_SCROLL:
1339
case PAN_MOMENTUM:
1340
case AUTOSCROLL:
1341
MOZ_ASSERT(GetCurrentTouchBlock());
1342
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(
1343
ExcludeOverscroll);
1344
MOZ_FALLTHROUGH;
1345
case SCROLLBAR_DRAG:
1346
case NOTHING: {
1347
ParentLayerPoint point = GetFirstTouchPoint(aEvent);
1348
mStartTouch = GetFirstExternalTouchPoint(aEvent);
1349
mX.StartTouch(point.x, aEvent.mTime);
1350
mY.StartTouch(point.y, aEvent.mTime);
1351
if (RefPtr<GeckoContentController> controller =
1352
GetGeckoContentController()) {
1353
MOZ_ASSERT(GetCurrentTouchBlock());
1354
controller->NotifyAPZStateChange(
1355
GetGuid(), APZStateChange::eStartTouch,
1356
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(
1357
this));
1358
}
1359
SetState(TOUCHING);
1360
break;
1361
}
1362
case TOUCHING:
1363
case PANNING:
1364
case PANNING_LOCKED_X:
1365
case PANNING_LOCKED_Y:
1366
case PINCHING:
1367
NS_WARNING("Received impossible touch in OnTouchStart");
1368
break;
1369
}
1370
1371
return nsEventStatus_eConsumeNoDefault;
1372
}
1373
1374
nsEventStatus AsyncPanZoomController::OnTouchMove(
1375
const MultiTouchInput& aEvent) {
1376
APZC_LOG("%p got a touch-move in state %d\n", this, mState);
1377
switch (mState) {
1378
case FLING:
1379
case SMOOTH_SCROLL:
1380
case NOTHING:
1381
case ANIMATING_ZOOM:
1382
// May happen if the user double-taps and drags without lifting after the
1383
// second tap. Ignore the move if this happens.
1384
return nsEventStatus_eIgnore;
1385
1386
case TOUCHING: {
1387
ScreenCoord panThreshold = GetTouchStartTolerance();
1388
ExternalPoint extPoint = GetFirstExternalTouchPoint(aEvent);
1389
// We intentionally skip the UpdateWithTouchAtDevicePoint call when the
1390
// panThreshold is zero. This ensures more deterministic behaviour during
1391
// testing. If we call that, Axis::mPos gets updated to the point of this
1392
// touchmove event, but we "consume" the move to overcome the
1393
// panThreshold, so it's hard to pan a specific amount reliably from a
1394
// mochitest.
1395
if (panThreshold > 0.0f) {
1396
UpdateWithTouchAtDevicePoint(aEvent);
1397
if (PanVector(extPoint).Length() < panThreshold) {
1398
return nsEventStatus_eIgnore;
1399
}
1400
}
1401
1402
MOZ_ASSERT(GetCurrentTouchBlock());
1403
if (StaticPrefs::layout_css_touch_action_enabled() &&
1404
GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
1405
// User tries to trigger a touch behavior. If allowed touch behavior is
1406
// vertical pan
1407
// + horizontal pan (touch-action value is equal to AUTO) we can return
1408
// ConsumeNoDefault status immediately to trigger cancel event further.
1409
// It should happen independent of the parent type (whether it is
1410
// scrolling or not).
1411
StartPanning(extPoint);
1412
return nsEventStatus_eConsumeNoDefault;
1413
}
1414
1415
return StartPanning(extPoint);
1416
}
1417
1418
case PANNING:
1419
case PANNING_LOCKED_X:
1420
case PANNING_LOCKED_Y:
1421
case PAN_MOMENTUM:
1422
TrackTouch(aEvent);
1423
return nsEventStatus_eConsumeNoDefault;
1424
1425
case PINCHING:
1426
// The scale gesture listener should have handled this.
1427
NS_WARNING(
1428
"Gesture listener should have handled pinching in OnTouchMove.");
1429
return nsEventStatus_eIgnore;
1430
1431
case WHEEL_SCROLL:
1432
case KEYBOARD_SCROLL:
1433
case OVERSCROLL_ANIMATION:
1434
case AUTOSCROLL:
1435
case SCROLLBAR_DRAG:
1436
// Should not receive a touch-move in the OVERSCROLL_ANIMATION state
1437
// as touch blocks that begin in an overscrolled state cancel the
1438
// animation. The same is true for wheel scroll animations.
1439
NS_WARNING("Received impossible touch in OnTouchMove");
1440
break;
1441
}
1442
1443
return nsEventStatus_eConsumeNoDefault;
1444
}
1445
1446
nsEventStatus AsyncPanZoomController::OnTouchEnd(
1447
const MultiTouchInput& aEvent) {
1448
APZC_LOG("%p got a touch-end in state %d\n", this, mState);
1449
OnTouchEndOrCancel();
1450
1451
// In case no touch behavior triggered previously we can avoid sending
1452
// scroll events or requesting content repaint. This condition is added
1453
// to make tests consistent - in case touch-action is NONE (and therefore
1454
// no pans/zooms can be performed) we expected neither scroll or repaint
1455
// events.
1456
if (mState != NOTHING) {
1457
RecursiveMutexAutoLock lock(mRecursiveMutex);
1458
}
1459
1460
switch (mState) {
1461
case FLING:
1462
// Should never happen.
1463
NS_WARNING("Received impossible touch end in OnTouchEnd.");
1464
MOZ_FALLTHROUGH;
1465
case ANIMATING_ZOOM:
1466
case SMOOTH_SCROLL:
1467
case NOTHING:
1468
// May happen if the user double-taps and drags without lifting after the
1469
// second tap. Ignore if this happens.
1470
return nsEventStatus_eIgnore;
1471
1472
case TOUCHING:
1473
// We may have some velocity stored on the axis from move events
1474
// that were not big enough to trigger scrolling. Clear that out.
1475
mX.SetVelocity(0);
1476
mY.SetVelocity(0);
1477
MOZ_ASSERT(GetCurrentTouchBlock());
1478
APZC_LOG("%p still has %u touch points active\n", this,
1479
GetCurrentTouchBlock()->GetActiveTouchCount());
1480
// In cases where the user is panning, then taps the second finger without
1481
// entering a pinch, we will arrive here when the second finger is lifted.
1482
// However the first finger is still down so we want to remain in state
1483
// TOUCHING.
1484
if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
1485
// It's possible we may be overscrolled if the user tapped during a
1486
// previous overscroll pan. Make sure to snap back in this situation.
1487
// An ancestor APZC could be overscrolled instead of this APZC, so
1488
// walk the handoff chain as well.
1489
GetCurrentTouchBlock()
1490
->GetOverscrollHandoffChain()
1491
->SnapBackOverscrolledApzc(this);
1492
// SnapBackOverscrolledApzc() will put any APZC it causes to snap back
1493
// into the OVERSCROLL_ANIMATION state. If that's not us, since we're
1494
// done TOUCHING enter the NOTHING state.
1495
if (mState != OVERSCROLL_ANIMATION) {
1496
SetState(NOTHING);
1497
}
1498
}
1499
return nsEventStatus_eIgnore;
1500
1501
case PANNING:
1502
case PANNING_LOCKED_X:
1503
case PANNING_LOCKED_Y:
1504
case PAN_MOMENTUM: {
1505
MOZ_ASSERT(GetCurrentTouchBlock());
1506
mX.EndTouch(aEvent.mTime);
1507
mY.EndTouch(aEvent.mTime);
1508
return HandleEndOfPan();
1509
}
1510
case PINCHING:
1511
SetState(NOTHING);
1512
// Scale gesture listener should have handled this.
1513
NS_WARNING(
1514
"Gesture listener should have handled pinching in OnTouchEnd.");
1515
return nsEventStatus_eIgnore;
1516
1517
case WHEEL_SCROLL:
1518
case KEYBOARD_SCROLL:
1519
case OVERSCROLL_ANIMATION:
1520
case AUTOSCROLL:
1521
case SCROLLBAR_DRAG:
1522
// Should not receive a touch-end in the OVERSCROLL_ANIMATION state
1523
// as touch blocks that begin in an overscrolled state cancel the
1524
// animation. The same is true for WHEEL_SCROLL.
1525
NS_WARNING("Received impossible touch in OnTouchEnd");
1526
break;
1527
}
1528
1529
return nsEventStatus_eConsumeNoDefault;
1530
}
1531
1532
nsEventStatus AsyncPanZoomController::OnTouchCancel(
1533
const MultiTouchInput& aEvent) {
1534
APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
1535
OnTouchEndOrCancel();
1536
CancelAnimationAndGestureState();
1537
return nsEventStatus_eConsumeNoDefault;
1538
}
1539
1540
nsEventStatus AsyncPanZoomController::OnScaleBegin(
1541
const PinchGestureInput& aEvent) {
1542
APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
1543
1544
mPinchLocked = false;
1545
mPinchPaintTimerSet = false;
1546
// Note that there may not be a touch block at this point, if we received the
1547
// PinchGestureEvent directly from widget code without any touch events.
1548
if (HasReadyTouchBlock() &&
1549
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1550
return nsEventStatus_eIgnore;
1551
}
1552
1553
// If zooming is not allowed, this is a two-finger pan.
1554
// Start tracking panning distance and velocity.
1555
if (!mZoomConstraints.mAllowZoom) {
1556
mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
1557
mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
1558
}
1559
1560
// For platforms that don't support APZ zooming, dispatch a message to the
1561
// content controller, it may want to do something else with this gesture.
1562
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
1563
// per-document basis.
1564
if (!StaticPrefs::apz_allow_zooming()) {
1565
if (RefPtr<GeckoContentController> controller =
1566
GetGeckoContentController()) {
1567
controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0,
1568
aEvent.modifiers);
1569
}
1570
}
1571
1572
SetState(PINCHING);
1573
mX.SetVelocity(0);
1574
mY.SetVelocity(0);
1575
RecursiveMutexAutoLock lock(mRecursiveMutex);
1576
mLastZoomFocus =
1577
aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft();
1578
1579
mPinchEventBuffer.push(aEvent);
1580
1581
return nsEventStatus_eConsumeNoDefault;
1582
}
1583
1584
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
1585
APZC_LOG("%p got a scale in state %d\n", this, mState);
1586
1587
if (HasReadyTouchBlock() &&
1588
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1589
return nsEventStatus_eIgnore;
1590
}
1591
1592
if (mState != PINCHING) {
1593
return nsEventStatus_eConsumeNoDefault;
1594
}
1595
1596
mPinchEventBuffer.push(aEvent);
1597
HandlePinchLocking();
1598
bool allowZoom = mZoomConstraints.mAllowZoom && !mPinchLocked;
1599
1600
// If zooming is not allowed, this is a two-finger pan.
1601
// Tracking panning distance and velocity.
1602
// UpdateWithTouchAtDevicePoint() acquires the tree lock, so
1603
// it cannot be called while the mRecursiveMutex lock is held.
1604
if (!allowZoom) {
1605
mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x, aEvent.mTime);
1606
mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y, aEvent.mTime);
1607
}
1608
1609
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
1610
// per-document basis.
1611
if (!StaticPrefs::apz_allow_zooming()) {
1612
if (RefPtr<GeckoContentController> controller =
1613
GetGeckoContentController()) {
1614
controller->NotifyPinchGesture(
1615
aEvent.mType, GetGuid(),
1616
ViewAs<LayoutDevicePixel>(
1617
aEvent.mCurrentSpan - aEvent.mPreviousSpan,
1618
PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
1619
aEvent.modifiers);
1620
}
1621
}
1622
1623
{
1624
RecursiveMutexAutoLock lock(mRecursiveMutex);
1625
// Only the root APZC is zoomable, and the root APZC is not allowed to have
1626
// different x and y scales. If it did, the calculations in this function
1627
// would have to be adjusted (as e.g. it would no longer be valid to take
1628
// the minimum or maximum of the ratios of the widths and heights of the
1629
// page rect and the composition bounds).
1630
MOZ_ASSERT(Metrics().IsRootContent());
1631
MOZ_ASSERT(Metrics().GetZoom().AreScalesSame());
1632
1633
CSSToParentLayerScale userZoom = Metrics().GetZoom().ToScaleFactor();
1634
ParentLayerPoint focusPoint =
1635
aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft();
1636
CSSPoint cssFocusPoint = focusPoint / Metrics().GetZoom();
1637
1638
ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
1639
mLastZoomFocus = focusPoint;
1640
// If displacing by the change in focus point will take us off page bounds,
1641
// then reduce the displacement such that it doesn't.
1642
focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
1643
focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
1644
ScrollBy(focusChange / userZoom);
1645
1646
// If the span is zero or close to it, we don't want to process this zoom
1647
// change because we're going to get wonky numbers for the spanRatio. So
1648
// let's bail out here. Note that we do this after the focus-change-scroll
1649
// above, so that if we have a pinch with zero span but changing focus,
1650
// such as generated by some Synaptics touchpads on Windows, we still
1651
// scroll properly.
1652
float prevSpan = aEvent.mPreviousSpan;
1653
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
1654
// We might have done a nonzero ScrollBy above, so update metrics and
1655
// repaint/recomposite
1656
ScheduleCompositeAndMaybeRepaint();
1657
UpdateSharedCompositorFrameMetrics();
1658
return nsEventStatus_eConsumeNoDefault;
1659
}
1660
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
1661
1662
// When we zoom in with focus, we can zoom too much towards the boundaries
1663
// that we actually go over them. These are the needed displacements along
1664
// either axis such that we don't overscroll the boundaries when zooming.
1665
CSSPoint neededDisplacement;
1666
1667
CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
1668
CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
1669
realMinZoom.scale =
1670
std::max(realMinZoom.scale, Metrics().GetCompositionBounds().Width() /
1671
Metrics().GetScrollableRect().Width());
1672
realMinZoom.scale =
1673
std::max(realMinZoom.scale, Metrics().GetCompositionBounds().Height() /
1674
Metrics().GetScrollableRect().Height());
1675
if (realMaxZoom < realMinZoom) {
1676
realMaxZoom = realMinZoom;
1677
}
1678
1679
bool doScale = allowZoom && ((spanRatio > 1.0 && userZoom < realMaxZoom) ||
1680
(spanRatio < 1.0 && userZoom > realMinZoom));
1681
1682
if (doScale) {
1683
spanRatio = clamped(spanRatio, realMinZoom.scale / userZoom.scale,
1684
realMaxZoom.scale / userZoom.scale);
1685
1686
// Note that the spanRatio here should never put us into OVERSCROLL_BOTH
1687
// because up above we clamped it.
1688
neededDisplacement.x =
1689
-mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
1690
neededDisplacement.y =
1691
-mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
1692
1693
ScaleWithFocus(spanRatio, cssFocusPoint);
1694
1695
if (neededDisplacement != CSSPoint()) {
1696
ScrollBy(neededDisplacement);
1697
}
1698
1699
// We don't want to redraw on every scale, so throttle it.
1700
if (!mPinchPaintTimerSet) {
1701
const int delay = StaticPrefs::apz_scale_repaint_delay_ms();
1702
if (delay >= 0) {
1703
if (RefPtr<GeckoContentController> controller =
1704
GetGeckoContentController()) {
1705
mPinchPaintTimerSet = true;
1706
controller->PostDelayedTask(
1707
NewRunnableMethod(
1708
"layers::AsyncPanZoomController::"
1709
"DoDelayedRequestContentRepaint",
1710
this,
1711
&AsyncPanZoomController::DoDelayedRequestContentRepaint),
1712
delay);
1713
}
1714
}
1715
}
1716
1717
UpdateSharedCompositorFrameMetrics();
1718
1719
} else {
1720
// Trigger a repaint request after scrolling.
1721
RequestContentRepaint();
1722
}
1723
1724
// We did a ScrollBy call above even if we didn't do a scale, so we
1725
// should composite for that.
1726
ScheduleComposite();
1727
}
1728
1729
return nsEventStatus_eConsumeNoDefault;
1730
}
1731
1732
nsEventStatus AsyncPanZoomController::OnScaleEnd(
1733
const PinchGestureInput& aEvent) {
1734
APZC_LOG("%p got a scale-end in state %d\n", this, mState);
1735
1736
mPinchPaintTimerSet = false;
1737
1738
if (HasReadyTouchBlock() &&
1739
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1740
return nsEventStatus_eIgnore;
1741
}
1742
1743
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
1744
// per-document basis.
1745
if (!StaticPrefs::apz_allow_zooming()) {
1746
if (RefPtr<GeckoContentController> controller =
1747
GetGeckoContentController()) {
1748
controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0,
1749
aEvent.modifiers);
1750
}
1751
}
1752
1753
{
1754
RecursiveMutexAutoLock lock(mRecursiveMutex);
1755
ScheduleComposite();
1756
RequestContentRepaint();
1757
UpdateSharedCompositorFrameMetrics();
1758
}
1759
1760
mPinchEventBuffer.clear();
1761
1762
// Non-negative focus point would indicate that one finger is still down
1763
if (aEvent.mLocalFocusPoint != PinchGestureInput::BothFingersLifted()) {
1764
if (mZoomConstraints.mAllowZoom) {
1765
mPanDirRestricted = false;
1766
mX.StartTouch(aEvent.mLocalFocusPoint.x, aEvent.mTime);
1767
mY.StartTouch(aEvent.mLocalFocusPoint.y, aEvent.mTime);
1768
SetState(TOUCHING);
1769
} else {
1770
// If zooming isn't allowed, StartTouch() was already called
1771
// in OnScaleBegin().
1772
StartPanning(ToExternalPoint(aEvent.mScreenOffset, aEvent.mFocusPoint));
1773
}
1774
} else {
1775
// Otherwise, handle the fingers being lifted.
1776
1777
// Some of the code paths below, like ScrollSnap() or HandleEndOfPan(),
1778
// may start an animation, but otherwise we want to end up in the NOTHING
1779
// state. To avoid state change notification churn, we use a
1780
// notification blocker.
1781
bool stateWasPinching = (mState == PINCHING);
1782
StateChangeNotificationBlocker blocker(this);
1783
SetState(NOTHING);
1784
1785
if (mZoomConstraints.mAllowZoom) {
1786
RecursiveMutexAutoLock lock(mRecursiveMutex);
1787
1788
// We can get into a situation where we are overscrolled at the end of a
1789
// pinch if we go into overscroll with a two-finger pan, and then turn
1790
// that into a pinch by increasing the span sufficiently. In such a case,
1791
// there is no snap-back animation to get us out of overscroll, so we need
1792
// to get out of it somehow.
1793
// Moreover, in cases of scroll handoff, the overscroll can be on an APZC
1794
// further up in the handoff chain rather than on the current APZC, so
1795
// we need to clear overscroll along the entire handoff chain.
1796
if (HasReadyTouchBlock()) {
1797
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
1798
} else {
1799
ClearOverscroll();
1800
}
1801
// Along with clearing the overscroll, we also want to snap to the nearest
1802
// snap point as appropriate.
1803
ScrollSnap();
1804
} else {
1805
// when zoom is not allowed
1806
mX.EndTouch(aEvent.mTime);
1807
mY.EndTouch(aEvent.mTime);
1808
if (stateWasPinching) {
1809
// still pinching
1810
if (HasReadyTouchBlock()) {
1811
return HandleEndOfPan();
1812
}
1813
}
1814
}
1815
}
1816
return nsEventStatus_eConsumeNoDefault;
1817
}
1818
1819
nsEventStatus AsyncPanZoomController::HandleEndOfPan() {
1820
MOZ_ASSERT(GetCurrentTouchBlock() || GetCurrentPanGestureBlock());
1821
GetCurrentInputBlock()->GetOverscrollHandoffChain()->FlushRepaints();
1822
ParentLayerPoint flingVelocity = GetVelocityVector();
1823
1824
// Clear our velocities; if DispatchFling() gives the fling to us,
1825
// the fling velocity gets *added* to our existing velocity in
1826
// AcceptFling().
1827
mX.SetVelocity(0);
1828
mY.SetVelocity(0);
1829
// Clear our state so that we don't stay in the PANNING state
1830
// if DispatchFling() gives the fling to somone else. However,
1831
// don't send the state change notification until we've determined
1832
// what our final state is to avoid notification churn.
1833
StateChangeNotificationBlocker blocker(this);
1834
SetState(NOTHING);
1835
1836
APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
1837
flingVelocity.Length().value,
1838
StaticPrefs::apz_fling_min_velocity_threshold());
1839
1840
if (flingVelocity.Length() <
1841
StaticPrefs::apz_fling_min_velocity_threshold()) {
1842
// Relieve overscroll now if needed, since we will not transition to a fling
1843
// animation and then an overscroll animation, and relieve it then.
1844
GetCurrentInputBlock()