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