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 <stack>
8
#include <unordered_set>
9
#include "APZCTreeManager.h"
10
#include "AsyncPanZoomController.h"
11
#include "Compositor.h" // for Compositor
12
#include "DragTracker.h" // for DragTracker
13
#include "GenericFlingAnimation.h" // for FLING_LOG
14
#include "HitTestingTreeNode.h" // for HitTestingTreeNode
15
#include "InputBlockState.h" // for InputBlockState
16
#include "InputData.h" // for InputData, etc
17
#include "Layers.h" // for Layer, etc
18
#include "mozilla/RecursiveMutex.h"
19
#include "mozilla/dom/MouseEventBinding.h" // for MouseEvent constants
20
#include "mozilla/dom/BrowserParent.h" // for AreRecordReplayTabsActive
21
#include "mozilla/dom/Touch.h" // for Touch
22
#include "mozilla/gfx/CompositorHitTestInfo.h"
23
#include "mozilla/gfx/LoggingConstants.h"
24
#include "mozilla/gfx/gfxVars.h" // for gfxVars
25
#include "mozilla/gfx/GPUParent.h" // for GPUParent
26
#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
27
#include "mozilla/gfx/Point.h" // for Point
28
#include "mozilla/layers/APZSampler.h" // for APZSampler
29
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread, etc
30
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
31
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
32
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
33
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
34
#include "mozilla/layers/LayerMetricsWrapper.h"
35
#include "mozilla/layers/MatrixMessage.h"
36
#include "mozilla/layers/WebRenderScrollDataWrapper.h"
37
#include "mozilla/MouseEvents.h"
38
#include "mozilla/mozalloc.h" // for operator new
39
#include "mozilla/Preferences.h" // for Preferences
40
#include "mozilla/StaticPrefs_accessibility.h"
41
#include "mozilla/StaticPrefs_apz.h"
42
#include "mozilla/StaticPrefs_layout.h"
43
#include "mozilla/TouchEvents.h"
44
#include "mozilla/EventStateManager.h" // for WheelPrefs
45
#include "mozilla/webrender/WebRenderAPI.h"
46
#include "nsDebug.h" // for NS_WARNING
47
#include "nsPoint.h" // for nsIntPoint
48
#include "nsThreadUtils.h" // for NS_IsMainThread
49
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
50
#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
51
#include "LayersLogging.h" // for Stringify
52
#include "Units.h" // for ParentlayerPixel
53
#include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
54
#include "UnitTransforms.h" // for ViewAs
55
56
static mozilla::LazyLogModule sApzMgrLog("apz.manager");
57
#define APZCTM_LOG(...) MOZ_LOG(sApzMgrLog, LogLevel::Debug, (__VA_ARGS__))
58
59
static mozilla::LazyLogModule sApzKeyLog("apz.key");
60
#define APZ_KEY_LOG(...) MOZ_LOG(sApzKeyLog, LogLevel::Debug, (__VA_ARGS__))
61
62
namespace mozilla {
63
namespace layers {
64
65
using mozilla::gfx::CompositorHitTestDispatchToContent;
66
using mozilla::gfx::CompositorHitTestFlags;
67
using mozilla::gfx::CompositorHitTestInfo;
68
using mozilla::gfx::CompositorHitTestInvisibleToHit;
69
using mozilla::gfx::LOG_DEFAULT;
70
71
typedef mozilla::gfx::Point Point;
72
typedef mozilla::gfx::Point4D Point4D;
73
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
74
75
typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
76
77
struct APZCTreeManager::TreeBuildingState {
78
TreeBuildingState(LayersId aRootLayersId, bool aIsFirstPaint,
79
LayersId aOriginatingLayersId, APZTestData* aTestData,
80
uint32_t aPaintSequence)
81
: mIsFirstPaint(aIsFirstPaint),
82
mOriginatingLayersId(aOriginatingLayersId),
83
mPaintLogger(aTestData, aPaintSequence) {
84
CompositorBridgeParent::CallWithIndirectShadowTree(
85
aRootLayersId, [this](LayerTreeState& aState) -> void {
86
mCompositorController = aState.GetCompositorController();
87
mInProcessSharingController = aState.InProcessSharingController();
88
});
89
}
90
91
typedef std::unordered_map<AsyncPanZoomController*, gfx::Matrix4x4>
92
DeferredTransformMap;
93
94
// State that doesn't change as we recurse in the tree building
95
RefPtr<CompositorController> mCompositorController;
96
RefPtr<MetricsSharingController> mInProcessSharingController;
97
const bool mIsFirstPaint;
98
const LayersId mOriginatingLayersId;
99
const APZPaintLogHelper mPaintLogger;
100
101
// State that is updated as we perform the tree build
102
103
// A list of nodes that need to be destroyed at the end of the tree building.
104
// This is initialized with all nodes in the old tree, and nodes are removed
105
// from it as we reuse them in the new tree.
106
nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
107
108
// This map is populated as we place APZCs into the new tree. Its purpose is
109
// to facilitate re-using the same APZC for different layers that scroll
110
// together (and thus have the same ScrollableLayerGuid). The presShellId
111
// doesn't matter for this purpose, and we move the map to the APZCTreeManager
112
// after we're done building, so it's useful to have the presshell-ignoring
113
// map for that.
114
std::unordered_map<ScrollableLayerGuid, ApzcMapData,
115
ScrollableLayerGuid::HashIgnoringPresShellFn,
116
ScrollableLayerGuid::EqualIgnoringPresShellFn>
117
mApzcMap;
118
119
// This is populated with all the HitTestingTreeNodes that are scroll thumbs
120
// and have a scrollthumb animation id (which indicates that they need to be
121
// sampled for WebRender on the sampler thread).
122
std::vector<HitTestingTreeNode*> mScrollThumbs;
123
// This is populated with all the scroll target nodes. We use in conjunction
124
// with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo.
125
std::unordered_map<ScrollableLayerGuid, HitTestingTreeNode*,
126
ScrollableLayerGuid::HashIgnoringPresShellFn,
127
ScrollableLayerGuid::EqualIgnoringPresShellFn>
128
mScrollTargets;
129
130
// As the tree is traversed, the top element of this stack tracks whether
131
// the parent scroll node has a perspective transform.
132
std::stack<bool> mParentHasPerspective;
133
134
// During the tree building process, the perspective transform component
135
// of the ancestor transforms of some APZCs can be "deferred" to their
136
// children, meaning they are added to the children's ancestor transforms
137
// instead. Those deferred transforms are tracked here.
138
DeferredTransformMap mPerspectiveTransformsDeferredToChildren;
139
140
// As we recurse down through the tree, this picks up the zoom animation id
141
// from a node in the layer tree, and propagates it downwards to the nearest
142
// APZC instance that is for an RCD node. Generally it will be set on the
143
// root node of the layers (sub-)tree, which may not be same as the RCD node
144
// for the subtree, and so we need this mechanism to ensure it gets propagated
145
// to the RCD's APZC instance. Once it is set on the APZC instance, the value
146
// is cleared back to Nothing(). Note that this is only used in the WebRender
147
// codepath.
148
Maybe<uint64_t> mZoomAnimationId;
149
150
// This is populated with all the HitTestingTreeNodes that have a fixed
151
// position animation id (which indicates that they need to be sampled for
152
// WebRender on the sampler thread).
153
std::vector<HitTestingTreeNode*> mFixedPositionNodesWithAnimationId;
154
155
// This is populated with all the HitTestingTreeNodes that are scrollbar
156
// containers for the root viewport and have a scrollthumb animation id
157
// (which indicates that they need to be sampled for WebRender on the sampler
158
// thread).
159
std::vector<HitTestingTreeNode*> mRootScrollbars;
160
};
161
162
class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
163
public:
164
NS_DECL_ISUPPORTS
165
NS_DECL_NSIOBSERVER
166
167
explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
168
: mTreeManager(aTreeManager) {
169
MOZ_ASSERT(NS_IsMainThread());
170
nsCOMPtr<nsIObserverService> obsSvc =
171
mozilla::services::GetObserverService();
172
MOZ_ASSERT(obsSvc);
173
if (obsSvc) {
174
obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
175
}
176
}
177
178
void Unregister() {
179
MOZ_ASSERT(NS_IsMainThread());
180
nsCOMPtr<nsIObserverService> obsSvc =
181
mozilla::services::GetObserverService();
182
if (obsSvc) {
183
obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
184
}
185
mTreeManager = nullptr;
186
}
187
188
protected:
189
virtual ~CheckerboardFlushObserver() = default;
190
191
private:
192
RefPtr<APZCTreeManager> mTreeManager;
193
};
194
195
NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
196
197
NS_IMETHODIMP
198
APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
199
const char* aTopic,
200
const char16_t*) {
201
MOZ_ASSERT(NS_IsMainThread());
202
MOZ_ASSERT(mTreeManager.get());
203
204
RecursiveMutexAutoLock lock(mTreeManager->mTreeLock);
205
if (mTreeManager->mRootNode) {
206
ForEachNode<ReverseIterator>(
207
mTreeManager->mRootNode.get(), [](HitTestingTreeNode* aNode) {
208
if (aNode->IsPrimaryHolder()) {
209
MOZ_ASSERT(aNode->GetApzc());
210
aNode->GetApzc()->FlushActiveCheckerboardReport();
211
}
212
});
213
}
214
if (XRE_IsGPUProcess()) {
215
if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
216
nsCString topic("APZ:FlushActiveCheckerboard:Done");
217
Unused << gpu->SendNotifyUiObservers(topic);
218
}
219
} else {
220
MOZ_ASSERT(XRE_IsParentProcess());
221
nsCOMPtr<nsIObserverService> obsSvc =
222
mozilla::services::GetObserverService();
223
if (obsSvc) {
224
obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done",
225
nullptr);
226
}
227
}
228
return NS_OK;
229
}
230
231
/**
232
* A RAII class used for setting the focus sequence number on input events
233
* as they are being processed. Any input event is assumed to be potentially
234
* focus changing unless explicitly marked otherwise.
235
*/
236
class MOZ_RAII AutoFocusSequenceNumberSetter {
237
public:
238
AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
239
: mFocusState(aFocusState), mEvent(aEvent), mMayChangeFocus(true) {}
240
241
void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
242
243
~AutoFocusSequenceNumberSetter() {
244
if (mMayChangeFocus) {
245
mFocusState.ReceiveFocusChangingEvent();
246
247
APZ_KEY_LOG(
248
"Marking input with type=%d as focus changing with seq=%" PRIu64 "\n",
249
static_cast<int>(mEvent.mInputType),
250
mFocusState.LastAPZProcessedEvent());
251
} else {
252
APZ_KEY_LOG(
253
"Marking input with type=%d as non focus changing with seq=%" PRIu64
254
"\n",
255
static_cast<int>(mEvent.mInputType),
256
mFocusState.LastAPZProcessedEvent());
257
}
258
259
mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
260
}
261
262
private:
263
FocusState& mFocusState;
264
InputData& mEvent;
265
bool mMayChangeFocus;
266
};
267
268
APZCTreeManager::APZCTreeManager(LayersId aRootLayersId)
269
: mInputQueue(new InputQueue()),
270
mRootLayersId(aRootLayersId),
271
mSampler(nullptr),
272
mUpdater(nullptr),
273
mTreeLock("APZCTreeLock"),
274
mUsingAsyncZoomContainer(false),
275
mMapLock("APZCMapLock"),
276
mRetainedTouchIdentifier(-1),
277
mInScrollbarTouchDrag(false),
278
mApzcTreeLog("apzctree"),
279
mTestDataLock("APZTestDataLock"),
280
mDPI(160.0) {
281
RefPtr<APZCTreeManager> self(this);
282
NS_DispatchToMainThread(NS_NewRunnableFunction(
283
"layers::APZCTreeManager::APZCTreeManager",
284
[self] { self->mFlushObserver = new CheckerboardFlushObserver(self); }));
285
AsyncPanZoomController::InitializeGlobalState();
286
mApzcTreeLog.ConditionOnPrefFunction(StaticPrefs::apz_printtree);
287
#ifdef MOZ_WIDGET_ANDROID
288
if (AndroidDynamicToolbarAnimator::IsEnabled()) {
289
mToolbarAnimator = new AndroidDynamicToolbarAnimator(this);
290
}
291
#endif // (MOZ_WIDGET_ANDROID)
292
}
293
294
APZCTreeManager::~APZCTreeManager() = default;
295
296
void APZCTreeManager::SetSampler(APZSampler* aSampler) {
297
// We're either setting the sampler or clearing it
298
MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
299
mSampler = aSampler;
300
}
301
302
void APZCTreeManager::SetUpdater(APZUpdater* aUpdater) {
303
// We're either setting the updater or clearing it
304
MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
305
mUpdater = aUpdater;
306
}
307
308
void APZCTreeManager::NotifyLayerTreeAdopted(
309
LayersId aLayersId, const RefPtr<APZCTreeManager>& aOldApzcTreeManager) {
310
AssertOnUpdaterThread();
311
312
if (aOldApzcTreeManager) {
313
aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
314
// While we could move the focus target information from the old APZC tree
315
// manager into this one, it's safer to not do that, as we'll probably have
316
// that information repopulated soon anyway (on the next layers update).
317
}
318
319
UniquePtr<APZTestData> adoptedData;
320
if (aOldApzcTreeManager) {
321
MutexAutoLock lock(aOldApzcTreeManager->mTestDataLock);
322
auto it = aOldApzcTreeManager->mTestData.find(aLayersId);
323
if (it != aOldApzcTreeManager->mTestData.end()) {
324
adoptedData = std::move(it->second);
325
aOldApzcTreeManager->mTestData.erase(it);
326
}
327
}
328
if (adoptedData) {
329
MutexAutoLock lock(mTestDataLock);
330
mTestData[aLayersId] = std::move(adoptedData);
331
}
332
}
333
334
void APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId) {
335
AssertOnUpdaterThread();
336
337
mFocusState.RemoveFocusTarget(aLayersId);
338
339
{ // scope lock
340
MutexAutoLock lock(mTestDataLock);
341
mTestData.erase(aLayersId);
342
}
343
}
344
345
AsyncPanZoomController* APZCTreeManager::NewAPZCInstance(
346
LayersId aLayersId, GeckoContentController* aController) {
347
return new AsyncPanZoomController(
348
aLayersId, this, mInputQueue, aController,
349
AsyncPanZoomController::USE_GESTURE_DETECTOR);
350
}
351
352
void APZCTreeManager::SetTestSampleTime(const Maybe<TimeStamp>& aTime) {
353
mTestSampleTime = aTime;
354
}
355
356
TimeStamp APZCTreeManager::GetFrameTime() {
357
if (mTestSampleTime) {
358
return *mTestSampleTime;
359
}
360
return TimeStamp::Now();
361
}
362
363
void APZCTreeManager::SetAllowedTouchBehavior(
364
uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
365
APZThreadUtils::AssertOnControllerThread();
366
367
mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
368
}
369
370
template <class ScrollNode>
371
void // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
372
APZCTreeManager::UpdateHitTestingTreeImpl(const ScrollNode& aRoot,
373
bool aIsFirstPaint,
374
LayersId aOriginatingLayersId,
375
uint32_t aPaintSequenceNumber) {
376
RecursiveMutexAutoLock lock(mTreeLock);
377
378
// For testing purposes, we log some data to the APZTestData associated with
379
// the layers id that originated this update.
380
APZTestData* testData = nullptr;
381
if (StaticPrefs::apz_test_logging_enabled()) {
382
MutexAutoLock lock(mTestDataLock);
383
UniquePtr<APZTestData> ptr = MakeUnique<APZTestData>();
384
auto result =
385
mTestData.insert(std::make_pair(aOriginatingLayersId, std::move(ptr)));
386
testData = result.first->second.get();
387
testData->StartNewPaint(aPaintSequenceNumber);
388
}
389
390
TreeBuildingState state(mRootLayersId, aIsFirstPaint, aOriginatingLayersId,
391
testData, aPaintSequenceNumber);
392
393
// We do this business with collecting the entire tree into an array because
394
// otherwise it's very hard to determine which APZC instances need to be
395
// destroyed. In the worst case, there are two scenarios: (a) a layer with an
396
// APZC is removed from the layer tree and (b) a layer with an APZC is moved
397
// in the layer tree from one place to a completely different place. In
398
// scenario (a) we would want to destroy the APZC while walking the layer tree
399
// and noticing that the layer/APZC is no longer there. But if we do that then
400
// we run into a problem in scenario (b) because we might encounter that layer
401
// later during the walk. To handle both of these we have to 'remember' that
402
// the layer was not found, and then do the destroy only at the end of the
403
// tree walk after we are sure that the layer was removed and not just
404
// transplanted elsewhere. Doing that as part of a recursive tree walk is hard
405
// and so maintaining a list and removing APZCs that are still alive is much
406
// simpler.
407
ForEachNode<ReverseIterator>(mRootNode.get(),
408
[&state](HitTestingTreeNode* aNode) {
409
state.mNodesToDestroy.AppendElement(aNode);
410
});
411
mRootNode = nullptr;
412
mUsingAsyncZoomContainer = false;
413
int asyncZoomContainerNestingDepth = 0;
414
bool haveMultipleAsyncZoomContainers = false;
415
bool haveRootContentOutsideAsyncZoomContainer = false;
416
417
if (aRoot) {
418
std::unordered_set<LayersId, LayersId::HashFn> seenLayersIds;
419
std::stack<gfx::TreeAutoIndent<LOG_DEFAULT>> indents;
420
std::stack<AncestorTransform> ancestorTransforms;
421
HitTestingTreeNode* parent = nullptr;
422
HitTestingTreeNode* next = nullptr;
423
LayersId layersId = mRootLayersId;
424
seenLayersIds.insert(mRootLayersId);
425
ancestorTransforms.push(AncestorTransform());
426
state.mParentHasPerspective.push(false);
427
428
mApzcTreeLog << "[start]\n";
429
mTreeLock.AssertCurrentThreadIn();
430
431
ForEachNode<ReverseIterator>(
432
aRoot,
433
[&](ScrollNode aLayerMetrics) {
434
mApzcTreeLog << aLayerMetrics.Name() << '\t';
435
436
if (aLayerMetrics.IsAsyncZoomContainer()) {
437
if (mUsingAsyncZoomContainer) {
438
haveMultipleAsyncZoomContainers = true;
439
}
440
mUsingAsyncZoomContainer = true;
441
++asyncZoomContainerNestingDepth;
442
}
443
444
if (aLayerMetrics.Metrics().IsRootContent()) {
445
MutexAutoLock lock(mMapLock);
446
mGeckoFixedLayerMargins =
447
aLayerMetrics.Metrics().GetFixedLayerMargins();
448
} else {
449
MOZ_ASSERT(aLayerMetrics.Metrics().GetFixedLayerMargins() ==
450
ScreenMargin(),
451
"fixed-layer-margins should be 0 on non-root layer");
452
}
453
454
// Note that this check happens after the potential increment of
455
// asyncZoomContainerNestingDepth, to allow the root content
456
// metadata to be on the same node as the async zoom container.
457
if (aLayerMetrics.Metrics().IsRootContent() &&
458
asyncZoomContainerNestingDepth == 0) {
459
haveRootContentOutsideAsyncZoomContainer = true;
460
}
461
462
HitTestingTreeNode* node = PrepareNodeForLayer(
463
lock, aLayerMetrics, aLayerMetrics.Metrics(), layersId,
464
ancestorTransforms.top(), parent, next, state);
465
MOZ_ASSERT(node);
466
AsyncPanZoomController* apzc = node->GetApzc();
467
aLayerMetrics.SetApzc(apzc);
468
469
// GetScrollbarAnimationId is only set when webrender is enabled,
470
// which limits the extra thumb mapping work to the webrender-enabled
471
// case where it is needed.
472
// Note also that when webrender is enabled, a "valid" animation id
473
// is always nonzero, so we don't need to worry about handling the
474
// case where WR is enabled and the animation id is zero.
475
if (node->GetScrollbarAnimationId()) {
476
if (node->IsScrollThumbNode()) {
477
state.mScrollThumbs.push_back(node);
478
} else if (node->IsScrollbarContainerNode()) {
479
// Only scrollbar containers for the root have an animation id.
480
state.mRootScrollbars.push_back(node);
481
}
482
}
483
484
// GetFixedPositionAnimationId is only set when webrender is enabled.
485
if (node->GetFixedPositionAnimationId().isSome()) {
486
state.mFixedPositionNodesWithAnimationId.push_back(node);
487
}
488
if (apzc && node->IsPrimaryHolder()) {
489
state.mScrollTargets[apzc->GetGuid()] = node;
490
}
491
492
mApzcTreeLog << '\n';
493
494
// Accumulate the CSS transform between layers that have an APZC.
495
// In the terminology of the big comment above
496
// APZCTreeManager::GetScreenToApzcTransform, if we are at layer M,
497
// then aAncestorTransform is NC * OC * PC, and we left-multiply MC
498
// and compute ancestorTransform to be MC * NC * OC * PC. This gets
499
// passed down as the ancestor transform to layer L when we recurse
500
// into the children below. If we are at a layer with an APZC, such as
501
// P, then we reset the ancestorTransform to just PC, to start the new
502
// accumulation as we go down.
503
AncestorTransform currentTransform{
504
aLayerMetrics.GetTransform(),
505
aLayerMetrics.TransformIsPerspective()};
506
if (!apzc) {
507
currentTransform = currentTransform * ancestorTransforms.top();
508
}
509
ancestorTransforms.push(currentTransform);
510
511
// Note that |node| at this point will not have any children,
512
// otherwise we we would have to set next to node->GetFirstChild().
513
MOZ_ASSERT(!node->GetFirstChild());
514
parent = node;
515
next = nullptr;
516
517
// Update the layersId or renderroot if we have a new one
518
if (Maybe<LayersId> newLayersId = aLayerMetrics.GetReferentId()) {
519
layersId = *newLayersId;
520
seenLayersIds.insert(layersId);
521
}
522
523
indents.push(gfx::TreeAutoIndent<LOG_DEFAULT>(mApzcTreeLog));
524
state.mParentHasPerspective.push(
525
aLayerMetrics.TransformIsPerspective());
526
},
527
[&](ScrollNode aLayerMetrics) {
528
if (aLayerMetrics.IsAsyncZoomContainer()) {
529
--asyncZoomContainerNestingDepth;
530
}
531
532
next = parent;
533
parent = parent->GetParent();
534
layersId = next->GetLayersId();
535
ancestorTransforms.pop();
536
indents.pop();
537
state.mParentHasPerspective.pop();
538
});
539
540
mApzcTreeLog << "[end]\n";
541
542
MOZ_ASSERT(
543
!mUsingAsyncZoomContainer || !haveRootContentOutsideAsyncZoomContainer,
544
"If there is an async zoom container, all scroll nodes with root "
545
"content scroll metadata should be inside it");
546
// TODO(bug 1534459): Avoid multiple async zoom containers. They
547
// can't currently occur in production code, but that will become
548
// possible with either OOP iframes or desktop zooming (due to
549
// RDM), and will need to be guarded against.
550
// MOZ_ASSERT(!haveMultipleAsyncZoomContainers,
551
// "Should only have one async zoom container");
552
553
// If we have perspective transforms deferred to children, do another
554
// walk of the tree and actually apply them to the children.
555
// We can't do this "as we go" in the previous traversal, because by the
556
// time we realize we need to defer a perspective transform for an APZC,
557
// we may already have processed a previous layer (including children
558
// found in its subtree) that shares that APZC.
559
if (!state.mPerspectiveTransformsDeferredToChildren.empty()) {
560
ForEachNode<ReverseIterator>(
561
mRootNode.get(), [&state](HitTestingTreeNode* aNode) {
562
AsyncPanZoomController* apzc = aNode->GetApzc();
563
if (!apzc) {
564
return;
565
}
566
if (!aNode->IsPrimaryHolder()) {
567
return;
568
}
569
570
AsyncPanZoomController* parent = apzc->GetParent();
571
if (!parent) {
572
return;
573
}
574
575
auto it =
576
state.mPerspectiveTransformsDeferredToChildren.find(parent);
577
if (it != state.mPerspectiveTransformsDeferredToChildren.end()) {
578
apzc->SetAncestorTransform(AncestorTransform{
579
it->second * apzc->GetAncestorTransform(), false});
580
}
581
});
582
}
583
584
// Remove any layers ids for which we no longer have content from
585
// mDetachedLayersIds.
586
for (auto iter = mDetachedLayersIds.begin();
587
iter != mDetachedLayersIds.end();) {
588
// unordered_set::erase() invalidates the iterator pointing to the
589
// element being erased, but returns an iterator to the next element.
590
if (seenLayersIds.find(*iter) == seenLayersIds.end()) {
591
iter = mDetachedLayersIds.erase(iter);
592
} else {
593
++iter;
594
}
595
}
596
}
597
598
// We do not support tree structures where the root node has siblings.
599
MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
600
601
{ // scope lock and update our mApzcMap before we destroy all the unused
602
// APZC instances
603
MutexAutoLock lock(mMapLock);
604
mApzcMap = std::move(state.mApzcMap);
605
606
for (auto& mapping : mApzcMap) {
607
AsyncPanZoomController* parent = mapping.second.apzc->GetParent();
608
mapping.second.parent = parent ? Some(parent->GetGuid()) : Nothing();
609
}
610
611
mScrollThumbInfo.clear();
612
// For non-webrender, state.mScrollThumbs will be empty so this will be a
613
// no-op.
614
for (HitTestingTreeNode* thumb : state.mScrollThumbs) {
615
MOZ_ASSERT(thumb->IsScrollThumbNode());
616
ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0,
617
thumb->GetScrollTargetId());
618
auto it = state.mScrollTargets.find(targetGuid);
619
if (it == state.mScrollTargets.end()) {
620
// It could be that |thumb| is a scrollthumb for content which didn't
621
// have an APZC, for example if the content isn't layerized. Regardless,
622
// we can't async-scroll it so we don't need to worry about putting it
623
// in mScrollThumbInfo.
624
continue;
625
}
626
HitTestingTreeNode* target = it->second;
627
mScrollThumbInfo.emplace_back(
628
*(thumb->GetScrollbarAnimationId()), thumb->GetTransform(),
629
thumb->GetScrollbarData(), targetGuid, target->GetTransform(),
630
target->IsAncestorOf(thumb));
631
}
632
633
mRootScrollbarInfo.clear();
634
// For non-webrender, and platforms without a dynamic toolbar,
635
// state.mRootScrollbarsWithAnimationId will be empty so this will be a
636
// no-op.
637
for (const HitTestingTreeNode* scrollbar : state.mRootScrollbars) {
638
MOZ_ASSERT(scrollbar->IsScrollbarContainerNode());
639
mRootScrollbarInfo.emplace_back(*(scrollbar->GetScrollbarAnimationId()),
640
scrollbar->GetScrollbarDirection());
641
}
642
643
mFixedPositionInfo.clear();
644
// For non-webrender, state.mFixedPositionNodesWithAnimationId will be empty
645
// so this will be a no-op.
646
for (HitTestingTreeNode* fixedPos :
647
state.mFixedPositionNodesWithAnimationId) {
648
MOZ_ASSERT(fixedPos->GetFixedPositionAnimationId().isSome());
649
mFixedPositionInfo.emplace_back(
650
fixedPos->GetFixedPositionAnimationId().value(),
651
fixedPos->GetFixedPosSides());
652
}
653
}
654
655
for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
656
APZCTM_LOG("Destroying node at %p with APZC %p\n",
657
state.mNodesToDestroy[i].get(),
658
state.mNodesToDestroy[i]->GetApzc());
659
state.mNodesToDestroy[i]->Destroy();
660
}
661
662
APZCTM_LOG("APZCTreeManager (%p)\n", this);
663
if (mRootNode && MOZ_LOG_TEST(sApzMgrLog, LogLevel::Debug)) {
664
mRootNode->Dump(" ");
665
}
666
SendSubtreeTransformsToChromeMainThread(nullptr);
667
}
668
669
void APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
670
LayersId aOriginatingLayersId,
671
const FocusTarget& aFocusTarget) {
672
AssertOnUpdaterThread();
673
674
if (!StaticPrefs::apz_keyboard_enabled_AtStartup()) {
675
return;
676
}
677
678
mFocusState.Update(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget);
679
}
680
681
void APZCTreeManager::UpdateHitTestingTree(Layer* aRoot, bool aIsFirstPaint,
682
LayersId aOriginatingLayersId,
683
uint32_t aPaintSequenceNumber) {
684
AssertOnUpdaterThread();
685
686
LayerMetricsWrapper root(aRoot);
687
UpdateHitTestingTreeImpl(root, aIsFirstPaint, aOriginatingLayersId,
688
aPaintSequenceNumber);
689
}
690
691
void APZCTreeManager::UpdateHitTestingTree(
692
const WebRenderScrollDataWrapper& aScrollWrapper, bool aIsFirstPaint,
693
LayersId aOriginatingLayersId, uint32_t aPaintSequenceNumber) {
694
AssertOnUpdaterThread();
695
696
UpdateHitTestingTreeImpl(aScrollWrapper, aIsFirstPaint, aOriginatingLayersId,
697
aPaintSequenceNumber);
698
}
699
700
void APZCTreeManager::SampleForWebRender(
701
wr::TransactionWrapper& aTxn, const TimeStamp& aSampleTime,
702
wr::RenderRoot aRenderRoot,
703
const wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
704
AssertOnSamplerThread();
705
MutexAutoLock lock(mMapLock);
706
707
nsTArray<wr::WrTransformProperty> transforms;
708
709
// Sample async transforms on scrollable layers.
710
for (const auto& mapping : mApzcMap) {
711
AsyncPanZoomController* apzc = mapping.second.apzc;
712
if (apzc->GetRenderRoot() != aRenderRoot) {
713
// If this APZC belongs to a different render root, skip over it
714
continue;
715
}
716
717
const AsyncTransformComponents asyncTransformComponents =
718
apzc->GetZoomAnimationId()
719
? AsyncTransformComponents{AsyncTransformComponent::eLayout}
720
: LayoutAndVisual;
721
ParentLayerPoint layerTranslation =
722
apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing,
723
asyncTransformComponents)
724
.mTranslation;
725
726
if (Maybe<CompositionPayload> payload = apzc->NotifyScrollSampling()) {
727
RefPtr<WebRenderBridgeParent> wrBridgeParent;
728
LayersId layersId = apzc->GetGuid().mLayersId;
729
CompositorBridgeParent::CallWithIndirectShadowTree(
730
layersId, [&](LayerTreeState& aState) -> void {
731
wrBridgeParent = aState.mWrBridge;
732
});
733
734
if (wrBridgeParent) {
735
wr::PipelineId pipelineId = wr::AsPipelineId(layersId);
736
for (size_t i = 0; i < aEpochsBeingRendered->Length(); i++) {
737
if ((*aEpochsBeingRendered)[i].pipeline_id == pipelineId) {
738
auto& epoch = (*aEpochsBeingRendered)[i].epoch;
739
wrBridgeParent->AddPendingScrollPayload(
740
*payload, std::make_pair(pipelineId, epoch));
741
break;
742
}
743
}
744
}
745
}
746
747
if (Maybe<uint64_t> zoomAnimationId = apzc->GetZoomAnimationId()) {
748
// for now we only support zooming on root content APZCs
749
MOZ_ASSERT(apzc->IsRootContent());
750
751
LayoutDeviceToParentLayerScale zoom = apzc->GetCurrentPinchZoomScale(
752
AsyncPanZoomController::eForCompositing);
753
754
AsyncTransform asyncVisualTransform = apzc->GetCurrentAsyncTransform(
755
AsyncPanZoomController::eForCompositing,
756
AsyncTransformComponents{AsyncTransformComponent::eVisual});
757
758
transforms.AppendElement(wr::ToWrTransformProperty(
759
*zoomAnimationId, LayoutDeviceToParentLayerMatrix4x4::Scaling(
760
zoom.scale, zoom.scale, 1.0f) *
761
AsyncTransformComponentMatrix::Translation(
762
asyncVisualTransform.mTranslation)));
763
764
aTxn.UpdateIsTransformAsyncZooming(*zoomAnimationId,
765
apzc->IsAsyncZooming());
766
}
767
768
// If layerTranslation includes only the layout component of the async
769
// transform then it has not been scaled by the async zoom, so we want to
770
// divide it by the resolution. If layerTranslation includes the visual
771
// component, then we should use the pinch zoom scale, which includes the
772
// async zoom. However, we only use LayoutAndVisual for non-zoomable APZCs,
773
// so it makes no difference.
774
LayoutDeviceToParentLayerScale resolution =
775
apzc->GetCumulativeResolution().ToScaleFactor() *
776
LayerToParentLayerScale(1.0f);
777
// The positive translation means the painted content is supposed to
778
// move down (or to the right), and that corresponds to a reduction in
779
// the scroll offset. Since we are effectively giving WR the async
780
// scroll delta here, we want to negate the translation.
781
LayoutDevicePoint asyncScrollDelta = -layerTranslation / resolution;
782
aTxn.UpdateScrollPosition(wr::AsPipelineId(apzc->GetGuid().mLayersId),
783
apzc->GetGuid().mScrollId,
784
wr::ToLayoutPoint(asyncScrollDelta));
785
786
#if defined(MOZ_WIDGET_ANDROID)
787
// Send the root frame metrics to java through the UIController
788
RefPtr<UiCompositorControllerParent> uiController =
789
UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayersId);
790
if (uiController &&
791
apzc->UpdateRootFrameMetricsIfChanged(mLastRootMetrics)) {
792
uiController->NotifyUpdateScreenMetrics(mLastRootMetrics);
793
}
794
#endif
795
}
796
797
// Now collect all the async transforms needed for the scrollthumbs.
798
for (const ScrollThumbInfo& info : mScrollThumbInfo) {
799
auto it = mApzcMap.find(info.mTargetGuid);
800
if (it == mApzcMap.end()) {
801
// It could be that |info| is a scrollthumb for content which didn't
802
// have an APZC, for example if the content isn't layerized. Regardless,
803
// we can't async-scroll it so we don't need to worry about putting it
804
// in mScrollThumbInfo.
805
continue;
806
}
807
AsyncPanZoomController* scrollTargetApzc = it->second.apzc;
808
MOZ_ASSERT(scrollTargetApzc);
809
if (scrollTargetApzc->GetRenderRoot() != aRenderRoot) {
810
// If this APZC belongs to a different render root, skip over it
811
continue;
812
}
813
LayerToParentLayerMatrix4x4 transform =
814
scrollTargetApzc->CallWithLastContentPaintMetrics(
815
[&](const FrameMetrics& aMetrics) {
816
return ComputeTransformForScrollThumb(
817
info.mThumbTransform * AsyncTransformMatrix(),
818
info.mTargetTransform.ToUnknownMatrix(), scrollTargetApzc,
819
aMetrics, info.mThumbData, info.mTargetIsAncestor, nullptr);
820
});
821
transforms.AppendElement(
822
wr::ToWrTransformProperty(info.mThumbAnimationId, transform));
823
}
824
825
// Move the root scrollbar in response to the dynamic toolbar transition.
826
for (const RootScrollbarInfo& info : mRootScrollbarInfo) {
827
// We only care about the horizontal scrollbar.
828
if (info.mScrollDirection == ScrollDirection::eHorizontal) {
829
ScreenPoint translation =
830
AsyncCompositionManager::ComputeFixedMarginsOffset(
831
mCompositorFixedLayerMargins, SideBits::eBottom, ScreenMargin());
832
833
LayerToParentLayerMatrix4x4 transform =
834
LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>(
835
translation, PixelCastJustification::ScreenIsParentLayerForRoot));
836
837
transforms.AppendElement(
838
wr::ToWrTransformProperty(info.mScrollbarAnimationId, transform));
839
}
840
}
841
842
for (const FixedPositionInfo& info : mFixedPositionInfo) {
843
ScreenPoint translation =
844
AsyncCompositionManager::ComputeFixedMarginsOffset(
845
mCompositorFixedLayerMargins, info.mFixedPosSides,
846
mGeckoFixedLayerMargins);
847
848
LayerToParentLayerMatrix4x4 transform =
849
LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>(
850
translation, PixelCastJustification::ScreenIsParentLayerForRoot));
851
852
transforms.AppendElement(
853
wr::ToWrTransformProperty(info.mFixedPositionAnimationId, transform));
854
}
855
856
aTxn.AppendTransformProperties(transforms);
857
858
// Advance animations. It's important that this happens after
859
// sampling all async transforms, because AdvanceAnimations() updates
860
// the effective scroll offset to the value it should have for the *next*
861
// composite after this one (if the APZ frame delay is enabled).
862
bool activeAnimations =
863
AdvanceAnimationsInternal(lock, Some(aRenderRoot), aSampleTime);
864
if (activeAnimations) {
865
RefPtr<CompositorController> controller;
866
CompositorBridgeParent::CallWithIndirectShadowTree(
867
mRootLayersId, [&](LayerTreeState& aState) -> void {
868
controller = aState.GetCompositorController();
869
});
870
if (controller) {
871
controller->ScheduleRenderOnCompositorThread(
872
wr::RenderRootSet(aRenderRoot));
873
}
874
}
875
}
876
877
bool APZCTreeManager::AdvanceAnimations(Maybe<wr::RenderRoot> aRenderRoot,
878
const TimeStamp& aSampleTime) {
879
MutexAutoLock lock(mMapLock);
880
return AdvanceAnimationsInternal(lock, std::move(aRenderRoot), aSampleTime);
881
}
882
883
ParentLayerRect APZCTreeManager::ComputeClippedCompositionBounds(
884
const MutexAutoLock& aProofOfMapLock, ClippedCompositionBoundsMap& aDestMap,
885
ScrollableLayerGuid aGuid) {
886
auto insertResult = aDestMap.insert(std::make_pair(aGuid, ParentLayerRect()));
887
if (!insertResult.second) {
888
// We already computed it for this one, early-exit. This might happen
889
// because on a later iteration of mApzcMap we might encounter an ancestor
890
// of an APZC that we processed on an earlier iteration. In this case we
891
// would have computed the ancestor's clipped composition bounds when
892
// recursing up on the earlier iteration.
893
return insertResult.first->second;
894
}
895
896
ParentLayerRect bounds = mApzcMap[aGuid].apzc->GetCompositionBounds();
897
const auto& mapEntry = mApzcMap.find(aGuid);
898
MOZ_ASSERT(mapEntry != mApzcMap.end());
899
if (mapEntry->second.parent.isNothing()) {
900
// Recursion base case, where the APZC with guid `aGuid` has no parent.
901
// In this case, we don't need to clip `bounds` any further and can just
902
// early exit.
903
insertResult.first->second = bounds;
904
return bounds;
905
}
906
907
ScrollableLayerGuid parentGuid = mapEntry->second.parent.value();
908
auto parentBoundsEntry = aDestMap.find(parentGuid);
909
// If aDestMap doesn't contain the parent entry yet, we recurse to compute
910
// that one first.
911
ParentLayerRect parentClippedBounds =
912
(parentBoundsEntry == aDestMap.end())
913
? ComputeClippedCompositionBounds(aProofOfMapLock, aDestMap,
914
parentGuid)
915
: parentBoundsEntry->second;
916
917
// The parent layer's async transform applies to the current layer to take
918
// `bounds` into the same coordinate space as `parentClippedBounds`. However,
919
// we're going to do the inverse operation and unapply this transform to
920
// `parentClippedBounds` to bring it into the same coordinate space as
921
// `bounds`.
922
AsyncTransform appliesToLayer =
923
mApzcMap[parentGuid].apzc->GetCurrentAsyncTransform(
924
AsyncPanZoomController::eForCompositing);
925
926
// Do the unapplication
927
LayerRect parentClippedBoundsInParentLayerSpace =
928
(parentClippedBounds - appliesToLayer.mTranslation) /
929
appliesToLayer.mScale;
930
931
// And then clip `bounds` by the parent's comp bounds in the current space.
932
bounds = bounds.Intersect(
933
ViewAs<ParentLayerPixel>(parentClippedBoundsInParentLayerSpace,
934
PixelCastJustification::MovingDownToChildren));
935
936
// Done!
937
insertResult.first->second = bounds;
938
return bounds;
939
}
940
941
bool APZCTreeManager::AdvanceAnimationsInternal(
942
const MutexAutoLock& aProofOfMapLock, Maybe<wr::RenderRoot> aRenderRoot,
943
const TimeStamp& aSampleTime) {
944
ClippedCompositionBoundsMap clippedCompBounds;
945
bool activeAnimations = false;
946
for (const auto& mapping : mApzcMap) {
947
AsyncPanZoomController* apzc = mapping.second.apzc;
948
if (aRenderRoot && apzc->GetRenderRoot() != *aRenderRoot) {
949
// If this APZC belongs to a different render root, skip over it
950
continue;
951
}
952
953
// Note that this call is recursive, but it early-exits if called again
954
// with the same guid. So this loop is still amortized O(n) with respect to
955
// the number of APZCs.
956
ParentLayerRect clippedBounds = ComputeClippedCompositionBounds(
957
aProofOfMapLock, clippedCompBounds, mapping.first);
958
959
apzc->ReportCheckerboard(aSampleTime, clippedBounds);
960
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
961
}
962
return activeAnimations;
963
}
964
965
// Compute the clip region to be used for a layer with an APZC. This function
966
// is only called for layers which actually have scrollable metrics and an APZC.
967
template <class ScrollNode>
968
Maybe<ParentLayerIntRegion> APZCTreeManager::ComputeClipRegion(
969
const ScrollNode& aLayer) {
970
Maybe<ParentLayerIntRegion> clipRegion;
971
if (aLayer.GetClipRect()) {
972
clipRegion.emplace(*aLayer.GetClipRect());
973
} else if (aLayer.Metrics().IsRootContent() && mUsingAsyncZoomContainer) {
974
// If we are using containerless scrolling, part of the root content
975
// layers' async transform has been lifted to the async zoom container
976
// layer. The composition bounds clip, which applies after the async
977
// transform, needs to be lifted too. Layout code already takes care of
978
// this for us, we just need to not mess it up by introducing a
979
// composition bounds clip here, so we leave the clip empty.
980
} else {
981
// if there is no clip on this layer (which should only happen for the
982
// root scrollable layer in a process, or for some of the LayerMetrics
983
// expansions of a multi-metrics layer), fall back to using the comp
984
// bounds which should be equivalent.
985
clipRegion.emplace(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
986
}
987
988
return clipRegion;
989
}
990
991
template <class ScrollNode>
992
void APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
993
const AsyncPanZoomController* apzc) {
994
const FrameMetrics& metrics = aLayer.Metrics();
995
mApzcTreeLog << "APZC " << apzc->GetGuid()
996
<< "\tcb=" << metrics.GetCompositionBounds()
997
<< "\tsr=" << metrics.GetScrollableRect()
998
<< (metrics.IsScrollInfoLayer() ? "\tscrollinfo" : "")
999
<< (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
1000
<< aLayer.Metadata().GetContentDescription().get();
1001
}
1002
1003
void APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
1004
HitTestingTreeNode* aParent,
1005
HitTestingTreeNode* aNextSibling) {
1006
if (aNextSibling) {
1007
aNextSibling->SetPrevSibling(aNode);
1008
} else if (aParent) {
1009
aParent->SetLastChild(aNode);
1010
} else {
1011
MOZ_ASSERT(!mRootNode);
1012
mRootNode = aNode;
1013
aNode->MakeRoot();
1014
}
1015
}
1016
1017
template <class ScrollNode>
1018
static EventRegions GetEventRegions(const ScrollNode& aLayer) {
1019
if (aLayer.Metrics().IsScrollInfoLayer()) {
1020
ParentLayerIntRect compositionBounds(
1021
RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
1022
nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
1023
EventRegions eventRegions(hitRegion);
1024
eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
1025
return eventRegions;
1026
}
1027
return aLayer.GetEventRegions();
1028
}
1029
1030
already_AddRefed<HitTestingTreeNode> APZCTreeManager::RecycleOrCreateNode(
1031
const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState,
1032
AsyncPanZoomController* aApzc, LayersId aLayersId) {
1033
// Find a node without an APZC and return it. Note that unless the layer tree
1034
// actually changes, this loop should generally do an early-return on the
1035
// first iteration, so it should be cheap in the common case.
1036
for (int32_t i = aState.mNodesToDestroy.Length() - 1; i >= 0; i--) {
1037
RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
1038
if (node->IsRecyclable(aProofOfTreeLock)) {
1039
aState.mNodesToDestroy.RemoveElementAt(i);
1040
node->RecycleWith(aProofOfTreeLock, aApzc, aLayersId);
1041
return node.forget();
1042
}
1043
}
1044
RefPtr<HitTestingTreeNode> node =
1045
new HitTestingTreeNode(aApzc, false, aLayersId);
1046
return node.forget();
1047
}
1048
1049
template <class ScrollNode>
1050
static EventRegionsOverride GetEventRegionsOverride(HitTestingTreeNode* aParent,
1051
const ScrollNode& aLayer) {
1052
// Make it so that if the flag is set on the layer tree, it automatically
1053
// propagates to all the nodes in the corresponding subtree rooted at that
1054
// layer in the hit-test tree. This saves having to walk up the tree every
1055
// we want to see if a hit-test node is affected by this flag.
1056
EventRegionsOverride result = aLayer.GetEventRegionsOverride();
1057
if (result != EventRegionsOverride::NoOverride) {
1058
// Overrides should only ever get set for ref layers.
1059
MOZ_ASSERT(aLayer.GetReferentId());
1060
}
1061
if (aParent) {
1062
result |= aParent->GetEventRegionsOverride();
1063
}
1064
return result;
1065
}
1066
1067
void APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
1068
const AsyncDragMetrics& aDragMetrics) {
1069
APZThreadUtils::AssertOnControllerThread();
1070
1071
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1072
if (!apzc) {
1073
NotifyScrollbarDragRejected(aGuid);
1074
return;
1075
}
1076
1077
uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
1078
mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
1079
}
1080
1081
bool APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid,
1082
const ScreenPoint& aAnchorLocation) {
1083
APZThreadUtils::AssertOnControllerThread();
1084
1085
RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1086
if (!apzc) {
1087
if (XRE_IsGPUProcess()) {
1088
// If we're in the compositor process, the "return false" will be
1089
// ignored because the query comes over the PAPZCTreeManager protocol
1090
// via an async message. In this case, send an explicit rejection
1091
// message to content.
1092
NotifyAutoscrollRejected(aGuid);
1093
}
1094
return false;
1095
}
1096
1097
apzc->StartAutoscroll(aAnchorLocation);
1098
return true;
1099
}
1100
1101
void APZCTreeManager::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
1102
APZThreadUtils::AssertOnControllerThread();
1103
1104
if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
1105
apzc->StopAutoscroll();
1106
}
1107
}
1108
1109
void APZCTreeManager::NotifyScrollbarDragInitiated(
1110
uint64_t aDragBlockId, const ScrollableLayerGuid& aGuid,
1111
ScrollDirection aDirection) const {
1112
RefPtr<GeckoContentController> controller =
1113
GetContentController(aGuid.mLayersId);
1114
if (controller) {
1115
controller->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aGuid.mScrollId,
1116
aDirection);
1117
}
1118
}
1119
1120
void APZCTreeManager::NotifyScrollbarDragRejected(
1121
const ScrollableLayerGuid& aGuid) const {
1122
RefPtr<GeckoContentController> controller =
1123
GetContentController(aGuid.mLayersId);
1124
if (controller) {
1125
controller->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
1126
}
1127
}
1128
1129
void APZCTreeManager::NotifyAutoscrollRejected(
1130
const ScrollableLayerGuid& aGuid) const {
1131
RefPtr<GeckoContentController> controller =
1132
GetContentController(aGuid.mLayersId);
1133
MOZ_ASSERT(controller);
1134
controller->NotifyAsyncAutoscrollRejected(aGuid.mScrollId);
1135
}
1136
1137
template <class ScrollNode>
1138
void SetHitTestData(HitTestingTreeNode* aNode, HitTestingTreeNode* aParent,
1139
const ScrollNode& aLayer,
1140
const Maybe<ParentLayerIntRegion>& aClipRegion) {
1141
aNode->SetHitTestData(
1142
GetEventRegions(aLayer), aLayer.GetVisibleRegion(),
1143
aLayer.GetRemoteDocumentSize(), aLayer.GetTransformTyped(), aClipRegion,
1144
GetEventRegionsOverride(aParent, aLayer), aLayer.IsBackfaceHidden(),
1145
!!aLayer.IsAsyncZoomContainer());
1146
}
1147
1148
template <class ScrollNode>
1149
HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(
1150
const RecursiveMutexAutoLock& aProofOfTreeLock, const ScrollNode& aLayer,
1151
const FrameMetrics& aMetrics, LayersId aLayersId,
1152
const AncestorTransform& aAncestorTransform, HitTestingTreeNode* aParent,
1153
HitTestingTreeNode* aNextSibling, TreeBuildingState& aState) {
1154
bool needsApzc = true;
1155
if (!aMetrics.IsScrollable()) {
1156
needsApzc = false;
1157
}
1158
1159
// XXX: As a future optimization we can probably stick these things on the
1160
// TreeBuildingState, and update them as we change layers id during the
1161
// traversal
1162
RefPtr<GeckoContentController> geckoContentController;
1163
RefPtr<MetricsSharingController> crossProcessSharingController;
1164
CompositorBridgeParent::CallWithIndirectShadowTree(
1165
aLayersId, [&](LayerTreeState& lts) -> void {
1166
geckoContentController = lts.mController;
1167
crossProcessSharingController = lts.CrossProcessSharingController();
1168
});
1169
1170
if (!geckoContentController) {
1171
needsApzc = false;
1172
}
1173
1174
bool parentHasPerspective = aState.mParentHasPerspective.top();
1175
1176
if (Maybe<uint64_t> zoomAnimationId = aLayer.GetZoomAnimationId()) {
1177
aState.mZoomAnimationId = zoomAnimationId;
1178
}
1179
1180
RefPtr<HitTestingTreeNode> node = nullptr;
1181
if (!needsApzc) {
1182
// Note: if layer properties must be propagated to nodes, RecvUpdate in
1183
// LayerTransactionParent.cpp must ensure that APZ will be notified
1184
// when those properties change.
1185
node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId);
1186
AttachNodeToTree(node, aParent, aNextSibling);
1187
SetHitTestData(node, aParent, aLayer,
1188
(!parentHasPerspective && aLayer.GetClipRect())
1189
? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
1190
: Nothing());
1191
node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
1192
aLayer.GetScrollbarData());
1193
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(),
1194
aLayer.GetFixedPositionSides(),
1195
aLayer.GetFixedPositionAnimationId());
1196
if (aLayer.GetIsStickyPosition()) {
1197
node->SetStickyPosData(aLayer.GetStickyScrollContainerId(),
1198
aLayer.GetStickyScrollRangeOuter(),
1199
aLayer.GetStickyScrollRangeInner());
1200
} else {
1201
node->SetStickyPosData(ScrollableLayerGuid::NULL_SCROLL_ID,
1202
LayerRectAbsolute(), LayerRectAbsolute());
1203
}
1204
return node;
1205
}
1206
1207
AsyncPanZoomController* apzc = nullptr;
1208
// If we get here, aLayer is a scrollable layer and somebody
1209
// has registered a GeckoContentController for it, so we need to ensure
1210
// it has an APZC instance to manage its scrolling.
1211
1212
// aState.mApzcMap allows reusing the exact same APZC instance for different
1213
// layers with the same FrameMetrics data. This is needed because in some
1214
// cases content that is supposed to scroll together is split into multiple
1215
// layers because of e.g. non-scrolling content interleaved in z-index order.
1216
ScrollableLayerGuid guid(aLayersId, aMetrics.GetPresShellId(),
1217
aMetrics.GetScrollId());
1218
auto insertResult = aState.mApzcMap.insert(std::make_pair(
1219
guid,
1220
ApzcMapData{static_cast<AsyncPanZoomController*>(nullptr), Nothing()}));
1221
if (!insertResult.second) {
1222
apzc = insertResult.first->second.apzc;
1223
PrintAPZCInfo(aLayer, apzc);
1224
}
1225
APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64
1226
"\n",
1227
apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId);
1228
1229
// If we haven't encountered a layer already with the same metrics, then we
1230
// need to do the full reuse-or-make-an-APZC algorithm, which is contained
1231
// inside the block below.
1232
if (apzc == nullptr) {
1233
apzc = aLayer.GetApzc();
1234
1235
// If the content represented by the scrollable layer has changed (which may
1236
// be possible because of DLBI heuristics) then we don't want to keep using
1237
// the same old APZC for the new content. Also, when reparenting a tab into
1238
// a new window a layer might get moved to a different layer tree with a
1239
// different APZCTreeManager. In these cases we don't want to reuse the same
1240
// APZC, so null it out so we run through the code to find another one or
1241
// create one.
1242
if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
1243
apzc = nullptr;
1244
}
1245
1246
// See if we can find an APZC from the previous tree that matches the
1247
// ScrollableLayerGuid from this layer. If there is one, then we know that
1248
// the layout of the page changed causing the layer tree to be rebuilt, but
1249
// the underlying content for the APZC is still there somewhere. Therefore,
1250
// we want to find the APZC instance and continue using it here.
1251
//
1252
// We particularly want to find the primary-holder node from the previous
1253
// tree that matches, because we don't want that node to get destroyed. If
1254
// it does get destroyed, then the APZC will get destroyed along with it by
1255
// definition, but we want to keep that APZC around in the new tree.
1256
// We leave non-primary-holder nodes in the destroy list because we don't
1257
// care about those nodes getting destroyed.
1258
for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
1259
RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
1260
if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
1261
node = n;
1262
if (apzc != nullptr) {
1263
// If there is an APZC already then it should match the one from the
1264
// old primary-holder node
1265
MOZ_ASSERT(apzc == node->GetApzc());
1266
}
1267
apzc = node->GetApzc();
1268
break;
1269
}
1270
}
1271
1272
// The APZC we get off the layer may have been destroyed previously if the
1273
// layer was inactive or omitted from the layer tree for whatever reason
1274
// from a layers update. If it later comes back it will have a reference to
1275
// a destroyed APZC and so we need to throw that out and make a new one.
1276
bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
1277
if (newApzc) {
1278
apzc = NewAPZCInstance(aLayersId, geckoContentController);
1279
apzc->SetCompositorController(aState.mCompositorController.get());
1280
if (crossProcessSharingController) {
1281
apzc->SetMetricsSharingController(crossProcessSharingController);
1282
} else {
1283
apzc->SetMetricsSharingController(
1284
aState.mInProcessSharingController.get());
1285
}
1286
MOZ_ASSERT(node == nullptr);
1287
node = new HitTestingTreeNode(apzc, true, aLayersId);
1288
} else {
1289
// If we are re-using a node for this layer clear the tree pointers
1290
// so that it doesn't continue pointing to nodes that might no longer
1291
// be in the tree. These pointers will get reset properly as we continue
1292
// building the tree. Also remove it from the set of nodes that are going
1293
// to be destroyed, because it's going to remain active.
1294
aState.mNodesToDestroy.RemoveElement(node);
1295
node->SetPrevSibling(nullptr);
1296
node->SetLastChild(nullptr);
1297
}
1298
1299
if (aMetrics.IsRootContent()) {
1300
apzc->SetZoomAnimationId(aState.mZoomAnimationId);
1301
aState.mZoomAnimationId = Nothing();
1302
}
1303
1304
APZCTM_LOG(
1305
"Using APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n",
1306
apzc, aLayer.GetLayer(), uint64_t(aLayersId), aMetrics.GetScrollId());
1307
1308
apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
1309
aLayersId == aState.mOriginatingLayersId);
1310
1311
// Since this is the first time we are encountering an APZC with this guid,
1312
// the node holding it must be the primary holder. It may be newly-created
1313
// or not, depending on whether it went through the newApzc branch above.
1314
MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() &&
1315
node->GetApzc()->Matches(guid));
1316
1317
Maybe<ParentLayerIntRegion> clipRegion =
1318
parentHasPerspective ? Nothing() : ComputeClipRegion(aLayer);
1319
SetHitTestData(node, aParent, aLayer, clipRegion);
1320
apzc->SetAncestorTransform(aAncestorTransform);
1321
1322
PrintAPZCInfo(aLayer, apzc);
1323
1324
// Bind the APZC instance into the tree of APZCs
1325
AttachNodeToTree(node, aParent, aNextSibling);
1326
1327
// For testing, log the parent scroll id of every APZC that has a
1328
// parent. This allows test code to reconstruct the APZC tree.
1329
// Note that we currently only do this for APZCs in the layer tree
1330
// that originated the update, because the only identifying information
1331
// we are logging about APZCs is the scroll id, and otherwise we could
1332
// confuse APZCs from different layer trees with the same scroll id.
1333
if (aLayersId == aState.mOriginatingLayersId) {
1334
if (apzc->HasNoParentWithSameLayersId()) {
1335
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1336
"hasNoParentWithSameLayersId", true);
1337
} else {
1338
MOZ_ASSERT(apzc->GetParent());
1339
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1340
"parentScrollId",
1341
apzc->GetParent()->GetGuid().mScrollId);
1342
}
1343
if (aMetrics.IsRootContent()) {
1344
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "isRootContent",
1345
true);
1346
}
1347
// Note that the async scroll offset is in ParentLayer pixels
1348
aState.mPaintLogger.LogTestData(
1349
aMetrics.GetScrollId(), "asyncScrollOffset",
1350
apzc->GetCurrentAsyncScrollOffset(
1351
AsyncPanZoomController::eForHitTesting));
1352
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1353
"hasAsyncKeyScrolled",
1354
apzc->TestHasAsyncKeyScrolled());
1355
}
1356
1357
if (newApzc) {
1358
auto it = mZoomConstraints.find(guid);
1359
if (it != mZoomConstraints.end()) {
1360
// We have a zoomconstraints for this guid, apply it.
1361
apzc->UpdateZoomConstraints(it->second);
1362
} else if (!apzc->HasNoParentWithSameLayersId()) {
1363
// This is a sub-APZC, so inherit the zoom constraints from its parent.
1364
// This ensures that if e.g. user-scalable=no was specified, none of the
1365
// APZCs for that subtree allow double-tap to zoom.
1366
apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
1367
}
1368
// Otherwise, this is the root of a layers id, but we didn't have a saved
1369
// zoom constraints. Leave it empty for now.
1370
}
1371
1372
// Add a guid -> APZC mapping for the newly created APZC.
1373
insertResult.first->second.apzc = apzc;
1374
} else {
1375
// We already built an APZC earlier in this tree walk, but we have another
1376
// layer now that will also be using that APZC. The hit-test region on the
1377
// APZC needs to be updated to deal with the new layer's hit region.
1378
1379
node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId);
1380
AttachNodeToTree(node, aParent, aNextSibling);
1381
1382
// Even though different layers associated with a given APZC may be at
1383
// different levels in the layer tree (e.g. one being an uncle of another),
1384
// we require from Layout that the CSS transforms up to their common
1385
// ancestor be roughly the same. There are cases in which the transforms
1386
// are not exactly the same, for example if the parent is container layer
1387
// for an opacity, and this container layer has a resolution-induced scale
1388
// as its base transform and a prescale that is supposed to undo that scale.
1389
// Due to floating point inaccuracies those transforms can end up not quite
1390
// canceling each other. That's why we're using a fuzzy comparison here
1391
// instead of an exact one.
1392
// In addition, two ancestor transforms are allowed to differ if one of
1393
// them contains a perspective transform component and the other does not.
1394
// This represents situations where some content in a scrollable frame
1395
// is subject to a perspective transform and other content does not.
1396
// In such cases, go with the one that does not include the perspective
1397
// component; the perspective transform is remembered and applied to the
1398
// children instead.
1399
if (!aAncestorTransform.CombinedTransform().FuzzyEqualsMultiplicative(
1400
apzc->GetAncestorTransform())) {
1401
typedef TreeBuildingState::DeferredTransformMap::value_type PairType;
1402
if (!aAncestorTransform.ContainsPerspectiveTransform() &&
1403
!apzc->AncestorTransformContainsPerspective()) {
1404
MOZ_ASSERT(false,
1405
"Two layers that scroll together have different ancestor "
1406
"transforms");
1407
} else if (!aAncestorTransform.ContainsPerspectiveTransform()) {
1408
aState.mPerspectiveTransformsDeferredToChildren.insert(
1409
PairType{apzc, apzc->GetAncestorTransformPerspective()});
1410
apzc->SetAncestorTransform(aAncestorTransform);
1411
} else {
1412
aState.mPerspectiveTransformsDeferredToChildren.insert(
1413
PairType{apzc, aAncestorTransform.GetPerspectiveTransform()});
1414
}
1415
}
1416
1417
Maybe<ParentLayerIntRegion> clipRegion =
1418
parentHasPerspective ? Nothing() : ComputeClipRegion(aLayer);
1419
SetHitTestData(node, aParent, aLayer, clipRegion);
1420
}
1421
1422
// Note: if layer properties must be propagated to nodes, RecvUpdate in
1423
// LayerTransactionParent.cpp must ensure that APZ will be notified
1424
// when those properties change.
1425
node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
1426
aLayer.GetScrollbarData());
1427
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(),
1428
aLayer.GetFixedPositionSides(),
1429
aLayer.GetFixedPositionAnimationId());
1430
if (aLayer.GetIsStickyPosition()) {
1431
node->SetStickyPosData(aLayer.GetStickyScrollContainerId(),
1432
aLayer.GetStickyScrollRangeOuter(),
1433
aLayer.GetStickyScrollRangeInner());
1434
} else {
1435
node->SetStickyPosData(ScrollableLayerGuid::NULL_SCROLL_ID,
1436
LayerRectAbsolute(), LayerRectAbsolute());
1437
}
1438
return node;
1439
}
1440
1441
template <typename PanGestureOrScrollWheelInput>
1442
static bool WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput) {
1443
if (!XRE_IsParentProcess() || !NS_IsMainThread()) {
1444
return true;
1445
}
1446
1447
WidgetWheelEvent wheelEvent = aPanInput.ToWidgetWheelEvent(nullptr);
1448
return APZInputBridge::ActionForWheelEvent(&wheelEvent).isSome();
1449
}
1450
1451
/*static*/
1452
void APZCTreeManager::FlushApzRepaints(LayersId aLayersId) {
1453
// Previously, paints were throttled and therefore this method was used to
1454
// ensure any pending paints were flushed. Now, paints are flushed
1455
// immediately, so it is safe to simply send a notification now.
1456
APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n",
1457
uint64_t(aLayersId));
1458
RefPtr<GeckoContentController> controller = GetContentController(aLayersId);
1459
#ifndef MOZ_WIDGET_ANDROID
1460
// On Android, this code is run in production and may actually get a nullptr
1461
// controller here. On other platforms this code is test-only and should never
1462
// get a nullptr.
1463
MOZ_ASSERT(controller);
1464
#endif
1465
if (controller) {
1466
controller->DispatchToRepaintThread(NewRunnableMethod(
1467
"layers::GeckoContentController::NotifyFlushComplete", controller,
1468
&GeckoContentController::NotifyFlushComplete));
1469
}
1470
}
1471
1472
void APZCTreeManager::MarkAsDetached(LayersId aLayersId) {
1473
RecursiveMutexAutoLock lock(mTreeLock);
1474
mDetachedLayersIds.insert(aLayersId);
1475
}
1476
1477
APZEventResult APZCTreeManager::ReceiveInputEvent(InputData& aEvent) {
1478
APZThreadUtils::AssertOnControllerThread();
1479
APZEventResult result;
1480
1481
// Use a RAII class for updating the focus sequence number of this event
1482
AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
1483
1484
#if defined(MOZ_WIDGET_ANDROID)
1485
if (mToolbarAnimator) {
1486
ScreenPoint scrollOffset;
1487
{
1488
RecursiveMutexAutoLock lock(mTreeLock);
1489
RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
1490
if (apzc) {
1491
scrollOffset = ViewAs<ScreenPixel>(
1492
apzc->GetCurrentAsyncScrollOffset(
1493
AsyncPanZoomController::eForHitTesting),
1494
PixelCastJustification::ScreenIsParentLayerForRoot);
1495
}
1496
}
1497
RefPtr<APZCTreeManager> self = this;
1498
nsEventStatus isConsumed =
1499
mToolbarAnimator->ReceiveInputEvent(self, aEvent, scrollOffset);
1500
// Check if the mToolbarAnimator consumed the event.
1501
if (isConsumed == nsEventStatus_eConsumeNoDefault) {
1502
APZCTM_LOG("Dynamic toolbar consumed event");
1503
result.mStatus = isConsumed;
1504
return result;
1505
}
1506
}
1507
#endif // (MOZ_WIDGET_ANDROID)
1508
1509
CompositorHitTestInfo hitResult = CompositorHitTestInvisibleToHit;
1510
switch (aEvent.mInputType) {
1511
case MULTITOUCH_INPUT: {
1512
MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
1513
result = ProcessTouchInput(touchInput);
1514
break;
1515
}
1516
case MOUSE_INPUT: {
1517
MouseInput& mouseInput = aEvent.AsMouseInput();
1518
mouseInput.mHandledByAPZ = true;
1519
1520
mCurrentMousePosition = mouseInput.mOrigin;
1521
1522
bool startsDrag = DragTracker::StartsDrag(mouseInput);
1523
if (startsDrag) {
1524
// If this is the start of a drag we need to unambiguously know if it's
1525
// going to land on a scrollbar or not. We can't apply an untransform
1526
// here without knowing that, so we need to ensure the untransform is
1527
// a no-op.
1528
FlushRepaintsToClearScreenToGeckoTransform();
1529
}
1530
1531
HitTestResult hit = GetTargetAPZC(mouseInput.mOrigin);
1532
aEvent.mLayersId = hit.mLayersId;
1533
hitResult = hit.mHitResult;
1534
bool hitScrollbar = (bool)hit.mScrollbarNode;
1535
1536
// When the mouse is outside the window we still want to handle dragging
1537
// but we won't find an APZC. Fallback to root APZC then.
1538
{ // scope lock
1539
RecursiveMutexAutoLock lock(mTreeLock);
1540
if (!hit.mTargetApzc && mRootNode) {
1541
hit.mTargetApzc = mRootNode->GetApzc();
1542
}
1543
}
1544
1545
if (hit.mTargetApzc) {
1546
if (StaticPrefs::apz_test_logging_enabled() &&
1547
mouseInput.mType == MouseInput::MOUSE_HITTEST) {
1548
ScrollableLayerGuid guid = hit.mTargetApzc->GetGuid();
1549
1550
MutexAutoLock lock(mTestDataLock);
1551
auto it = mTestData.find(guid.mLayersId);
1552
MOZ_ASSERT(it != mTestData.end());
1553
it->second->RecordHitResult(mouseInput.mOrigin, hitResult,
1554
guid.mLayersId, guid.mScrollId);
1555
}
1556
1557
TargetConfirmationFlags confFlags{hitResult};
1558
bool apzDragEnabled = StaticPrefs::apz_drag_enabled();
1559
if (apzDragEnabled && hitScrollbar) {
1560
// If scrollbar dragging is enabled and we hit a scrollbar, wait
1561
// for the main-thread confirmation because it contains drag metrics
1562
// that we need.
1563
confFlags.mTargetConfirmed = false;
1564
}
1565
result.mStatus = mInputQueue->ReceiveInputEvent(
1566
hit.mTargetApzc, confFlags, mouseInput, &result.mInputBlockId);
1567
1568
// If we're starting an async scrollbar drag
1569
if (apzDragEnabled && startsDrag && hit.mScrollbarNode &&
1570
hit.mScrollbarNode->IsScrollThumbNode() &&
1571
hit.mScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable) {
1572
SetupScrollbarDrag(mouseInput, hit.mScrollbarNode,
1573
hit.mTargetApzc.get());
1574
}
1575
1576
if (result.mStatus == nsEventStatus_eConsumeDoDefault) {
1577
// This input event is part of a drag block, so whether or not it is
1578
// directed at a scrollbar depends on whether the drag block started
1579
// on a scrollbar.
1580
hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
1581
}
1582
1583
// Update the out-parameters so they are what the caller expects.
1584
hit.mTargetApzc->GetGuid(&result.mTargetGuid);
1585
1586
if (!hitScrollbar) {
1587
// The input was not targeted at a scrollbar, so we untransform it
1588
// like we do for other content. Scrollbars are "special" because they
1589
// have special handling in AsyncCompositionManager when resolution is
1590
// applied. TODO: we should find a better way to deal with this.
1591
ScreenToParentLayerMatrix4x4 transformToApzc =
1592
GetScreenToApzcTransform(hit.mTargetApzc);
1593
ParentLayerToScreenMatrix4x4 transformToGecko =
1594
GetApzcToGeckoTransform(hit.mTargetApzc);
1595
ScreenToScreenMatrix4x4 outTransform =
1596
transformToApzc * transformToGecko;
1597
Maybe<ScreenPoint> untransformedRefPoint =
1598
UntransformBy(outTransform, mouseInput.mOrigin);
1599
if (untransformedRefPoint) {
1600
mouseInput.mOrigin = *untransformedRefPoint;
1601
}
1602
} else {
1603
// Likewise, if the input was targeted at a scrollbar, we don't want
1604
// to apply the callback transform in the main thread, so we remove
1605
// the scrollid from the guid. We need to keep the layersId intact so
1606
// that the response from the child process doesn't get discarded.
1607
result.mTargetGuid.mScrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
1608
}
1609
}
1610
break;
1611
}
1612
case SCROLLWHEEL_INPUT: {
1613
FlushRepaintsToClearScreenToGeckoTransform();
1614
1615
// Do this before early return for Fission hit testing.
1616
ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
1617
HitTestResult hit = GetTargetAPZC(wheelInput.mOrigin);
1618
aEvent.mLayersId = hit.mLayersId;
1619
hitResult = hit.mHitResult;
1620
1621
wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
1622
if (!wheelInput.mHandledByAPZ) {
1623
return result;
1624
}
1625
1626
if (hit.mTargetApzc) {
1627
MOZ_ASSERT(hitResult != CompositorHitTestInvisibleToHit);
1628
1629
if (wheelInput.mAPZAction == APZWheelAction::PinchZoom) {
1630
// The mousewheel may have hit a subframe, but we want to send the
1631
// pinch-zoom events to the root-content APZC.
1632
{
1633
RecursiveMutexAutoLock lock(mTreeLock);
1634
hit.mTargetApzc =
1635
FindRootContentApzcForLayersId(hit.mTargetApzc->GetLayersId());
1636
}
1637
if (hit.mTargetApzc) {
1638
SynthesizePinchGestureFromMouseWheel(wheelInput, hit.mTargetApzc);
1639
}
1640
result.mStatus = nsEventStatus_eConsumeNoDefault;
1641
return result;
1642
}
1643
1644
MOZ_ASSERT(wheelInput.mAPZAction == APZWheelAction::Scroll);
1645
1646
// For wheel events, the call to ReceiveInputEvent below may result in
1647
// scrolling, which changes the async transform. However, the event we
1648
// want to pass to gecko should be the pre-scroll event coordinates,
1649
// transformed into the gecko space. (pre-scroll because the mouse
1650
// cursor is stationary during wheel scrolling, unlike touchmove
1651
// events). Since we just flushed the pending repaints the transform to
1652
// gecko space should only consist of overscroll-cancelling transforms.
1653
ScreenToScreenMatrix4x4 transformToGecko =
1654
GetScreenToApzcTransform(hit.mTargetApzc) *
1655
GetApzcToGeckoTransform(hit.mTargetApzc);
1656
Maybe<ScreenPoint> untransformedOrigin =
1657
UntransformBy(transformToGecko, wheelInput.mOrigin);
1658
1659
if (!untransformedOrigin) {
1660
return result;
1661
}
1662
1663
result.mStatus = mInputQueue->ReceiveInputEvent(
1664
hit.mTargetApzc, TargetConfirmationFlags{hitResult}, wheelInput,
1665
&result.mInputBlockId);
1666
1667
// Update the out-parameters so they are what the caller expects.
1668
hit.mTargetApzc->GetGuid(&result.mTargetGuid);
1669
wheelInput.mOrigin = *untransformedOrigin;
1670
}
1671
break;
1672
}
1673
case PANGESTURE_INPUT: {
1674
FlushRepaintsToClearScreenToGeckoTransform();
1675
1676
// Do this before early return for Fission hit testing.
1677
PanGestureInput& panInput = aEvent.AsPanGestureInput();
1678
HitTestResult hit = GetTargetAPZC(panInput.mPanStartPoint);
1679
aEvent.mLayersId = hit.mLayersId;
1680
hitResult = hit.mHitResult;
1681
1682
panInput.mHandledByAPZ = WillHandleInput(panInput);
1683
if (!panInput.mHandledByAPZ) {
1684
return result;
1685
}
1686
1687
// If/when we enable support for pan inputs off-main-thread, we'll need
1688
// to duplicate this EventStateManager code or something. See the call to
1689
// GetUserPrefsForWheelEvent in IAPZCTreeManager.cpp for why these fields
1690
// are stored separately.
1691
MOZ_ASSERT(NS_IsMainThread());
1692
WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
1693
EventStateManager::GetUserPrefsForWheelEvent(
1694
&wheelEvent, &panInput.mUserDeltaMultiplierX,
1695
&panInput.mUserDeltaMultiplierY);
1696
1697
if (hit.mTargetApzc) {
1698
MOZ_ASSERT(hitResult != CompositorHitTestInvisibleToHit);
1699
1700
// For pan gesture events, the call to ReceiveInputEvent below may
1701
// result in scrolling, which changes the async transform. However, the
1702
// event we want to pass to gecko should be the pre-scroll event
1703
// coordinates, transformed into the gecko space. (pre-scroll because
1704
// the mouse cursor is stationary during pan gesture scrolling, unlike
1705
// touchmove events). Since we just flushed the pending repaints the
1706
// transform to gecko space should only consist of overscroll-cancelling
1707
// transforms.
1708
ScreenToScreenMatrix4x4 transformToGecko =
1709
GetScreenToApzcTransform(hit.mTargetApzc) *
1710
GetApzcToGeckoTransform(hit.mTargetApzc);
1711
Maybe<ScreenPoint> untransformedStartPoint =
1712
UntransformBy(transformToGecko, panInput.mPanStartPoint);
1713
Maybe<ScreenPoint> untransformedDisplacement =
1714
UntransformVector(transformToGecko, panInput.mPanDisplacement,
1715
panInput.mPanStartPoint);
1716
1717
if (!untransformedStartPoint || !untransformedDisplacement) {
1718
return result;
1719
}
1720
1721
result.mStatus = mInputQueue->ReceiveInputEvent(
1722
hit.mTargetApzc, TargetConfirmationFlags{hitResult}, panInput,
1723
&result.mInputBlockId);
1724
1725
// Update the out-parameters so they are what the caller expects.
1726
hit.mTargetApzc->GetGuid(&result.mTargetGuid);
1727
panInput.mPanStartPoint = *untransformedStartPoint;
1728
panInput.mPanDisplacement = *untransformedDisplacement;
1729
1730
panInput.mOverscrollBehaviorAllowsSwipe =
1731
hit.mTargetApzc->OverscrollBehaviorAllowsSwipe();
1732
}
1733
break;