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/ScrollTypes.h"
49
#include "mozilla/StaticPrefs_apz.h"
50
#include "mozilla/StaticPrefs_general.h"
51
#include "mozilla/StaticPrefs_gfx.h"
52
#include "mozilla/StaticPrefs_mousewheel.h"
53
#include "mozilla/StaticPrefs_layers.h"
54
#include "mozilla/StaticPrefs_layout.h"
55
#include "mozilla/StaticPrefs_slider.h"
56
#include "mozilla/StaticPrefs_test.h"
57
#include "mozilla/StaticPrefs_toolkit.h"
58
#include "mozilla/Telemetry.h" // for Telemetry
59
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
60
#include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
61
// note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
62
#include "mozilla/dom/Touch.h" // for Touch
63
#include "mozilla/gfx/BasePoint.h" // for BasePoint
64
#include "mozilla/gfx/BaseRect.h" // for BaseRect
65
#include "mozilla/gfx/Point.h" // for Point, RoundedToInt, etc
66
#include "mozilla/gfx/Rect.h" // for RoundedIn
67
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
68
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
69
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
70
#include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
71
#include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
72
#include "mozilla/layers/CompositorController.h" // for CompositorController
73
#include "mozilla/layers/DirectionUtils.h" // for GetAxis{Start,End,Length,Scale}
74
#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
75
#include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
76
#include "mozilla/mozalloc.h" // for operator new, etc
77
#include "mozilla/Unused.h" // for unused
78
#include "mozilla/FloatingPoint.h" // for FuzzyEquals*
79
#include "nsAlgorithm.h" // for clamped
80
#include "nsCOMPtr.h" // for already_AddRefed
81
#include "nsDebug.h" // for NS_WARNING
82
#include "nsMathUtils.h" // for NS_hypot
83
#include "nsPoint.h" // for nsIntPoint
84
#include "nsStyleConsts.h"
85
#include "nsTimingFunction.h"
86
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
87
#include "nsThreadUtils.h" // for NS_IsMainThread
88
#include "nsViewportInfo.h" // for kViewportMinScale, kViewportMaxScale
89
#include "prsystem.h" // for PR_GetPhysicalMemorySize
90
#include "SharedMemoryBasic.h" // for SharedMemoryBasic
91
#include "ScrollSnap.h" // for ScrollSnapUtils
92
#include "ScrollAnimationPhysics.h" // for ComputeAcceleratedWheelDelta
93
#include "WheelScrollAnimation.h"
94
#include "KeyboardScrollAnimation.h"
95
#if defined(MOZ_WIDGET_ANDROID)
96
# include "AndroidAPZ.h"
97
# include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
98
#endif // defined(MOZ_WIDGET_ANDROID)
99
100
static mozilla::LazyLogModule sApzCtlLog("apz.controller");
101
#define APZC_LOG(...) MOZ_LOG(sApzCtlLog, LogLevel::Debug, (__VA_ARGS__))
102
#define APZC_LOGV(...) MOZ_LOG(sApzCtlLog, LogLevel::Verbose, (__VA_ARGS__))
103
104
#define APZC_LOG_FM_COMMON(fm, prefix, level, ...) \
105
if (MOZ_LOG_TEST(sApzCtlLog, level)) { \
106
std::stringstream ss; \
107
ss << nsPrintfCString(prefix, __VA_ARGS__).get(); \
108
AppendToString(ss, fm, ":", "", true); \
109
MOZ_LOG(sApzCtlLog, level, ("%s\n", ss.str().c_str())); \
110
}
111
#define APZC_LOG_FM(fm, prefix, ...) \
112
APZC_LOG_FM_COMMON(fm, prefix, LogLevel::Debug, __VA_ARGS__)
113
#define APZC_LOGV_FM(fm, prefix, ...) \
114
APZC_LOG_FM_COMMON(fm, prefix, LogLevel::Verbose, __VA_ARGS__)
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 StaticPrefList.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(
581
const AsyncPanZoomController*,
582
const RecursiveMutexAutoLock& aProofOfLock);
583
~AutoApplyAsyncTestAttributes();
584
585
private:
586
AsyncPanZoomController* mApzc;
587
FrameMetrics mPrevFrameMetrics;
588
const RecursiveMutexAutoLock& mProofOfLock;
589
};
590
591
AutoApplyAsyncTestAttributes::AutoApplyAsyncTestAttributes(
592
const AsyncPanZoomController* aApzc,
593
const RecursiveMutexAutoLock& aProofOfLock)
594
// Having to use const_cast here seems less ugly than the alternatives
595
// of making several members of AsyncPanZoomController that
596
// ApplyAsyncTestAttributes() modifies |mutable|, or several methods that
597
// query the async transforms non-const.
598
: mApzc(const_cast<AsyncPanZoomController*>(aApzc)),
599
mPrevFrameMetrics(aApzc->Metrics()),
600
mProofOfLock(aProofOfLock) {
601
mApzc->ApplyAsyncTestAttributes(aProofOfLock);
602
}
603
604
AutoApplyAsyncTestAttributes::~AutoApplyAsyncTestAttributes() {
605
mApzc->UnapplyAsyncTestAttributes(mProofOfLock, mPrevFrameMetrics);
606
}
607
608
class ZoomAnimation : public AsyncPanZoomAnimation {
609
public:
610
ZoomAnimation(AsyncPanZoomController& aApzc, const CSSPoint& aStartOffset,
611
const CSSToParentLayerScale2D& aStartZoom,
612
const CSSPoint& aEndOffset,
613
const CSSToParentLayerScale2D& aEndZoom)
614
: mApzc(aApzc),
615
mTotalDuration(TimeDuration::FromMilliseconds(
616
StaticPrefs::apz_zoom_animation_duration_ms())),
617
mStartOffset(aStartOffset),
618
mStartZoom(aStartZoom),
619
mEndOffset(aEndOffset),
620
mEndZoom(aEndZoom) {}
621
622
virtual bool DoSample(FrameMetrics& aFrameMetrics,
623
const TimeDuration& aDelta) override {
624
mDuration += aDelta;
625
double animPosition = mDuration / mTotalDuration;
626
627
if (animPosition >= 1.0) {
628
aFrameMetrics.SetZoom(mEndZoom);
629
mApzc.SetScrollOffset(mEndOffset);
630
return false;
631
}
632
633
// Sample the zoom at the current time point. The sampled zoom
634
// will affect the final computed resolution.
635
float sampledPosition = gZoomAnimationFunction->GetValue(
636
animPosition, ComputedTimingFunction::BeforeFlag::Unset);
637
638
// We scale the scrollOffset linearly with sampledPosition, so the zoom
639
// needs to scale inversely to match.
640
aFrameMetrics.SetZoom(CSSToParentLayerScale2D(
641
1 / (sampledPosition / mEndZoom.xScale +
642
(1 - sampledPosition) / mStartZoom.xScale),
643
1 / (sampledPosition / mEndZoom.yScale +
644
(1 - sampledPosition) / mStartZoom.yScale)));
645
646
mApzc.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
647
mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
648
mEndOffset.y * sampledPosition +
649
mStartOffset.y * (1 - sampledPosition))));
650
651
return true;
652
}
653
654
virtual bool WantsRepaints() override { return false; }
655
656
private:
657
AsyncPanZoomController& mApzc;
658
659
TimeDuration mDuration;
660
const TimeDuration mTotalDuration;
661
662
// Old metrics from before we started a zoom animation. This is only valid
663
// when we are in the "ANIMATED_ZOOM" state. This is used so that we can
664
// interpolate between the start and end frames. We only use the
665
// |mViewportScrollOffset| and |mResolution| fields on this.
666
CSSPoint mStartOffset;
667
CSSToParentLayerScale2D mStartZoom;
668
669
// Target metrics for a zoom to animation. This is only valid when we are in
670
// the "ANIMATED_ZOOM" state. We only use the |mViewportScrollOffset| and
671
// |mResolution| fields on this.
672
CSSPoint mEndOffset;
673
CSSToParentLayerScale2D mEndZoom;
674
};
675
676
class SmoothScrollAnimation : public AsyncPanZoomAnimation {
677
public:
678
SmoothScrollAnimation(AsyncPanZoomController& aApzc,
679
const nsPoint& aInitialPosition,
680
const nsPoint& aInitialVelocity,
681
const nsPoint& aDestination, double aSpringConstant,
682
double aDampingRatio)
683
: mApzc(aApzc),
684
mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
685
aSpringConstant, aDampingRatio),
686
mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
687
aSpringConstant, aDampingRatio) {}
688
689
/**
690
* Advances a smooth scroll simulation based on the time passed in |aDelta|.
691
* This should be called whenever sampling the content transform for this
692
* frame. Returns true if the smooth scroll should be advanced by one frame,
693
* or false if the smooth scroll has ended.
694
*/
695
bool DoSample(FrameMetrics& aFrameMetrics,
696
const TimeDuration& aDelta) override {
697
nsPoint oneParentLayerPixel =
698
CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
699
if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
700
mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
701
// Set the scroll offset to the exact destination. If we allow the scroll
702
// offset to end up being a bit off from the destination, we can get
703
// artefacts like "scroll to the next snap point in this direction"
704
// scrolling to the snap point we're already supposed to be at.
705
mApzc.ClampAndSetScrollOffset(CSSPoint::FromAppUnits(
706
nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination())));
707
return false;
708
}
709
710
mXAxisModel.Simulate(aDelta);
711
mYAxisModel.Simulate(aDelta);
712
713
CSSPoint position = CSSPoint::FromAppUnits(
714
nsPoint(mXAxisModel.GetPosition(), mYAxisModel.GetPosition()));
715
CSSPoint css_velocity = CSSPoint::FromAppUnits(
716
nsPoint(mXAxisModel.GetVelocity(), mYAxisModel.GetVelocity()));
717
718
// Convert from points/second to points/ms
719
ParentLayerPoint velocity =
720
ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f;
721
722
// Keep the velocity updated for the Axis class so that any animations
723
// chained off of the smooth scroll will inherit it.
724
if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) {
725
mApzc.mX.SetVelocity(0);
726
} else {
727
mApzc.mX.SetVelocity(velocity.x);
728
}
729
if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
730
mApzc.mY.SetVelocity(0);
731
} else {
732
mApzc.mY.SetVelocity(velocity.y);
733
}
734
// If we overscroll, hand off to a fling animation that will complete the
735
// spring back.
736
CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
737
ParentLayerPoint displacement =
738
(position - aFrameMetrics.GetScrollOffset()) * zoom;
739
740
ParentLayerPoint overscroll;
741
ParentLayerPoint adjustedOffset;
742
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
743
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
744
745
mApzc.ScrollBy(adjustedOffset / zoom);
746
747
// The smooth scroll may have caused us to reach the end of our scroll
748
// range. This can happen if either the
749
// layout.css.scroll-behavior.damping-ratio preference is set to less than 1
750
// (underdamped) or if a smooth scroll inherits velocity from a fling
751
// gesture.
752
if (!IsZero(overscroll)) {
753
// Hand off a fling with the remaining momentum to the next APZC in the
754
// overscroll handoff chain.
755
756
// We may have reached the end of the scroll range along one axis but
757
// not the other. In such a case we only want to hand off the relevant
758
// component of the fling.
759
if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
760
velocity.x = 0;
761
} else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
762
velocity.y = 0;
763
}
764
765
// To hand off the fling, we attempt to find a target APZC and start a new
766
// fling with the same velocity on that APZC. For simplicity, the actual
767
// overscroll of the current sample is discarded rather than being handed
768
// off. The compositor should sample animations sufficiently frequently
769
// that this is not noticeable. The target APZC is chosen by seeing if
770
// there is an APZC further in the handoff chain which is pannable; if
771
// there isn't, we take the new fling ourselves, entering an overscrolled
772
// state.
773
// Note: APZC is holding mRecursiveMutex, so directly calling
774
// HandleSmoothScrollOverscroll() (which acquires the tree lock) would
775
// violate the lock ordering. Instead we schedule
776
// HandleSmoothScrollOverscroll() to be called after mRecursiveMutex is
777
// released.
778
mDeferredTasks.AppendElement(NewRunnableMethod<ParentLayerPoint>(
779
"layers::AsyncPanZoomController::HandleSmoothScrollOverscroll",
780
&mApzc, &AsyncPanZoomController::HandleSmoothScrollOverscroll,
781
velocity));
782
return false;
783
}
784
785
return true;
786
}
787
788
void SetDestination(const nsPoint& aNewDestination) {
789
mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
790
mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
791
}
792
793
CSSPoint GetDestination() const {
794
return CSSPoint::FromAppUnits(
795
nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
796
}
797
798
SmoothScrollAnimation* AsSmoothScrollAnimation() override { return this; }
799
800
private:
801
AsyncPanZoomController& mApzc;
802
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
803
};
804
805
/*static*/
806
void AsyncPanZoomController::InitializeGlobalState() {
807
static bool sInitialized = false;
808
if (sInitialized) return;
809
sInitialized = true;
810
811
MOZ_ASSERT(NS_IsMainThread());
812
813
gZoomAnimationFunction =
814
new ComputedTimingFunction(nsTimingFunction(StyleTimingKeyword::Ease));
815
ClearOnShutdown(&gZoomAnimationFunction);
816
gVelocityCurveFunction = new ComputedTimingFunction(
817
nsTimingFunction(StaticPrefs::apz_fling_curve_function_x1_AtStartup(),
818
StaticPrefs::apz_fling_curve_function_y1_AtStartup(),
819
StaticPrefs::apz_fling_curve_function_x2_AtStartup(),
820
StaticPrefs::apz_fling_curve_function_y2_AtStartup()));
821
ClearOnShutdown(&gVelocityCurveFunction);
822
823
uint64_t sysmem = PR_GetPhysicalMemorySize();
824
uint64_t threshold = 1LL << 32; // 4 GB in bytes
825
gIsHighMemSystem = sysmem >= threshold;
826
827
PlatformSpecificState::InitializeGlobalState();
828
}
829
830
AsyncPanZoomController::AsyncPanZoomController(
831
LayersId aLayersId, APZCTreeManager* aTreeManager,
832
const RefPtr<InputQueue>& aInputQueue,
833
GeckoContentController* aGeckoContentController, GestureBehavior aGestures)
834
: mLayersId(aLayersId),
835
mRenderRoot(wr::RenderRoot::Default),
836
mGeckoContentController(aGeckoContentController),
837
mRefPtrMonitor("RefPtrMonitor"),
838
// mTreeManager must be initialized before GetFrameTime() is called
839
mTreeManager(aTreeManager),
840
mRecursiveMutex("AsyncPanZoomController"),
841
mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
842
mX(this),
843
mY(this),
844
mPanDirRestricted(false),
845
mPinchLocked(false),
846
mPinchEventBuffer(TimeDuration::FromMilliseconds(
847
StaticPrefs::apz_pinch_lock_buffer_max_age_AtStartup())),
848
mZoomConstraints(false, false,
849
mScrollMetadata.GetMetrics().GetDevPixelsPerCSSPixel() *
850
kViewportMinScale / ParentLayerToScreenScale(1),
851
mScrollMetadata.GetMetrics().GetDevPixelsPerCSSPixel() *
852
kViewportMaxScale / ParentLayerToScreenScale(1)),
853
mLastSampleTime(GetFrameTime()),
854
mLastCheckerboardReport(GetFrameTime()),
855
mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
856
mState(NOTHING),
857
mNotificationBlockers(0),
858
mInputQueue(aInputQueue),
859
mPinchPaintTimerSet(false),
860
mAPZCId(sAsyncPanZoomControllerCount++),
861
mSharedLock(nullptr),
862
mTestAttributeAppliers(0),
863
mAsyncTransformAppliedToContent(false),
864
mTestHasAsyncKeyScrolled(false),
865
mCheckerboardEventLock("APZCBELock") {
866
if (aGestures == USE_GESTURE_DETECTOR) {
867
mGestureEventListener = new GestureEventListener(this);
868
}
869
}
870
871
AsyncPanZoomController::~AsyncPanZoomController() { MOZ_ASSERT(IsDestroyed()); }
872
873
PlatformSpecificStateBase* AsyncPanZoomController::GetPlatformSpecificState() {
874
if (!mPlatformSpecificState) {
875
mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
876
}
877
return mPlatformSpecificState.get();
878
}
879
880
already_AddRefed<GeckoContentController>
881
AsyncPanZoomController::GetGeckoContentController() const {
882
MonitorAutoLock lock(mRefPtrMonitor);
883
RefPtr<GeckoContentController> controller = mGeckoContentController;
884
return controller.forget();
885
}
886
887
already_AddRefed<GestureEventListener>
888
AsyncPanZoomController::GetGestureEventListener() const {
889
MonitorAutoLock lock(mRefPtrMonitor);
890
RefPtr<GestureEventListener> listener = mGestureEventListener;
891
return listener.forget();
892
}
893
894
const RefPtr<InputQueue>& AsyncPanZoomController::GetInputQueue() const {
895
return mInputQueue;
896
}
897
898
void AsyncPanZoomController::Destroy() {
899
AssertOnUpdaterThread();
900
901
CancelAnimation(CancelAnimationFlags::ScrollSnap);
902
903
{ // scope the lock
904
MonitorAutoLock lock(mRefPtrMonitor);
905
mGeckoContentController = nullptr;
906
mGestureEventListener = nullptr;
907
}
908
mParent = nullptr;
909
mTreeManager = nullptr;
910
911
// Only send the release message if the SharedFrameMetrics has been created.
912
if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
913
Unused << mMetricsSharingController->StopSharingMetrics(GetScrollId(),
914
mAPZCId);
915
}
916
917
{ // scope the lock
918
RecursiveMutexAutoLock lock(mRecursiveMutex);
919
mSharedFrameMetricsBuffer = nullptr;
920
delete mSharedLock;
921
mSharedLock = nullptr;
922
}
923
}
924
925
bool AsyncPanZoomController::IsDestroyed() const {
926
return mTreeManager == nullptr;
927
}
928
929
float AsyncPanZoomController::GetDPI() const {
930
if (APZCTreeManager* localPtr = mTreeManager) {
931
return localPtr->GetDPI();
932
}
933
// If this APZC has been destroyed then this value is not going to be
934
// used for anything that the user will end up seeing, so we can just
935
// return 0.
936
return 0.0;
937
}
938
939
ScreenCoord AsyncPanZoomController::GetTouchStartTolerance() const {
940
return (StaticPrefs::apz_touch_start_tolerance() * GetDPI());
941
}
942
943
ScreenCoord AsyncPanZoomController::GetTouchMoveTolerance() const {
944
return (StaticPrefs::apz_touch_move_tolerance() * GetDPI());
945
}
946
947
ScreenCoord AsyncPanZoomController::GetSecondTapTolerance() const {
948
return (StaticPrefs::apz_second_tap_tolerance() * GetDPI());
949
}
950
951
/* static */ AsyncPanZoomController::AxisLockMode
952
AsyncPanZoomController::GetAxisLockMode() {
953
return static_cast<AxisLockMode>(StaticPrefs::apz_axis_lock_mode());
954
}
955
956
/* static */ AsyncPanZoomController::PinchLockMode
957
AsyncPanZoomController::GetPinchLockMode() {
958
return static_cast<PinchLockMode>(StaticPrefs::apz_pinch_lock_mode());
959
}
960
961
bool AsyncPanZoomController::ArePointerEventsConsumable(
962
TouchBlockState* aBlock, const MultiTouchInput& aInput) {
963
uint32_t touchPoints = aInput.mTouches.Length();
964
if (touchPoints == 0) {
965
// Cant' do anything with zero touch points
966
return false;
967
}
968
969
// This logic is simplified, erring on the side of returning true if we're
970
// not sure. It's safer to pretend that we can consume the event and then
971
// not be able to than vice-versa. But at the same time, we should try hard
972
// to return an accurate result, because returning true can trigger a
973
// pointercancel event to web content, which can break certain features
974
// that are using touch-action and handling the pointermove events.
975
//
976
// We could probably enhance this logic to determine things like "we're
977
// not pannable, so we can only zoom in, and the zoom is already maxed
978
// out, so we're not zoomable either" but no need for that at this point.
979
980
bool pannableX = aBlock->TouchActionAllowsPanningX() &&
981
aBlock->GetOverscrollHandoffChain()->CanScrollInDirection(
982
this, ScrollDirection::eHorizontal);
983
bool pannableY = aBlock->TouchActionAllowsPanningY() &&
984
aBlock->GetOverscrollHandoffChain()->CanScrollInDirection(
985
this, ScrollDirection::eVertical);
986
987
bool pannable;
988
989
Maybe<ScrollDirection> panDirection =
990
aBlock->GetBestGuessPanDirection(aInput);
991
if (panDirection == Some(ScrollDirection::eVertical)) {
992
pannable = pannableY;
993
} else if (panDirection == Some(ScrollDirection::eHorizontal)) {
994
pannable = pannableX;
995
} else {
996
// If we don't have a guessed pan direction, err on the side of returning
997
// true.
998
pannable = pannableX || pannableY;
999
}
1000
1001
if (touchPoints == 1) {
1002
return pannable;
1003
}
1004
1005
bool zoomable = mZoomConstraints.mAllowZoom;
1006
zoomable &= (aBlock->TouchActionAllowsPinchZoom());
1007
1008
return pannable || zoomable;
1009
}
1010
1011
nsEventStatus AsyncPanZoomController::HandleDragEvent(
1012
const MouseInput& aEvent, const AsyncDragMetrics& aDragMetrics,
1013
CSSCoord aInitialThumbPos) {
1014
if (!StaticPrefs::apz_drag_enabled()) {
1015
return nsEventStatus_eIgnore;
1016
}
1017
1018
if (!GetApzcTreeManager()) {
1019
return nsEventStatus_eConsumeNoDefault;
1020
}
1021
1022
if (aEvent.mType == MouseInput::MouseType::MOUSE_UP) {
1023
APZC_LOG("%p ending drag\n", this);
1024
SetState(NOTHING);
1025
ScrollSnap();
1026
return nsEventStatus_eConsumeNoDefault;
1027
}
1028
1029
HitTestingTreeNodeAutoLock node;
1030
GetApzcTreeManager()->FindScrollThumbNode(aDragMetrics, node);
1031
if (!node) {
1032
APZC_LOG("%p unable to find scrollthumb node with viewid %" PRIu64 "\n",
1033
this, aDragMetrics.mViewId);
1034
return nsEventStatus_eConsumeNoDefault;
1035
}
1036
1037
if (aEvent.mType == MouseInput::MouseType::MOUSE_DOWN) {
1038
APZC_LOG("%p starting scrollbar drag\n", this);
1039
SetState(SCROLLBAR_DRAG);
1040
}
1041
1042
if (aEvent.mType != MouseInput::MouseType::MOUSE_MOVE) {
1043
APZC_LOG("%p discarding event of type %d\n", this, aEvent.mType);
1044
return nsEventStatus_eConsumeNoDefault;
1045
}
1046
1047
const ScrollbarData& scrollbarData = node->GetScrollbarData();
1048
MOZ_ASSERT(scrollbarData.mScrollbarLayerType ==
1049
layers::ScrollbarLayerType::Thumb);
1050
MOZ_ASSERT(scrollbarData.mDirection.isSome());
1051
ScrollDirection direction = *scrollbarData.mDirection;
1052
1053
bool isMouseAwayFromThumb = false;
1054
if (int snapMultiplier = StaticPrefs::slider_snapMultiplier_AtStartup()) {
1055
// It's fine to ignore the async component of the thumb's transform,
1056
// because any async transform of the thumb will be in the direction of
1057
// scrolling, but here we're interested in the other direction.
1058
ParentLayerRect thumbRect =
1059
(node->GetTransform() * AsyncTransformMatrix())
1060
.TransformBounds(LayerRect(node->GetVisibleRegion().GetBounds()));
1061
ScrollDirection otherDirection = GetPerpendicularDirection(direction);
1062
ParentLayerCoord distance =
1063
GetAxisStart(otherDirection, thumbRect.DistanceTo(aEvent.mLocalOrigin));
1064
ParentLayerCoord thumbWidth = GetAxisLength(otherDirection, thumbRect);
1065
// Avoid triggering this condition spuriously when the thumb is
1066
// offscreen and its visible region is therefore empty.
1067
if (thumbWidth > 0 && thumbWidth * snapMultiplier < distance) {
1068
isMouseAwayFromThumb = true;
1069
APZC_LOG("%p determined mouse is away from thumb, will snap\n", this);
1070
}
1071
}
1072
1073
RecursiveMutexAutoLock lock(mRecursiveMutex);
1074
CSSCoord thumbPosition;
1075
if (isMouseAwayFromThumb) {
1076
thumbPosition = aInitialThumbPos;
1077
} else {
1078
thumbPosition = ConvertScrollbarPoint(aEvent.mLocalOrigin, scrollbarData) -
1079
aDragMetrics.mScrollbarDragOffset;
1080
}
1081
1082
CSSCoord maxThumbPos = scrollbarData.mScrollTrackLength;
1083
maxThumbPos -= scrollbarData.mThumbLength;
1084
1085
float scrollPercent =
1086
maxThumbPos.value == 0.0f ? 0.0f : (float)(thumbPosition / maxThumbPos);
1087
APZC_LOG("%p scrollbar dragged to %f percent\n", this, scrollPercent);
1088
1089
CSSCoord minScrollPosition =
1090
GetAxisStart(direction, Metrics().GetScrollableRect().TopLeft());
1091
CSSCoord maxScrollPosition =
1092
GetAxisStart(direction, Metrics().GetScrollableRect().BottomRight()) -
1093
GetAxisLength(
1094
direction,
1095
Metrics()
1096
.CalculateCompositionBoundsInCssPixelsOfSurroundingContent());
1097
CSSCoord scrollPosition =
1098
minScrollPosition +
1099
(scrollPercent * (maxScrollPosition - minScrollPosition));
1100
1101
scrollPosition = std::max(scrollPosition, minScrollPosition);
1102
scrollPosition = std::min(scrollPosition, maxScrollPosition);
1103
1104
CSSPoint scrollOffset = Metrics().GetScrollOffset();
1105
if (direction == ScrollDirection::eHorizontal) {
1106
scrollOffset.x = scrollPosition;
1107
} else {
1108
scrollOffset.y = scrollPosition;
1109
}
1110
APZC_LOG("%p set scroll offset to %s from scrollbar drag\n", this,
1111
Stringify(scrollOffset).c_str());
1112
SetScrollOffset(scrollOffset);
1113
ScheduleCompositeAndMaybeRepaint();
1114
UpdateSharedCompositorFrameMetrics();
1115
1116
return nsEventStatus_eConsumeNoDefault;
1117
}
1118
1119
nsEventStatus AsyncPanZoomController::HandleInputEvent(
1120
const InputData& aEvent,
1121
const ScreenToParentLayerMatrix4x4& aTransformToApzc) {
1122
APZThreadUtils::AssertOnControllerThread();
1123
1124
nsEventStatus rv = nsEventStatus_eIgnore;
1125
1126
switch (aEvent.mInputType) {
1127
case MULTITOUCH_INPUT: {
1128
MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
1129
RefPtr<GestureEventListener> listener = GetGestureEventListener();
1130
if (listener) {
1131
// We only care about screen coordinates in the gesture listener,
1132
// so we don't bother transforming the event to parent layer coordinates
1133
rv = listener->HandleInputEvent(multiTouchInput);
1134
if (rv == nsEventStatus_eConsumeNoDefault) {
1135
return rv;
1136
}
1137
}
1138
1139
if (!multiTouchInput.TransformToLocal(aTransformToApzc)) {
1140
return rv;
1141
}
1142
1143
switch (multiTouchInput.mType) {
1144
case MultiTouchInput::MULTITOUCH_START:
1145
rv = OnTouchStart(multiTouchInput);
1146
break;
1147
case MultiTouchInput::MULTITOUCH_MOVE:
1148
rv = OnTouchMove(multiTouchInput);
1149
break;
1150
case MultiTouchInput::MULTITOUCH_END:
1151
rv = OnTouchEnd(multiTouchInput);
1152
break;
1153
case MultiTouchInput::MULTITOUCH_CANCEL:
1154
rv = OnTouchCancel(multiTouchInput);
1155
break;
1156
}
1157
break;
1158
}
1159
case PANGESTURE_INPUT: {
1160
PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
1161
if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
1162
return rv;
1163
}
1164
1165
switch (panGestureInput.mType) {
1166
case PanGestureInput::PANGESTURE_MAYSTART:
1167
rv = OnPanMayBegin(panGestureInput);
1168
break;
1169
case PanGestureInput::PANGESTURE_CANCELLED:
1170
rv = OnPanCancelled(panGestureInput);
1171
break;
1172
case PanGestureInput::PANGESTURE_START:
1173
rv = OnPanBegin(panGestureInput);
1174
break;
1175
case PanGestureInput::PANGESTURE_PAN:
1176
rv = OnPan(panGestureInput, true);
1177
break;
1178
case PanGestureInput::PANGESTURE_END:
1179
rv = OnPanEnd(panGestureInput);
1180
break;
1181
case PanGestureInput::PANGESTURE_MOMENTUMSTART:
1182
rv = OnPanMomentumStart(panGestureInput);
1183
break;
1184
case PanGestureInput::PANGESTURE_MOMENTUMPAN:
1185
rv = OnPan(panGestureInput, false);
1186
break;
1187
case PanGestureInput::PANGESTURE_MOMENTUMEND:
1188
rv = OnPanMomentumEnd(panGestureInput);
1189
break;
1190
}
1191
break;
1192
}
1193
case MOUSE_INPUT: {
1194
MouseInput mouseInput = aEvent.AsMouseInput();
1195
if (!mouseInput.TransformToLocal(aTransformToApzc)) {
1196
return rv;
1197
}
1198
break;
1199
}
1200
case SCROLLWHEEL_INPUT: {
1201
ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
1202
if (!scrollInput.TransformToLocal(aTransformToApzc)) {
1203
return rv;
1204
}
1205
1206
rv = OnScrollWheel(scrollInput);
1207
break;
1208
}
1209
case PINCHGESTURE_INPUT: {
1210
// The APZCTreeManager should take care of ensuring that only root-content
1211
// APZCs get pinch inputs.
1212
MOZ_ASSERT(IsRootContent());
1213
PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
1214
if (!pinchInput.TransformToLocal(aTransformToApzc)) {
1215
return rv;
1216
}
1217
1218
rv = HandleGestureEvent(pinchInput);
1219
break;
1220
}
1221
case TAPGESTURE_INPUT: {
1222
TapGestureInput tapInput = aEvent.AsTapGestureInput();
1223
if (!tapInput.TransformToLocal(aTransformToApzc)) {
1224
return rv;
1225
}
1226
1227
rv = HandleGestureEvent(tapInput);
1228
break;
1229
}
1230
case KEYBOARD_INPUT: {
1231
const KeyboardInput& keyInput = aEvent.AsKeyboardInput();
1232
rv = OnKeyboard(keyInput);
1233
break;
1234
}
1235
}
1236
1237
return rv;
1238
}
1239
1240
nsEventStatus AsyncPanZoomController::HandleGestureEvent(
1241
const InputData& aEvent) {
1242
APZThreadUtils::AssertOnControllerThread();
1243
1244
nsEventStatus rv = nsEventStatus_eIgnore;
1245
1246
switch (aEvent.mInputType) {
1247
case PINCHGESTURE_INPUT: {
1248
// This may be invoked via a one-touch-pinch gesture from
1249
// GestureEventListener. In that case we want redirect it to the enclosing
1250
// root-content APZC.
1251
if (!IsRootContent()) {
1252
if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
1253
if (RefPtr<AsyncPanZoomController> root =
1254
treeManagerLocal->FindZoomableApzc(this)) {
1255
rv = root->HandleGestureEvent(aEvent);
1256
}
1257
}
1258
break;
1259
}
1260
PinchGestureInput pinchGestureInput = aEvent.AsPinchGestureInput();
1261
pinchGestureInput.TransformToLocal(GetTransformToThis());
1262
switch (pinchGestureInput.mType) {
1263
case PinchGestureInput::PINCHGESTURE_START:
1264
rv = OnScaleBegin(pinchGestureInput);
1265
break;
1266
case PinchGestureInput::PINCHGESTURE_SCALE:
1267
rv = OnScale(pinchGestureInput);
1268
break;
1269
case PinchGestureInput::PINCHGESTURE_END:
1270
rv = OnScaleEnd(pinchGestureInput);
1271
break;
1272
}
1273
break;
1274
}
1275
case TAPGESTURE_INPUT: {
1276
TapGestureInput tapGestureInput = aEvent.AsTapGestureInput();
1277
tapGestureInput.TransformToLocal(GetTransformToThis());
1278
switch (tapGestureInput.mType) {
1279
case TapGestureInput::TAPGESTURE_LONG:
1280
rv = OnLongPress(tapGestureInput);
1281
break;
1282
case TapGestureInput::TAPGESTURE_LONG_UP:
1283
rv = OnLongPressUp(tapGestureInput);
1284
break;
1285
case TapGestureInput::TAPGESTURE_UP:
1286
rv = OnSingleTapUp(tapGestureInput);
1287
break;
1288
case TapGestureInput::TAPGESTURE_CONFIRMED:
1289
rv = OnSingleTapConfirmed(tapGestureInput);
1290
break;
1291
case TapGestureInput::TAPGESTURE_DOUBLE:
1292
rv = OnDoubleTap(tapGestureInput);
1293
break;
1294
case TapGestureInput::TAPGESTURE_SECOND:
1295
rv = OnSecondTap(tapGestureInput);
1296
break;
1297
case TapGestureInput::TAPGESTURE_CANCEL:
1298
rv = OnCancelTap(tapGestureInput);
1299
break;
1300
}
1301
break;
1302
}
1303
default:
1304
MOZ_ASSERT_UNREACHABLE("Unhandled input event");
1305
break;
1306
}
1307
1308
return rv;
1309
}
1310
1311
void AsyncPanZoomController::HandleDynamicToolbarMovement(
1312
uint32_t aStartTimestampMs, uint32_t aEndTimestampMs,
1313
ParentLayerCoord aDeltaY) {
1314
mY.HandleDynamicToolbarMovement(aStartTimestampMs, aEndTimestampMs, aDeltaY);
1315
}
1316
1317
void AsyncPanZoomController::StartAutoscroll(const ScreenPoint& aPoint) {
1318
// Cancel any existing animation.
1319
CancelAnimation();
1320
1321
SetState(AUTOSCROLL);
1322
StartAnimation(new AutoscrollAnimation(*this, aPoint));
1323
}
1324
1325
void AsyncPanZoomController::StopAutoscroll() {
1326
if (mState == AUTOSCROLL) {
1327
CancelAnimation(TriggeredExternally);
1328
}
1329
}
1330
1331
nsEventStatus AsyncPanZoomController::OnTouchStart(
1332
const MultiTouchInput& aEvent) {
1333
APZC_LOG("%p got a touch-start in state %d\n", this, mState);
1334
mPanDirRestricted = false;
1335
1336
switch (mState) {
1337
case FLING:
1338
case ANIMATING_ZOOM:
1339
case SMOOTH_SCROLL:
1340
case OVERSCROLL_ANIMATION:
1341
case WHEEL_SCROLL:
1342
case KEYBOARD_SCROLL:
1343
case PAN_MOMENTUM:
1344
case AUTOSCROLL:
1345
MOZ_ASSERT(GetCurrentTouchBlock());
1346
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(
1347
ExcludeOverscroll);
1348
[[fallthrough]];
1349
case SCROLLBAR_DRAG:
1350
case NOTHING: {
1351
ParentLayerPoint point = GetFirstTouchPoint(aEvent);
1352
mStartTouch = GetFirstExternalTouchPoint(aEvent);
1353
StartTouch(point, aEvent.mTime);
1354
if (RefPtr<GeckoContentController> controller =
1355
GetGeckoContentController()) {
1356
MOZ_ASSERT(GetCurrentTouchBlock());
1357
controller->NotifyAPZStateChange(
1358
GetGuid(), APZStateChange::eStartTouch,
1359
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(
1360
this));
1361
}
1362
SetState(TOUCHING);
1363
break;
1364
}
1365
case TOUCHING:
1366
case PANNING:
1367
case PANNING_LOCKED_X:
1368
case PANNING_LOCKED_Y:
1369
case PINCHING:
1370
NS_WARNING("Received impossible touch in OnTouchStart");
1371
break;
1372
}
1373
1374
return nsEventStatus_eConsumeNoDefault;
1375
}
1376
1377
nsEventStatus AsyncPanZoomController::OnTouchMove(
1378
const MultiTouchInput& aEvent) {
1379
APZC_LOG("%p got a touch-move in state %d\n", this, mState);
1380
switch (mState) {
1381
case FLING:
1382
case SMOOTH_SCROLL:
1383
case NOTHING:
1384
case ANIMATING_ZOOM:
1385
// May happen if the user double-taps and drags without lifting after the
1386
// second tap. Ignore the move if this happens.
1387
return nsEventStatus_eIgnore;
1388
1389
case TOUCHING: {
1390
ScreenCoord panThreshold = GetTouchStartTolerance();
1391
ExternalPoint extPoint = GetFirstExternalTouchPoint(aEvent);
1392
// We intentionally skip the UpdateWithTouchAtDevicePoint call when the
1393
// panThreshold is zero. This ensures more deterministic behaviour during
1394
// testing. If we call that, Axis::mPos gets updated to the point of this
1395
// touchmove event, but we "consume" the move to overcome the
1396
// panThreshold, so it's hard to pan a specific amount reliably from a
1397
// mochitest.
1398
if (panThreshold > 0.0f) {
1399
UpdateWithTouchAtDevicePoint(aEvent);
1400
if (PanVector(extPoint).Length() < panThreshold) {
1401
return nsEventStatus_eIgnore;
1402
}
1403
}
1404
1405
MOZ_ASSERT(GetCurrentTouchBlock());
1406
if (StaticPrefs::layout_css_touch_action_enabled() &&
1407
GetCurrentTouchBlock()->TouchActionAllowsPanningXY()) {
1408
// User tries to trigger a touch behavior. If allowed touch behavior is
1409
// vertical pan
1410
// + horizontal pan (touch-action value is equal to AUTO) we can return
1411
// ConsumeNoDefault status immediately to trigger cancel event further.
1412
// It should happen independent of the parent type (whether it is
1413
// scrolling or not).
1414
StartPanning(extPoint);
1415
return nsEventStatus_eConsumeNoDefault;
1416
}
1417
1418
return StartPanning(extPoint);
1419
}
1420
1421
case PANNING:
1422
case PANNING_LOCKED_X:
1423
case PANNING_LOCKED_Y:
1424
case PAN_MOMENTUM:
1425
TrackTouch(aEvent);
1426
return nsEventStatus_eConsumeNoDefault;
1427
1428
case PINCHING:
1429
// The scale gesture listener should have handled this.
1430
NS_WARNING(
1431
"Gesture listener should have handled pinching in OnTouchMove.");
1432
return nsEventStatus_eIgnore;
1433
1434
case WHEEL_SCROLL:
1435
case KEYBOARD_SCROLL:
1436
case OVERSCROLL_ANIMATION:
1437
case AUTOSCROLL:
1438
case SCROLLBAR_DRAG:
1439
// Should not receive a touch-move in the OVERSCROLL_ANIMATION state
1440
// as touch blocks that begin in an overscrolled state cancel the
1441
// animation. The same is true for wheel scroll animations.
1442
NS_WARNING("Received impossible touch in OnTouchMove");
1443
break;
1444
}
1445
1446
return nsEventStatus_eConsumeNoDefault;
1447
}
1448
1449
nsEventStatus AsyncPanZoomController::OnTouchEnd(
1450
const MultiTouchInput& aEvent) {
1451
APZC_LOG("%p got a touch-end in state %d\n", this, mState);
1452
OnTouchEndOrCancel();
1453
1454
// In case no touch behavior triggered previously we can avoid sending
1455
// scroll events or requesting content repaint. This condition is added
1456
// to make tests consistent - in case touch-action is NONE (and therefore
1457
// no pans/zooms can be performed) we expected neither scroll or repaint
1458
// events.
1459
if (mState != NOTHING) {
1460
RecursiveMutexAutoLock lock(mRecursiveMutex);
1461
}
1462
1463
switch (mState) {
1464
case FLING:
1465
// Should never happen.
1466
NS_WARNING("Received impossible touch end in OnTouchEnd.");
1467
[[fallthrough]];
1468
case ANIMATING_ZOOM:
1469
case SMOOTH_SCROLL:
1470
case NOTHING:
1471
// May happen if the user double-taps and drags without lifting after the
1472
// second tap. Ignore if this happens.
1473
return nsEventStatus_eIgnore;
1474
1475
case TOUCHING:
1476
// We may have some velocity stored on the axis from move events
1477
// that were not big enough to trigger scrolling. Clear that out.
1478
SetVelocityVector(ParentLayerPoint(0, 0));
1479
MOZ_ASSERT(GetCurrentTouchBlock());
1480
APZC_LOG("%p still has %u touch points active\n", this,
1481
GetCurrentTouchBlock()->GetActiveTouchCount());
1482
// In cases where the user is panning, then taps the second finger without
1483
// entering a pinch, we will arrive here when the second finger is lifted.
1484
// However the first finger is still down so we want to remain in state
1485
// TOUCHING.
1486
if (GetCurrentTouchBlock()->GetActiveTouchCount() == 0) {
1487
// It's possible we may be overscrolled if the user tapped during a
1488
// previous overscroll pan. Make sure to snap back in this situation.
1489
// An ancestor APZC could be overscrolled instead of this APZC, so
1490
// walk the handoff chain as well.
1491
GetCurrentTouchBlock()
1492
->GetOverscrollHandoffChain()
1493
->SnapBackOverscrolledApzc(this);
1494
// SnapBackOverscrolledApzc() will put any APZC it causes to snap back
1495
// into the OVERSCROLL_ANIMATION state. If that's not us, since we're
1496
// done TOUCHING enter the NOTHING state.
1497
if (mState != OVERSCROLL_ANIMATION) {
1498
SetState(NOTHING);
1499
}
1500
}
1501
return nsEventStatus_eIgnore;
1502
1503
case PANNING:
1504
case PANNING_LOCKED_X:
1505
case PANNING_LOCKED_Y:
1506
case PAN_MOMENTUM: {
1507
MOZ_ASSERT(GetCurrentTouchBlock());
1508
EndTouch(aEvent.mTime);
1509
return HandleEndOfPan();
1510
}
1511
case PINCHING:
1512
SetState(NOTHING);
1513
// Scale gesture listener should have handled this.
1514
NS_WARNING(
1515
"Gesture listener should have handled pinching in OnTouchEnd.");
1516
return nsEventStatus_eIgnore;
1517
1518
case WHEEL_SCROLL:
1519
case KEYBOARD_SCROLL:
1520
case OVERSCROLL_ANIMATION:
1521
case AUTOSCROLL:
1522
case SCROLLBAR_DRAG:
1523
// Should not receive a touch-end in the OVERSCROLL_ANIMATION state
1524
// as touch blocks that begin in an overscrolled state cancel the
1525
// animation. The same is true for WHEEL_SCROLL.
1526
NS_WARNING("Received impossible touch in OnTouchEnd");
1527
break;
1528
}
1529
1530
return nsEventStatus_eConsumeNoDefault;
1531
}
1532
1533
nsEventStatus AsyncPanZoomController::OnTouchCancel(
1534
const MultiTouchInput& aEvent) {
1535
APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
1536
OnTouchEndOrCancel();
1537
CancelAnimationAndGestureState();
1538
return nsEventStatus_eConsumeNoDefault;
1539
}
1540
1541
nsEventStatus AsyncPanZoomController::OnScaleBegin(
1542
const PinchGestureInput& aEvent) {
1543
APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
1544
1545
mPinchLocked = false;
1546
mPinchPaintTimerSet = false;
1547
// Note that there may not be a touch block at this point, if we received the
1548
// PinchGestureEvent directly from widget code without any touch events.
1549
if (HasReadyTouchBlock() &&
1550
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1551
return nsEventStatus_eIgnore;
1552
}
1553
1554
// If zooming is not allowed, this is a two-finger pan.
1555
// Start tracking panning distance and velocity.
1556
if (!mZoomConstraints.mAllowZoom) {
1557
StartTouch(aEvent.mLocalFocusPoint, 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
SetVelocityVector(ParentLayerPoint(0, 0));
1574
RecursiveMutexAutoLock lock(mRecursiveMutex);
1575
mLastZoomFocus =
1576
aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft();
1577
1578
mPinchEventBuffer.push(aEvent);
1579
1580
return nsEventStatus_eConsumeNoDefault;
1581
}
1582
1583
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
1584
APZC_LOG("%p got a scale in state %d\n", this, mState);
1585
1586
if (HasReadyTouchBlock() &&
1587
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1588
return nsEventStatus_eIgnore;
1589
}
1590
1591
if (mState != PINCHING) {
1592
return nsEventStatus_eConsumeNoDefault;
1593
}
1594
1595
mPinchEventBuffer.push(aEvent);
1596
HandlePinchLocking();
1597
bool allowZoom = mZoomConstraints.mAllowZoom && !mPinchLocked;
1598
1599
// If zooming is not allowed, this is a two-finger pan.
1600
// Tracking panning distance and velocity.
1601
// UpdateWithTouchAtDevicePoint() acquires the tree lock, so
1602
// it cannot be called while the mRecursiveMutex lock is held.
1603
if (!allowZoom) {
1604
mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x, aEvent.mTime);
1605
mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y, aEvent.mTime);
1606
}
1607
1608
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
1609
// per-document basis.
1610
if (!StaticPrefs::apz_allow_zooming()) {
1611
if (RefPtr<GeckoContentController> controller =
1612
GetGeckoContentController()) {
1613
controller->NotifyPinchGesture(
1614
aEvent.mType, GetGuid(),
1615
ViewAs<LayoutDevicePixel>(
1616
aEvent.mCurrentSpan - aEvent.mPreviousSpan,
1617
PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
1618
aEvent.modifiers);
1619
}
1620
}
1621
1622
{
1623
RecursiveMutexAutoLock lock(mRecursiveMutex);
1624
// Only the root APZC is zoomable, and the root APZC is not allowed to have
1625
// different x and y scales. If it did, the calculations in this function
1626
// would have to be adjusted (as e.g. it would no longer be valid to take
1627
// the minimum or maximum of the ratios of the widths and heights of the
1628
// page rect and the composition bounds).
1629
MOZ_ASSERT(Metrics().IsRootContent());
1630
MOZ_ASSERT(Metrics().GetZoom().AreScalesSame());
1631
1632
CSSToParentLayerScale userZoom = Metrics().GetZoom().ToScaleFactor();
1633
ParentLayerPoint focusPoint =
1634
aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft();
1635
CSSPoint cssFocusPoint = focusPoint / Metrics().GetZoom();
1636
1637
ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
1638
mLastZoomFocus = focusPoint;
1639
// If displacing by the change in focus point will take us off page bounds,
1640
// then reduce the displacement such that it doesn't.
1641
focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
1642
focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
1643
ScrollBy(focusChange / userZoom);
1644
1645
// If the span is zero or close to it, we don't want to process this zoom
1646
// change because we're going to get wonky numbers for the spanRatio. So
1647
// let's bail out here. Note that we do this after the focus-change-scroll
1648
// above, so that if we have a pinch with zero span but changing focus,
1649
// such as generated by some Synaptics touchpads on Windows, we still
1650
// scroll properly.
1651
float prevSpan = aEvent.mPreviousSpan;
1652
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
1653
// We might have done a nonzero ScrollBy above, so update metrics and
1654
// repaint/recomposite
1655
ScheduleCompositeAndMaybeRepaint();
1656
UpdateSharedCompositorFrameMetrics();
1657
return nsEventStatus_eConsumeNoDefault;
1658
}
1659
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
1660
1661
// When we zoom in with focus, we can zoom too much towards the boundaries
1662
// that we actually go over them. These are the needed displacements along
1663
// either axis such that we don't overscroll the boundaries when zooming.
1664
CSSPoint neededDisplacement;
1665
1666
CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom;
1667
CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom;
1668
realMinZoom.scale =
1669
std::max(realMinZoom.scale, Metrics().GetCompositionBounds().Width() /
1670
Metrics().GetScrollableRect().Width());
1671
realMinZoom.scale =
1672
std::max(realMinZoom.scale, Metrics().GetCompositionBounds().Height() /
1673
Metrics().GetScrollableRect().Height());
1674
if (realMaxZoom < realMinZoom) {
1675
realMaxZoom = realMinZoom;
1676
}
1677
1678
bool doScale = allowZoom && ((spanRatio > 1.0 && userZoom < realMaxZoom) ||
1679
(spanRatio < 1.0 && userZoom > realMinZoom));
1680
1681
if (doScale) {
1682
spanRatio = clamped(spanRatio, realMinZoom.scale / userZoom.scale,
1683
realMaxZoom.scale / userZoom.scale);
1684
1685
// Note that the spanRatio here should never put us into OVERSCROLL_BOTH
1686
// because up above we clamped it.
1687
neededDisplacement.x =
1688
-mX.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.x);
1689
neededDisplacement.y =
1690
-mY.ScaleWillOverscrollAmount(spanRatio, cssFocusPoint.y);
1691
1692
ScaleWithFocus(spanRatio, cssFocusPoint);
1693
1694
if (neededDisplacement != CSSPoint()) {
1695
ScrollBy(neededDisplacement);
1696
}
1697
1698
// We don't want to redraw on every scale, so throttle it.
1699
if (!mPinchPaintTimerSet) {
1700
const int delay = StaticPrefs::apz_scale_repaint_delay_ms();
1701
if (delay >= 0) {
1702
if (RefPtr<GeckoContentController> controller =
1703
GetGeckoContentController()) {
1704
mPinchPaintTimerSet = true;
1705
controller->PostDelayedTask(
1706
NewRunnableMethod(
1707
"layers::AsyncPanZoomController::"
1708
"DoDelayedRequestContentRepaint",
1709
this,
1710
&AsyncPanZoomController::DoDelayedRequestContentRepaint),
1711
delay);
1712
}
1713
}
1714
}
1715
1716
UpdateSharedCompositorFrameMetrics();
1717
1718
} else {
1719
// Trigger a repaint request after scrolling.
1720
RequestContentRepaint();
1721
}
1722
1723
// We did a ScrollBy call above even if we didn't do a scale, so we
1724
// should composite for that.
1725
ScheduleComposite();
1726
}
1727
1728
return nsEventStatus_eConsumeNoDefault;
1729
}
1730
1731
nsEventStatus AsyncPanZoomController::OnScaleEnd(
1732
const PinchGestureInput& aEvent) {
1733
APZC_LOG("%p got a scale-end in state %d\n", this, mState);
1734
1735
mPinchPaintTimerSet = false;
1736
1737
if (HasReadyTouchBlock() &&
1738
!GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
1739
return nsEventStatus_eIgnore;
1740
}
1741
1742
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
1743
// per-document basis.
1744
if (!StaticPrefs::apz_allow_zooming()) {
1745
if (RefPtr<GeckoContentController> controller =
1746
GetGeckoContentController()) {
1747
controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0,
1748
aEvent.modifiers);
1749
}
1750
}
1751
1752
{
1753
RecursiveMutexAutoLock lock(mRecursiveMutex);
1754
ScheduleComposite();
1755
RequestContentRepaint();
1756
UpdateSharedCompositorFrameMetrics();
1757
}
1758
1759
mPinchEventBuffer.clear();
1760
1761
// Non-negative focus point would indicate that one finger is still down
1762
if (aEvent.mLocalFocusPoint != PinchGestureInput::BothFingersLifted()) {
1763
if (mZoomConstraints.mAllowZoom) {
1764
mPanDirRestricted = false;
1765
StartTouch(aEvent.mLocalFocusPoint, aEvent.mTime);
1766
SetState(TOUCHING);
1767
} else {
1768
// If zooming isn't allowed, StartTouch() was already called
1769
// in OnScaleBegin().
1770
StartPanning(ToExternalPoint(aEvent.mScreenOffset, aEvent.mFocusPoint));
1771
}
1772
} else {
1773
// Otherwise, handle the fingers being lifted.
1774
1775
// Some of the code paths below, like ScrollSnap() or HandleEndOfPan(),
1776
// may start an animation, but otherwise we want to end up in the NOTHING
1777
// state. To avoid state change notification churn, we use a
1778
// notification blocker.
1779
bool stateWasPinching = (mState == PINCHING);
1780
StateChangeNotificationBlocker blocker(this);
1781
SetState(NOTHING);
1782
1783
if (mZoomConstraints.mAllowZoom) {
1784
RecursiveMutexAutoLock lock(mRecursiveMutex);
1785
1786
// We can get into a situation where we are overscrolled at the end of a
1787
// pinch if we go into overscroll with a two-finger pan, and then turn
1788
// that into a pinch by increasing the span sufficiently. In such a case,
1789
// there is no snap-back animation to get us out of overscroll, so we need
1790
// to get out of it somehow.
1791
// Moreover, in cases of scroll handoff, the overscroll can be on an APZC
1792
// further up in the handoff chain rather than on the current APZC, so
1793
// we need to clear overscroll along the entire handoff chain.
1794
if (HasReadyTouchBlock()) {
1795
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
1796
} else {
1797
ClearOverscroll();
1798
}
1799
// Along with clearing the overscroll, we also want to snap to the nearest
1800
// snap point as appropriate.
1801
ScrollSnap();
1802
} else {
1803
// when zoom is not allowed
1804
EndTouch(aEvent.mTime);
1805
if (stateWasPinching) {
1806
// still pinching
1807
if (HasReadyTouchBlock()) {
1808
return HandleEndOfPan();
1809
}
1810
}
1811
}
1812
}
1813
return nsEventStatus_eConsumeNoDefault;
1814
}
1815
1816
nsEventStatus AsyncPanZoomController::HandleEndOfPan() {
1817
MOZ_ASSERT(GetCurrentTouchBlock() || GetCurrentPanGestureBlock());
1818
GetCurrentInputBlock()->GetOverscrollHandoffChain()->FlushRepaints();
1819
ParentLayerPoint flingVelocity = GetVelocityVector();
1820
1821
// Clear our velocities; if DispatchFling() gives the fling to us,
1822
// the fling velocity gets *added* to our existing velocity in
1823
// AcceptFling().
1824
SetVelocityVector(ParentLayerPoint(0, 0));
1825
// Clear our state so that we don't stay in the PANNING state
1826
// if DispatchFling() gives the fling to somone else. However,
1827
// don't send the state change notification until we've determined
1828
// what our final state is to avoid notification churn.
1829
StateChangeNotificationBlocker blocker(this);
1830
SetState(NOTHING);
1831
1832
APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
1833
flingVelocity.Length().value,
1834
StaticPrefs::apz_fling_min_velocity_threshold());
1835
1836
if (flingVelocity.Length() <
1837
StaticPrefs::apz_fling_min_velocity_threshold()) {
1838
// Relieve overscroll now if needed, since we will not transition to a fling
1839
// animation and then an overscroll animation, and relieve it then.
1840
GetCurrentInputBlock()
1841
->GetOverscrollHandoffChain()
1842
->SnapBackOverscrolledApzc(this);
1843
return nsEventStatus_eConsumeNoDefault;
1844