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/APZUtils.h" // for AsyncTransform
32 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
33 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
34 #include "mozilla/layers/DoubleTapToZoom.h" // for ZoomTarget
35 #include "mozilla/layers/LayerMetricsWrapper.h"
36 #include "mozilla/layers/MatrixMessage.h"
37 #include "mozilla/layers/UiCompositorControllerParent.h"
38 #include "mozilla/layers/WebRenderScrollDataWrapper.h"
39 #include "mozilla/MouseEvents.h"
40 #include "mozilla/mozalloc.h" // for operator new
41 #include "mozilla/Preferences.h" // for Preferences
42 #include "mozilla/StaticPrefs_accessibility.h"
43 #include "mozilla/StaticPrefs_apz.h"
44 #include "mozilla/StaticPrefs_layout.h"
45 #include "mozilla/ToString.h"
46 #include "mozilla/TouchEvents.h"
47 #include "mozilla/EventStateManager.h" // for WheelPrefs
48 #include "mozilla/webrender/WebRenderAPI.h"
49 #include "nsDebug.h" // for NS_WARNING
50 #include "nsPoint.h" // for nsIntPoint
51 #include "nsThreadUtils.h" // for NS_IsMainThread
52 #include "ScrollThumbUtils.h" // for ComputeTransformForScrollThumb
53 #include "OverscrollHandoffState.h" // for OverscrollHandoffState
54 #include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch, etc
55 #include "Units.h" // for ParentlayerPixel
56 #include "GestureEventListener.h" // for GestureEventListener::setLongTapEnabled
57 #include "UnitTransforms.h" // for ViewAs
58
59 static mozilla::LazyLogModule sApzMgrLog("apz.manager");
60 #define APZCTM_LOG(...) MOZ_LOG(sApzMgrLog, LogLevel::Debug, (__VA_ARGS__))
61
62 static mozilla::LazyLogModule sApzKeyLog("apz.key");
63 #define APZ_KEY_LOG(...) MOZ_LOG(sApzKeyLog, LogLevel::Debug, (__VA_ARGS__))
64
65 namespace mozilla {
66 namespace layers {
67
68 using mozilla::gfx::CompositorHitTestDispatchToContent;
69 using mozilla::gfx::CompositorHitTestFlags;
70 using mozilla::gfx::CompositorHitTestInfo;
71 using mozilla::gfx::CompositorHitTestInvisibleToHit;
72 using mozilla::gfx::LOG_DEFAULT;
73
74 typedef mozilla::gfx::Point Point;
75 typedef mozilla::gfx::Point4D Point4D;
76 typedef mozilla::gfx::Matrix4x4 Matrix4x4;
77
78 typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
79
80 struct APZCTreeManager::TreeBuildingState {
TreeBuildingStatemozilla::layers::APZCTreeManager::TreeBuildingState81 TreeBuildingState(LayersId aRootLayersId, bool aIsFirstPaint,
82 LayersId aOriginatingLayersId, APZTestData* aTestData,
83 uint32_t aPaintSequence)
84 : mIsFirstPaint(aIsFirstPaint),
85 mOriginatingLayersId(aOriginatingLayersId),
86 mPaintLogger(aTestData, aPaintSequence) {
87 CompositorBridgeParent::CallWithIndirectShadowTree(
88 aRootLayersId, [this](LayerTreeState& aState) -> void {
89 mCompositorController = aState.GetCompositorController();
90 mInProcessSharingController = aState.InProcessSharingController();
91 });
92 }
93
94 typedef std::unordered_map<AsyncPanZoomController*, gfx::Matrix4x4>
95 DeferredTransformMap;
96
97 // State that doesn't change as we recurse in the tree building
98 RefPtr<CompositorController> mCompositorController;
99 RefPtr<MetricsSharingController> mInProcessSharingController;
100 const bool mIsFirstPaint;
101 const LayersId mOriginatingLayersId;
102 const APZPaintLogHelper mPaintLogger;
103
104 // State that is updated as we perform the tree build
105
106 // A list of nodes that need to be destroyed at the end of the tree building.
107 // This is initialized with all nodes in the old tree, and nodes are removed
108 // from it as we reuse them in the new tree.
109 nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
110
111 // This map is populated as we place APZCs into the new tree. Its purpose is
112 // to facilitate re-using the same APZC for different layers that scroll
113 // together (and thus have the same ScrollableLayerGuid). The presShellId
114 // doesn't matter for this purpose, and we move the map to the APZCTreeManager
115 // after we're done building, so it's useful to have the presshell-ignoring
116 // map for that.
117 std::unordered_map<ScrollableLayerGuid, ApzcMapData,
118 ScrollableLayerGuid::HashIgnoringPresShellFn,
119 ScrollableLayerGuid::EqualIgnoringPresShellFn>
120 mApzcMap;
121
122 // This is populated with all the HitTestingTreeNodes that are scroll thumbs
123 // and have a scrollthumb animation id (which indicates that they need to be
124 // sampled for WebRender on the sampler thread).
125 std::vector<HitTestingTreeNode*> mScrollThumbs;
126 // This is populated with all the scroll target nodes. We use in conjunction
127 // with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo.
128 std::unordered_map<ScrollableLayerGuid, HitTestingTreeNode*,
129 ScrollableLayerGuid::HashIgnoringPresShellFn,
130 ScrollableLayerGuid::EqualIgnoringPresShellFn>
131 mScrollTargets;
132
133 // As the tree is traversed, the top element of this stack tracks whether
134 // the parent scroll node has a perspective transform.
135 std::stack<bool> mParentHasPerspective;
136
137 // During the tree building process, the perspective transform component
138 // of the ancestor transforms of some APZCs can be "deferred" to their
139 // children, meaning they are added to the children's ancestor transforms
140 // instead. Those deferred transforms are tracked here.
141 DeferredTransformMap mPerspectiveTransformsDeferredToChildren;
142
143 // As we recurse down through the tree, this picks up the zoom animation id
144 // from a node in the layer tree, and propagates it downwards to the nearest
145 // APZC instance that is for an RCD node. Generally it will be set on the
146 // root node of the layers (sub-)tree, which may not be same as the RCD node
147 // for the subtree, and so we need this mechanism to ensure it gets propagated
148 // to the RCD's APZC instance. Once it is set on the APZC instance, the value
149 // is cleared back to Nothing(). Note that this is only used in the WebRender
150 // codepath.
151 Maybe<uint64_t> mZoomAnimationId;
152
153 // See corresponding members of APZCTreeManager. These are the same thing, but
154 // on the tree-walking state. They are populated while walking the tree in
155 // a layers update, and then moved into APZCTreeManager.
156 std::vector<FixedPositionInfo> mFixedPositionInfo;
157 std::vector<RootScrollbarInfo> mRootScrollbarInfo;
158 std::vector<StickyPositionInfo> mStickyPositionInfo;
159
160 // As we recurse down through reflayers in the tree, this picks up the
161 // cumulative EventRegionsOverride flags from the reflayers, and is used to
162 // apply them to descendant layers.
163 std::stack<EventRegionsOverride> mOverrideFlags;
164 };
165
166 class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
167 public:
168 NS_DECL_ISUPPORTS
169 NS_DECL_NSIOBSERVER
170
CheckerboardFlushObserver(APZCTreeManager * aTreeManager)171 explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
172 : mTreeManager(aTreeManager) {
173 MOZ_ASSERT(NS_IsMainThread());
174 nsCOMPtr<nsIObserverService> obsSvc =
175 mozilla::services::GetObserverService();
176 MOZ_ASSERT(obsSvc);
177 if (obsSvc) {
178 obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
179 }
180 }
181
Unregister()182 void Unregister() {
183 MOZ_ASSERT(NS_IsMainThread());
184 nsCOMPtr<nsIObserverService> obsSvc =
185 mozilla::services::GetObserverService();
186 if (obsSvc) {
187 obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
188 }
189 mTreeManager = nullptr;
190 }
191
192 protected:
193 virtual ~CheckerboardFlushObserver() = default;
194
195 private:
196 RefPtr<APZCTreeManager> mTreeManager;
197 };
198
NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver,nsIObserver)199 NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
200
201 NS_IMETHODIMP
202 APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
203 const char* aTopic,
204 const char16_t*) {
205 MOZ_ASSERT(NS_IsMainThread());
206 MOZ_ASSERT(mTreeManager.get());
207
208 RecursiveMutexAutoLock lock(mTreeManager->mTreeLock);
209 if (mTreeManager->mRootNode) {
210 ForEachNode<ReverseIterator>(
211 mTreeManager->mRootNode.get(), [](HitTestingTreeNode* aNode) {
212 if (aNode->IsPrimaryHolder()) {
213 MOZ_ASSERT(aNode->GetApzc());
214 aNode->GetApzc()->FlushActiveCheckerboardReport();
215 }
216 });
217 }
218 if (XRE_IsGPUProcess()) {
219 if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
220 nsCString topic("APZ:FlushActiveCheckerboard:Done");
221 Unused << gpu->SendNotifyUiObservers(topic);
222 }
223 } else {
224 MOZ_ASSERT(XRE_IsParentProcess());
225 nsCOMPtr<nsIObserverService> obsSvc =
226 mozilla::services::GetObserverService();
227 if (obsSvc) {
228 obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done",
229 nullptr);
230 }
231 }
232 return NS_OK;
233 }
234
235 /**
236 * A RAII class used for setting the focus sequence number on input events
237 * as they are being processed. Any input event is assumed to be potentially
238 * focus changing unless explicitly marked otherwise.
239 */
240 class MOZ_RAII AutoFocusSequenceNumberSetter {
241 public:
AutoFocusSequenceNumberSetter(FocusState & aFocusState,InputData & aEvent)242 AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
243 : mFocusState(aFocusState), mEvent(aEvent), mMayChangeFocus(true) {}
244
MarkAsNonFocusChanging()245 void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
246
~AutoFocusSequenceNumberSetter()247 ~AutoFocusSequenceNumberSetter() {
248 if (mMayChangeFocus) {
249 mFocusState.ReceiveFocusChangingEvent();
250
251 APZ_KEY_LOG(
252 "Marking input with type=%d as focus changing with seq=%" PRIu64 "\n",
253 static_cast<int>(mEvent.mInputType),
254 mFocusState.LastAPZProcessedEvent());
255 } else {
256 APZ_KEY_LOG(
257 "Marking input with type=%d as non focus changing with seq=%" PRIu64
258 "\n",
259 static_cast<int>(mEvent.mInputType),
260 mFocusState.LastAPZProcessedEvent());
261 }
262
263 mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
264 }
265
266 private:
267 FocusState& mFocusState;
268 InputData& mEvent;
269 bool mMayChangeFocus;
270 };
271
APZCTreeManager(LayersId aRootLayersId,bool aIsUsingWebRender)272 APZCTreeManager::APZCTreeManager(LayersId aRootLayersId, bool aIsUsingWebRender)
273 : mTestSampleTime(Nothing(), "APZCTreeManager::mTestSampleTime"),
274 mInputQueue(new InputQueue()),
275 mRootLayersId(aRootLayersId),
276 mSampler(nullptr),
277 mUpdater(nullptr),
278 mTreeLock("APZCTreeLock"),
279 mMapLock("APZCMapLock"),
280 mRetainedTouchIdentifier(-1),
281 mInScrollbarTouchDrag(false),
282 mCurrentMousePosition(ScreenPoint(),
283 "APZCTreeManager::mCurrentMousePosition"),
284 mApzcTreeLog("apzctree"),
285 mTestDataLock("APZTestDataLock"),
286 mDPI(160.0),
287 mIsUsingWebRender(aIsUsingWebRender) {
288 RefPtr<APZCTreeManager> self(this);
289 NS_DispatchToMainThread(NS_NewRunnableFunction(
290 "layers::APZCTreeManager::APZCTreeManager",
291 [self] { self->mFlushObserver = new CheckerboardFlushObserver(self); }));
292 AsyncPanZoomController::InitializeGlobalState();
293 mApzcTreeLog.ConditionOnPrefFunction(StaticPrefs::apz_printtree);
294 }
295
296 APZCTreeManager::~APZCTreeManager() = default;
297
SetSampler(APZSampler * aSampler)298 void APZCTreeManager::SetSampler(APZSampler* aSampler) {
299 // We're either setting the sampler or clearing it
300 MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
301 mSampler = aSampler;
302 }
303
SetUpdater(APZUpdater * aUpdater)304 void APZCTreeManager::SetUpdater(APZUpdater* aUpdater) {
305 // We're either setting the updater or clearing it
306 MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
307 mUpdater = aUpdater;
308 }
309
NotifyLayerTreeAdopted(LayersId aLayersId,const RefPtr<APZCTreeManager> & aOldApzcTreeManager)310 void APZCTreeManager::NotifyLayerTreeAdopted(
311 LayersId aLayersId, const RefPtr<APZCTreeManager>& aOldApzcTreeManager) {
312 AssertOnUpdaterThread();
313
314 if (aOldApzcTreeManager) {
315 aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
316 // While we could move the focus target information from the old APZC tree
317 // manager into this one, it's safer to not do that, as we'll probably have
318 // that information repopulated soon anyway (on the next layers update).
319 }
320
321 UniquePtr<APZTestData> adoptedData;
322 if (aOldApzcTreeManager) {
323 MutexAutoLock lock(aOldApzcTreeManager->mTestDataLock);
324 auto it = aOldApzcTreeManager->mTestData.find(aLayersId);
325 if (it != aOldApzcTreeManager->mTestData.end()) {
326 adoptedData = std::move(it->second);
327 aOldApzcTreeManager->mTestData.erase(it);
328 }
329 }
330 if (adoptedData) {
331 MutexAutoLock lock(mTestDataLock);
332 mTestData[aLayersId] = std::move(adoptedData);
333 }
334 }
335
NotifyLayerTreeRemoved(LayersId aLayersId)336 void APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId) {
337 AssertOnUpdaterThread();
338
339 mFocusState.RemoveFocusTarget(aLayersId);
340
341 { // scope lock
342 MutexAutoLock lock(mTestDataLock);
343 mTestData.erase(aLayersId);
344 }
345 }
346
NewAPZCInstance(LayersId aLayersId,GeckoContentController * aController)347 AsyncPanZoomController* APZCTreeManager::NewAPZCInstance(
348 LayersId aLayersId, GeckoContentController* aController) {
349 return new AsyncPanZoomController(
350 aLayersId, this, mInputQueue, aController,
351 AsyncPanZoomController::USE_GESTURE_DETECTOR);
352 }
353
SetTestSampleTime(const Maybe<TimeStamp> & aTime)354 void APZCTreeManager::SetTestSampleTime(const Maybe<TimeStamp>& aTime) {
355 auto testSampleTime = mTestSampleTime.Lock();
356 testSampleTime.ref() = aTime;
357 }
358
GetFrameTime()359 SampleTime APZCTreeManager::GetFrameTime() {
360 auto testSampleTime = mTestSampleTime.Lock();
361 if (testSampleTime.ref()) {
362 return SampleTime::FromTest(*testSampleTime.ref());
363 }
364 return SampleTime::FromNow();
365 }
366
SetAllowedTouchBehavior(uint64_t aInputBlockId,const nsTArray<TouchBehaviorFlags> & aValues)367 void APZCTreeManager::SetAllowedTouchBehavior(
368 uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
369 APZThreadUtils::AssertOnControllerThread();
370
371 mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
372 }
373
374 template <class ScrollNode>
375 void // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
UpdateHitTestingTreeImpl(const ScrollNode & aRoot,bool aIsFirstPaint,LayersId aOriginatingLayersId,uint32_t aPaintSequenceNumber)376 APZCTreeManager::UpdateHitTestingTreeImpl(const ScrollNode& aRoot,
377 bool aIsFirstPaint,
378 LayersId aOriginatingLayersId,
379 uint32_t aPaintSequenceNumber) {
380 RecursiveMutexAutoLock lock(mTreeLock);
381
382 // For testing purposes, we log some data to the APZTestData associated with
383 // the layers id that originated this update.
384 APZTestData* testData = nullptr;
385 if (StaticPrefs::apz_test_logging_enabled()) {
386 MutexAutoLock lock(mTestDataLock);
387 UniquePtr<APZTestData> ptr = MakeUnique<APZTestData>();
388 auto result =
389 mTestData.insert(std::make_pair(aOriginatingLayersId, std::move(ptr)));
390 testData = result.first->second.get();
391 testData->StartNewPaint(aPaintSequenceNumber);
392 }
393
394 TreeBuildingState state(mRootLayersId, aIsFirstPaint, aOriginatingLayersId,
395 testData, aPaintSequenceNumber);
396
397 // We do this business with collecting the entire tree into an array because
398 // otherwise it's very hard to determine which APZC instances need to be
399 // destroyed. In the worst case, there are two scenarios: (a) a layer with an
400 // APZC is removed from the layer tree and (b) a layer with an APZC is moved
401 // in the layer tree from one place to a completely different place. In
402 // scenario (a) we would want to destroy the APZC while walking the layer tree
403 // and noticing that the layer/APZC is no longer there. But if we do that then
404 // we run into a problem in scenario (b) because we might encounter that layer
405 // later during the walk. To handle both of these we have to 'remember' that
406 // the layer was not found, and then do the destroy only at the end of the
407 // tree walk after we are sure that the layer was removed and not just
408 // transplanted elsewhere. Doing that as part of a recursive tree walk is hard
409 // and so maintaining a list and removing APZCs that are still alive is much
410 // simpler.
411 ForEachNode<ReverseIterator>(mRootNode.get(),
412 [&state](HitTestingTreeNode* aNode) {
413 state.mNodesToDestroy.AppendElement(aNode);
414 });
415 mRootNode = nullptr;
416 mAsyncZoomContainerSubtree = Nothing();
417 int asyncZoomContainerNestingDepth = 0;
418 bool haveNestedAsyncZoomContainers = false;
419 nsTArray<LayersId> subtreesWithRootContentOutsideAsyncZoomContainer;
420
421 if (aRoot) {
422 std::unordered_set<LayersId, LayersId::HashFn> seenLayersIds;
423 std::stack<gfx::TreeAutoIndent<LOG_DEFAULT>> indents;
424 std::stack<AncestorTransform> ancestorTransforms;
425 HitTestingTreeNode* parent = nullptr;
426 HitTestingTreeNode* next = nullptr;
427 LayersId layersId = mRootLayersId;
428 seenLayersIds.insert(mRootLayersId);
429 ancestorTransforms.push(AncestorTransform());
430 state.mParentHasPerspective.push(false);
431 state.mOverrideFlags.push(EventRegionsOverride::NoOverride);
432 nsTArray<Maybe<ZoomConstraints>> zoomConstraintsStack;
433
434 // push a nothing to be used for anything outside an async zoom container
435 zoomConstraintsStack.AppendElement(Nothing());
436
437 mApzcTreeLog << "[start]\n";
438 mTreeLock.AssertCurrentThreadIn();
439
440 ForEachNode<ReverseIterator>(
441 aRoot,
442 [&](ScrollNode aLayerMetrics) {
443 mApzcTreeLog << aLayerMetrics.Name() << '\t';
444
445 if (auto asyncZoomContainerId =
446 aLayerMetrics.GetAsyncZoomContainerId()) {
447 if (asyncZoomContainerNestingDepth > 0) {
448 haveNestedAsyncZoomContainers = true;
449 }
450 mAsyncZoomContainerSubtree = Some(layersId);
451 ++asyncZoomContainerNestingDepth;
452
453 auto it = mZoomConstraints.find(
454 ScrollableLayerGuid(layersId, 0, *asyncZoomContainerId));
455 if (it != mZoomConstraints.end()) {
456 zoomConstraintsStack.AppendElement(Some(it->second));
457 } else {
458 zoomConstraintsStack.AppendElement(Nothing());
459 }
460 }
461
462 if (aLayerMetrics.Metrics().IsRootContent()) {
463 MutexAutoLock lock(mMapLock);
464 mGeckoFixedLayerMargins =
465 aLayerMetrics.Metrics().GetFixedLayerMargins();
466 } else {
467 MOZ_ASSERT(aLayerMetrics.Metrics().GetFixedLayerMargins() ==
468 ScreenMargin(),
469 "fixed-layer-margins should be 0 on non-root layer");
470 }
471
472 // Note that this check happens after the potential increment of
473 // asyncZoomContainerNestingDepth, to allow the root content
474 // metadata to be on the same node as the async zoom container.
475 if (aLayerMetrics.Metrics().IsRootContent() &&
476 asyncZoomContainerNestingDepth == 0) {
477 subtreesWithRootContentOutsideAsyncZoomContainer.AppendElement(
478 layersId);
479 }
480
481 HitTestingTreeNode* node = PrepareNodeForLayer(
482 lock, aLayerMetrics, aLayerMetrics.Metrics(), layersId,
483 zoomConstraintsStack.LastElement(), ancestorTransforms.top(),
484 parent, next, state);
485 MOZ_ASSERT(node);
486 AsyncPanZoomController* apzc = node->GetApzc();
487 aLayerMetrics.SetApzc(apzc);
488
489 // GetScrollbarAnimationId is only set when webrender is enabled,
490 // which limits the extra thumb mapping work to the webrender-enabled
491 // case where it is needed.
492 // Note also that when webrender is enabled, a "valid" animation id
493 // is always nonzero, so we don't need to worry about handling the
494 // case where WR is enabled and the animation id is zero.
495 if (node->GetScrollbarAnimationId()) {
496 if (node->IsScrollThumbNode()) {
497 state.mScrollThumbs.push_back(node);
498 } else if (node->IsScrollbarContainerNode()) {
499 // Only scrollbar containers for the root have an animation id.
500 state.mRootScrollbarInfo.emplace_back(
501 *(node->GetScrollbarAnimationId()),
502 node->GetScrollbarDirection());
503 }
504 }
505
506 // GetFixedPositionAnimationId is only set when webrender is enabled.
507 if (node->GetFixedPositionAnimationId().isSome()) {
508 state.mFixedPositionInfo.emplace_back(node);
509 }
510 // GetStickyPositionAnimationId is only set when webrender is enabled.
511 if (node->GetStickyPositionAnimationId().isSome()) {
512 state.mStickyPositionInfo.emplace_back(node);
513 }
514 if (apzc && node->IsPrimaryHolder()) {
515 state.mScrollTargets[apzc->GetGuid()] = node;
516 }
517
518 mApzcTreeLog << '\n';
519
520 // Accumulate the CSS transform between layers that have an APZC.
521 // In the terminology of the big comment above
522 // APZCTreeManager::GetScreenToApzcTransform, if we are at layer M,
523 // then aAncestorTransform is NC * OC * PC, and we left-multiply MC
524 // and compute ancestorTransform to be MC * NC * OC * PC. This gets
525 // passed down as the ancestor transform to layer L when we recurse
526 // into the children below. If we are at a layer with an APZC, such as
527 // P, then we reset the ancestorTransform to just PC, to start the new
528 // accumulation as we go down.
529 AncestorTransform currentTransform{
530 aLayerMetrics.GetTransform(),
531 aLayerMetrics.TransformIsPerspective()};
532 if (!apzc) {
533 currentTransform = currentTransform * ancestorTransforms.top();
534 }
535 ancestorTransforms.push(currentTransform);
536
537 // Note that |node| at this point will not have any children,
538 // otherwise we we would have to set next to node->GetFirstChild().
539 MOZ_ASSERT(!node->GetFirstChild());
540 parent = node;
541 next = nullptr;
542
543 // Update the layersId if we have a new one
544 if (Maybe<LayersId> newLayersId = aLayerMetrics.GetReferentId()) {
545 layersId = *newLayersId;
546 seenLayersIds.insert(layersId);
547
548 // Propagate any event region override flags down into all
549 // descendant nodes from the reflayer that has the flag. This is an
550 // optimization to avoid having to walk up the tree to check the
551 // override flags. Note that we don't keep the flags on the reflayer
552 // itself, because the semantics of the flags are that they apply
553 // to all content in the layer subtree being referenced. This
554 // matters with the WR hit-test codepath, because this reflayer may
555 // be just one of many nodes associated with a particular APZC, and
556 // calling GetTargetNode with a guid may return any one of the
557 // nodes. If different nodes have different flags on them that can
558 // make the WR hit-test result incorrect, but being strict about
559 // only putting the flags on descendant layers avoids this problem.
560 state.mOverrideFlags.push(state.mOverrideFlags.top() |
561 aLayerMetrics.GetEventRegionsOverride());
562 }
563
564 indents.push(gfx::TreeAutoIndent<LOG_DEFAULT>(mApzcTreeLog));
565 state.mParentHasPerspective.push(
566 aLayerMetrics.TransformIsPerspective());
567 },
568 [&](ScrollNode aLayerMetrics) {
569 if (aLayerMetrics.GetAsyncZoomContainerId()) {
570 --asyncZoomContainerNestingDepth;
571 zoomConstraintsStack.RemoveLastElement();
572 }
573 if (aLayerMetrics.GetReferentId()) {
574 state.mOverrideFlags.pop();
575 }
576
577 next = parent;
578 parent = parent->GetParent();
579 layersId = next->GetLayersId();
580 ancestorTransforms.pop();
581 indents.pop();
582 state.mParentHasPerspective.pop();
583 });
584
585 mApzcTreeLog << "[end]\n";
586
587 MOZ_ASSERT(
588 !mAsyncZoomContainerSubtree ||
589 !subtreesWithRootContentOutsideAsyncZoomContainer.Contains(
590 *mAsyncZoomContainerSubtree),
591 "If there is an async zoom container, all scroll nodes with root "
592 "content scroll metadata should be inside it");
593 MOZ_ASSERT(!haveNestedAsyncZoomContainers,
594 "Should not have nested async zoom container");
595
596 // If we have perspective transforms deferred to children, do another
597 // walk of the tree and actually apply them to the children.
598 // We can't do this "as we go" in the previous traversal, because by the
599 // time we realize we need to defer a perspective transform for an APZC,
600 // we may already have processed a previous layer (including children
601 // found in its subtree) that shares that APZC.
602 if (!state.mPerspectiveTransformsDeferredToChildren.empty()) {
603 ForEachNode<ReverseIterator>(
604 mRootNode.get(), [&state](HitTestingTreeNode* aNode) {
605 AsyncPanZoomController* apzc = aNode->GetApzc();
606 if (!apzc) {
607 return;
608 }
609 if (!aNode->IsPrimaryHolder()) {
610 return;
611 }
612
613 AsyncPanZoomController* parent = apzc->GetParent();
614 if (!parent) {
615 return;
616 }
617
618 auto it =
619 state.mPerspectiveTransformsDeferredToChildren.find(parent);
620 if (it != state.mPerspectiveTransformsDeferredToChildren.end()) {
621 apzc->SetAncestorTransform(AncestorTransform{
622 it->second * apzc->GetAncestorTransform(), false});
623 }
624 });
625 }
626
627 // Remove any layers ids for which we no longer have content from
628 // mDetachedLayersIds.
629 for (auto iter = mDetachedLayersIds.begin();
630 iter != mDetachedLayersIds.end();) {
631 // unordered_set::erase() invalidates the iterator pointing to the
632 // element being erased, but returns an iterator to the next element.
633 if (seenLayersIds.find(*iter) == seenLayersIds.end()) {
634 iter = mDetachedLayersIds.erase(iter);
635 } else {
636 ++iter;
637 }
638 }
639 }
640
641 // We do not support tree structures where the root node has siblings.
642 MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
643
644 { // scope lock and update our mApzcMap before we destroy all the unused
645 // APZC instances
646 MutexAutoLock lock(mMapLock);
647 mApzcMap = std::move(state.mApzcMap);
648
649 for (auto& mapping : mApzcMap) {
650 AsyncPanZoomController* parent = mapping.second.apzc->GetParent();
651 mapping.second.parent = parent ? Some(parent->GetGuid()) : Nothing();
652 }
653
654 mScrollThumbInfo.clear();
655 // For non-webrender, state.mScrollThumbs will be empty so this will be a
656 // no-op.
657 for (HitTestingTreeNode* thumb : state.mScrollThumbs) {
658 MOZ_ASSERT(thumb->IsScrollThumbNode());
659 ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0,
660 thumb->GetScrollTargetId());
661 auto it = state.mScrollTargets.find(targetGuid);
662 if (it == state.mScrollTargets.end()) {
663 // It could be that |thumb| is a scrollthumb for content which didn't
664 // have an APZC, for example if the content isn't layerized. Regardless,
665 // we can't async-scroll it so we don't need to worry about putting it
666 // in mScrollThumbInfo.
667 continue;
668 }
669 HitTestingTreeNode* target = it->second;
670 mScrollThumbInfo.emplace_back(
671 *(thumb->GetScrollbarAnimationId()), thumb->GetTransform(),
672 thumb->GetScrollbarData(), targetGuid, target->GetTransform(),
673 target->IsAncestorOf(thumb));
674 }
675
676 mRootScrollbarInfo = std::move(state.mRootScrollbarInfo);
677 mFixedPositionInfo = std::move(state.mFixedPositionInfo);
678 mStickyPositionInfo = std::move(state.mStickyPositionInfo);
679 }
680
681 for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
682 APZCTM_LOG("Destroying node at %p with APZC %p\n",
683 state.mNodesToDestroy[i].get(),
684 state.mNodesToDestroy[i]->GetApzc());
685 state.mNodesToDestroy[i]->Destroy();
686 }
687
688 APZCTM_LOG("APZCTreeManager (%p)\n", this);
689 if (mRootNode && MOZ_LOG_TEST(sApzMgrLog, LogLevel::Debug)) {
690 mRootNode->Dump(" ");
691 }
692 SendSubtreeTransformsToChromeMainThread(nullptr);
693 }
694
UpdateFocusState(LayersId aRootLayerTreeId,LayersId aOriginatingLayersId,const FocusTarget & aFocusTarget)695 void APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
696 LayersId aOriginatingLayersId,
697 const FocusTarget& aFocusTarget) {
698 AssertOnUpdaterThread();
699
700 if (!StaticPrefs::apz_keyboard_enabled_AtStartup()) {
701 return;
702 }
703
704 mFocusState.Update(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget);
705 }
706
UpdateHitTestingTree(Layer * aRoot,bool aIsFirstPaint,LayersId aOriginatingLayersId,uint32_t aPaintSequenceNumber)707 void APZCTreeManager::UpdateHitTestingTree(Layer* aRoot, bool aIsFirstPaint,
708 LayersId aOriginatingLayersId,
709 uint32_t aPaintSequenceNumber) {
710 AssertOnUpdaterThread();
711
712 LayerMetricsWrapper root(aRoot);
713 UpdateHitTestingTreeImpl(root, aIsFirstPaint, aOriginatingLayersId,
714 aPaintSequenceNumber);
715 }
716
UpdateHitTestingTree(const WebRenderScrollDataWrapper & aScrollWrapper,bool aIsFirstPaint,LayersId aOriginatingLayersId,uint32_t aPaintSequenceNumber)717 void APZCTreeManager::UpdateHitTestingTree(
718 const WebRenderScrollDataWrapper& aScrollWrapper, bool aIsFirstPaint,
719 LayersId aOriginatingLayersId, uint32_t aPaintSequenceNumber) {
720 AssertOnUpdaterThread();
721
722 UpdateHitTestingTreeImpl(aScrollWrapper, aIsFirstPaint, aOriginatingLayersId,
723 aPaintSequenceNumber);
724 }
725
SampleForWebRender(const Maybe<VsyncId> & aVsyncId,wr::TransactionWrapper & aTxn,const SampleTime & aSampleTime)726 void APZCTreeManager::SampleForWebRender(const Maybe<VsyncId>& aVsyncId,
727 wr::TransactionWrapper& aTxn,
728 const SampleTime& aSampleTime) {
729 AssertOnSamplerThread();
730 MutexAutoLock lock(mMapLock);
731
732 RefPtr<WebRenderBridgeParent> wrBridgeParent;
733 RefPtr<CompositorController> controller;
734 CompositorBridgeParent::CallWithIndirectShadowTree(
735 mRootLayersId, [&](LayerTreeState& aState) -> void {
736 controller = aState.GetCompositorController();
737 wrBridgeParent = aState.mWrBridge;
738 });
739
740 bool activeAnimations = AdvanceAnimationsInternal(lock, aSampleTime);
741 if (activeAnimations && controller) {
742 controller->ScheduleRenderOnCompositorThread();
743 }
744
745 nsTArray<wr::WrTransformProperty> transforms;
746
747 // Sample async transforms on scrollable layers.
748 for (const auto& mapping : mApzcMap) {
749 AsyncPanZoomController* apzc = mapping.second.apzc;
750
751 const AsyncTransformComponents asyncTransformComponents =
752 apzc->GetZoomAnimationId()
753 ? AsyncTransformComponents{AsyncTransformComponent::eLayout}
754 : LayoutAndVisual;
755 ParentLayerPoint layerTranslation =
756 apzc->GetCurrentAsyncTransformWithOverscroll(
757 AsyncPanZoomController::eForCompositing,
758 asyncTransformComponents)
759 .TransformPoint(ParentLayerPoint(0, 0));
760
761 if (Maybe<CompositionPayload> payload = apzc->NotifyScrollSampling()) {
762 if (wrBridgeParent && aVsyncId) {
763 wrBridgeParent->AddPendingScrollPayload(*payload, *aVsyncId);
764 }
765 }
766
767 if (Maybe<uint64_t> zoomAnimationId = apzc->GetZoomAnimationId()) {
768 // for now we only support zooming on root content APZCs
769 MOZ_ASSERT(apzc->IsRootContent());
770
771 LayoutDeviceToParentLayerScale zoom = apzc->GetCurrentPinchZoomScale(
772 AsyncPanZoomController::eForCompositing);
773
774 AsyncTransform asyncVisualTransform = apzc->GetCurrentAsyncTransform(
775 AsyncPanZoomController::eForCompositing,
776 AsyncTransformComponents{AsyncTransformComponent::eVisual});
777
778 transforms.AppendElement(wr::ToWrTransformProperty(
779 *zoomAnimationId, LayoutDeviceToParentLayerMatrix4x4::Scaling(
780 zoom.scale, zoom.scale, 1.0f) *
781 AsyncTransformComponentMatrix::Translation(
782 asyncVisualTransform.mTranslation) *
783 apzc->GetOverscrollTransform(
784 AsyncPanZoomController::eForCompositing)));
785
786 aTxn.UpdateIsTransformAsyncZooming(*zoomAnimationId,
787 apzc->IsAsyncZooming());
788 }
789
790 // If layerTranslation includes only the layout component of the async
791 // transform then it has not been scaled by the async zoom, so we want to
792 // divide it by the resolution. If layerTranslation includes the visual
793 // component, then we should use the pinch zoom scale, which includes the
794 // async zoom. However, we only use LayoutAndVisual for non-zoomable APZCs,
795 // so it makes no difference.
796 LayoutDeviceToParentLayerScale resolution =
797 apzc->GetCumulativeResolution().ToScaleFactor() *
798 LayerToParentLayerScale(1.0f);
799 // The positive translation means the painted content is supposed to
800 // move down (or to the right), and that corresponds to a reduction in
801 // the scroll offset. Since we are effectively giving WR the async
802 // scroll delta here, we want to negate the translation.
803 LayoutDevicePoint asyncScrollDelta = -layerTranslation / resolution;
804 aTxn.UpdateScrollPosition(wr::AsPipelineId(apzc->GetGuid().mLayersId),
805 apzc->GetGuid().mScrollId,
806 wr::ToLayoutPoint(asyncScrollDelta));
807
808 #if defined(MOZ_WIDGET_ANDROID)
809 // Send the root frame metrics to java through the UIController
810 RefPtr<UiCompositorControllerParent> uiController =
811 UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayersId);
812 if (uiController &&
813 apzc->UpdateRootFrameMetricsIfChanged(mLastRootMetrics)) {
814 uiController->NotifyUpdateScreenMetrics(mLastRootMetrics);
815 }
816 #endif
817 }
818
819 // Now collect all the async transforms needed for the scrollthumbs.
820 for (const ScrollThumbInfo& info : mScrollThumbInfo) {
821 auto it = mApzcMap.find(info.mTargetGuid);
822 if (it == mApzcMap.end()) {
823 // It could be that |info| is a scrollthumb for content which didn't
824 // have an APZC, for example if the content isn't layerized. Regardless,
825 // we can't async-scroll it so we don't need to worry about putting it
826 // in mScrollThumbInfo.
827 continue;
828 }
829 AsyncPanZoomController* scrollTargetApzc = it->second.apzc;
830 MOZ_ASSERT(scrollTargetApzc);
831 LayerToParentLayerMatrix4x4 transform =
832 scrollTargetApzc->CallWithLastContentPaintMetrics(
833 [&](const FrameMetrics& aMetrics) {
834 return ComputeTransformForScrollThumb(
835 info.mThumbTransform * AsyncTransformMatrix(),
836 info.mTargetTransform.ToUnknownMatrix(), scrollTargetApzc,
837 aMetrics, info.mThumbData, info.mTargetIsAncestor, nullptr);
838 });
839 transforms.AppendElement(
840 wr::ToWrTransformProperty(info.mThumbAnimationId, transform));
841 }
842
843 // Move the root scrollbar in response to the dynamic toolbar transition.
844 for (const RootScrollbarInfo& info : mRootScrollbarInfo) {
845 // We only care about the horizontal scrollbar.
846 if (info.mScrollDirection == ScrollDirection::eHorizontal) {
847 ScreenPoint translation =
848 apz::ComputeFixedMarginsOffset(GetCompositorFixedLayerMargins(lock),
849 SideBits::eBottom, ScreenMargin());
850
851 LayerToParentLayerMatrix4x4 transform =
852 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>(
853 translation, PixelCastJustification::ScreenIsParentLayerForRoot));
854
855 transforms.AppendElement(
856 wr::ToWrTransformProperty(info.mScrollbarAnimationId, transform));
857 }
858 }
859
860 for (const FixedPositionInfo& info : mFixedPositionInfo) {
861 MOZ_ASSERT(info.mFixedPositionAnimationId.isSome());
862 if (!IsFixedToRootContent(info, lock)) {
863 continue;
864 }
865
866 ScreenPoint translation = apz::ComputeFixedMarginsOffset(
867 GetCompositorFixedLayerMargins(lock), info.mFixedPosSides,
868 mGeckoFixedLayerMargins);
869
870 LayerToParentLayerMatrix4x4 transform =
871 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>(
872 translation, PixelCastJustification::ScreenIsParentLayerForRoot));
873
874 transforms.AppendElement(
875 wr::ToWrTransformProperty(*info.mFixedPositionAnimationId, transform));
876 }
877
878 for (const StickyPositionInfo& info : mStickyPositionInfo) {
879 MOZ_ASSERT(info.mStickyPositionAnimationId.isSome());
880 SideBits sides = SidesStuckToRootContent(info, lock);
881 if (sides == SideBits::eNone) {
882 continue;
883 }
884
885 ScreenPoint translation = apz::ComputeFixedMarginsOffset(
886 GetCompositorFixedLayerMargins(lock), sides,
887 // For sticky layers, we don't need to factor
888 // mGeckoFixedLayerMargins because Gecko doesn't shift the
889 // position of sticky elements for dynamic toolbar movements.
890 ScreenMargin());
891
892 LayerToParentLayerMatrix4x4 transform =
893 LayerToParentLayerMatrix4x4::Translation(ViewAs<ParentLayerPixel>(
894 translation, PixelCastJustification::ScreenIsParentLayerForRoot));
895
896 transforms.AppendElement(
897 wr::ToWrTransformProperty(*info.mStickyPositionAnimationId, transform));
898 }
899
900 aTxn.AppendTransformProperties(transforms);
901 }
902
AdvanceAnimations(const SampleTime & aSampleTime)903 bool APZCTreeManager::AdvanceAnimations(const SampleTime& aSampleTime) {
904 MutexAutoLock lock(mMapLock);
905 return AdvanceAnimationsInternal(lock, aSampleTime);
906 }
907
ComputeClippedCompositionBounds(const MutexAutoLock & aProofOfMapLock,ClippedCompositionBoundsMap & aDestMap,ScrollableLayerGuid aGuid)908 ParentLayerRect APZCTreeManager::ComputeClippedCompositionBounds(
909 const MutexAutoLock& aProofOfMapLock, ClippedCompositionBoundsMap& aDestMap,
910 ScrollableLayerGuid aGuid) {
911 if (auto iter = aDestMap.find(aGuid); iter != aDestMap.end()) {
912 // We already computed it for this one, early-exit. This might happen
913 // because on a later iteration of mApzcMap we might encounter an ancestor
914 // of an APZC that we processed on an earlier iteration. In this case we
915 // would have computed the ancestor's clipped composition bounds when
916 // recursing up on the earlier iteration.
917 return iter->second;
918 }
919
920 ParentLayerRect bounds = mApzcMap[aGuid].apzc->GetCompositionBounds();
921 const auto& mapEntry = mApzcMap.find(aGuid);
922 MOZ_ASSERT(mapEntry != mApzcMap.end());
923 if (mapEntry->second.parent.isNothing()) {
924 // Recursion base case, where the APZC with guid `aGuid` has no parent.
925 // In this case, we don't need to clip `bounds` any further and can just
926 // early exit.
927 aDestMap.emplace(aGuid, bounds);
928 return bounds;
929 }
930
931 ScrollableLayerGuid parentGuid = mapEntry->second.parent.value();
932 auto parentBoundsEntry = aDestMap.find(parentGuid);
933 // If aDestMap doesn't contain the parent entry yet, we recurse to compute
934 // that one first.
935 ParentLayerRect parentClippedBounds =
936 (parentBoundsEntry == aDestMap.end())
937 ? ComputeClippedCompositionBounds(aProofOfMapLock, aDestMap,
938 parentGuid)
939 : parentBoundsEntry->second;
940
941 // The parent layer's async transform applies to the current layer to take
942 // `bounds` into the same coordinate space as `parentClippedBounds`. However,
943 // we're going to do the inverse operation and unapply this transform to
944 // `parentClippedBounds` to bring it into the same coordinate space as
945 // `bounds`.
946 AsyncTransform appliesToLayer =
947 mApzcMap[parentGuid].apzc->GetCurrentAsyncTransform(
948 AsyncPanZoomController::eForCompositing);
949
950 // Do the unapplication
951 LayerRect parentClippedBoundsInParentLayerSpace =
952 (parentClippedBounds - appliesToLayer.mTranslation) /
953 appliesToLayer.mScale;
954
955 // And then clip `bounds` by the parent's comp bounds in the current space.
956 bounds = bounds.Intersect(
957 ViewAs<ParentLayerPixel>(parentClippedBoundsInParentLayerSpace,
958 PixelCastJustification::MovingDownToChildren));
959
960 // Done!
961 aDestMap.emplace(aGuid, bounds);
962 return bounds;
963 }
964
AdvanceAnimationsInternal(const MutexAutoLock & aProofOfMapLock,const SampleTime & aSampleTime)965 bool APZCTreeManager::AdvanceAnimationsInternal(
966 const MutexAutoLock& aProofOfMapLock, const SampleTime& aSampleTime) {
967 ClippedCompositionBoundsMap clippedCompBounds;
968 bool activeAnimations = false;
969 for (const auto& mapping : mApzcMap) {
970 AsyncPanZoomController* apzc = mapping.second.apzc;
971 // Note that this call is recursive, but it early-exits if called again
972 // with the same guid. So this loop is still amortized O(n) with respect to
973 // the number of APZCs.
974 ParentLayerRect clippedBounds = ComputeClippedCompositionBounds(
975 aProofOfMapLock, clippedCompBounds, mapping.first);
976
977 apzc->ReportCheckerboard(aSampleTime, clippedBounds);
978 activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
979 }
980 return activeAnimations;
981 }
982
983 // Compute the clip region to be used for a layer with an APZC. This function
984 // is only called for layers which actually have scrollable metrics and an APZC.
985 template <class ScrollNode>
ComputeClipRegion(const LayersId & aLayersId,const ScrollNode & aLayer)986 Maybe<ParentLayerIntRegion> APZCTreeManager::ComputeClipRegion(
987 const LayersId& aLayersId, const ScrollNode& aLayer) {
988 Maybe<ParentLayerIntRegion> clipRegion;
989 if (aLayer.GetClipRect()) {
990 clipRegion.emplace(*aLayer.GetClipRect());
991 } else if (aLayer.Metrics().IsRootContent() &&
992 mAsyncZoomContainerSubtree == Some(aLayersId)) {
993 // If we are using containerless scrolling, part of the root content
994 // layers' async transform has been lifted to the async zoom container
995 // layer. The composition bounds clip, which applies after the async
996 // transform, needs to be lifted too. Layout code already takes care of
997 // this for us, we just need to not mess it up by introducing a
998 // composition bounds clip here, so we leave the clip empty.
999 } else {
1000 // if there is no clip on this layer (which should only happen for the
1001 // root scrollable layer in a process, or for some of the LayerMetrics
1002 // expansions of a multi-metrics layer), fall back to using the comp
1003 // bounds which should be equivalent.
1004 clipRegion.emplace(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
1005 }
1006
1007 return clipRegion;
1008 }
1009
1010 template <class ScrollNode>
PrintAPZCInfo(const ScrollNode & aLayer,const AsyncPanZoomController * apzc)1011 void APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
1012 const AsyncPanZoomController* apzc) {
1013 const FrameMetrics& metrics = aLayer.Metrics();
1014 std::stringstream guidStr;
1015 guidStr << apzc->GetGuid();
1016 mApzcTreeLog << "APZC " << guidStr.str()
1017 << "\tcb=" << metrics.GetCompositionBounds()
1018 << "\tsr=" << metrics.GetScrollableRect()
1019 << (metrics.IsScrollInfoLayer() ? "\tscrollinfo" : "")
1020 << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
1021 << aLayer.Metadata().GetContentDescription().get();
1022 }
1023
AttachNodeToTree(HitTestingTreeNode * aNode,HitTestingTreeNode * aParent,HitTestingTreeNode * aNextSibling)1024 void APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
1025 HitTestingTreeNode* aParent,
1026 HitTestingTreeNode* aNextSibling) {
1027 if (aNextSibling) {
1028 aNextSibling->SetPrevSibling(aNode);
1029 } else if (aParent) {
1030 aParent->SetLastChild(aNode);
1031 } else {
1032 MOZ_ASSERT(!mRootNode);
1033 mRootNode = aNode;
1034 aNode->MakeRoot();
1035 }
1036 }
1037
1038 template <class ScrollNode>
GetEventRegions(const ScrollNode & aLayer)1039 static EventRegions GetEventRegions(const ScrollNode& aLayer) {
1040 if (aLayer.Metrics().IsScrollInfoLayer()) {
1041 ParentLayerIntRect compositionBounds(
1042 RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
1043 nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
1044 EventRegions eventRegions(hitRegion);
1045 eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
1046 return eventRegions;
1047 }
1048 return aLayer.GetEventRegions();
1049 }
1050
RecycleOrCreateNode(const RecursiveMutexAutoLock & aProofOfTreeLock,TreeBuildingState & aState,AsyncPanZoomController * aApzc,LayersId aLayersId)1051 already_AddRefed<HitTestingTreeNode> APZCTreeManager::RecycleOrCreateNode(
1052 const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState,
1053 AsyncPanZoomController* aApzc, LayersId aLayersId) {
1054 // Find a node without an APZC and return it. Note that unless the layer tree
1055 // actually changes, this loop should generally do an early-return on the
1056 // first iteration, so it should be cheap in the common case.
1057 for (int32_t i = aState.mNodesToDestroy.Length() - 1; i >= 0; i--) {
1058 RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
1059 if (node->IsRecyclable(aProofOfTreeLock)) {
1060 aState.mNodesToDestroy.RemoveElementAt(i);
1061 node->RecycleWith(aProofOfTreeLock, aApzc, aLayersId);
1062 return node.forget();
1063 }
1064 }
1065 RefPtr<HitTestingTreeNode> node =
1066 new HitTestingTreeNode(aApzc, false, aLayersId);
1067 return node.forget();
1068 }
1069
StartScrollbarDrag(const ScrollableLayerGuid & aGuid,const AsyncDragMetrics & aDragMetrics)1070 void APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
1071 const AsyncDragMetrics& aDragMetrics) {
1072 APZThreadUtils::AssertOnControllerThread();
1073
1074 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1075 if (!apzc) {
1076 NotifyScrollbarDragRejected(aGuid);
1077 return;
1078 }
1079
1080 uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
1081 mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
1082 }
1083
StartAutoscroll(const ScrollableLayerGuid & aGuid,const ScreenPoint & aAnchorLocation)1084 bool APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid,
1085 const ScreenPoint& aAnchorLocation) {
1086 APZThreadUtils::AssertOnControllerThread();
1087
1088 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
1089 if (!apzc) {
1090 if (XRE_IsGPUProcess()) {
1091 // If we're in the compositor process, the "return false" will be
1092 // ignored because the query comes over the PAPZCTreeManager protocol
1093 // via an async message. In this case, send an explicit rejection
1094 // message to content.
1095 NotifyAutoscrollRejected(aGuid);
1096 }
1097 return false;
1098 }
1099
1100 apzc->StartAutoscroll(aAnchorLocation);
1101 return true;
1102 }
1103
StopAutoscroll(const ScrollableLayerGuid & aGuid)1104 void APZCTreeManager::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
1105 APZThreadUtils::AssertOnControllerThread();
1106
1107 if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
1108 apzc->StopAutoscroll();
1109 }
1110 }
1111
NotifyScrollbarDragInitiated(uint64_t aDragBlockId,const ScrollableLayerGuid & aGuid,ScrollDirection aDirection) const1112 void APZCTreeManager::NotifyScrollbarDragInitiated(
1113 uint64_t aDragBlockId, const ScrollableLayerGuid& aGuid,
1114 ScrollDirection aDirection) const {
1115 RefPtr<GeckoContentController> controller =
1116 GetContentController(aGuid.mLayersId);
1117 if (controller) {
1118 controller->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aGuid.mScrollId,
1119 aDirection);
1120 }
1121 }
1122
NotifyScrollbarDragRejected(const ScrollableLayerGuid & aGuid) const1123 void APZCTreeManager::NotifyScrollbarDragRejected(
1124 const ScrollableLayerGuid& aGuid) const {
1125 RefPtr<GeckoContentController> controller =
1126 GetContentController(aGuid.mLayersId);
1127 if (controller) {
1128 controller->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
1129 }
1130 }
1131
NotifyAutoscrollRejected(const ScrollableLayerGuid & aGuid) const1132 void APZCTreeManager::NotifyAutoscrollRejected(
1133 const ScrollableLayerGuid& aGuid) const {
1134 RefPtr<GeckoContentController> controller =
1135 GetContentController(aGuid.mLayersId);
1136 MOZ_ASSERT(controller);
1137 controller->NotifyAsyncAutoscrollRejected(aGuid.mScrollId);
1138 }
1139
1140 template <class ScrollNode>
SetHitTestData(HitTestingTreeNode * aNode,const ScrollNode & aLayer,const Maybe<ParentLayerIntRegion> & aClipRegion,const EventRegionsOverride & aOverrideFlags)1141 void SetHitTestData(HitTestingTreeNode* aNode, const ScrollNode& aLayer,
1142 const Maybe<ParentLayerIntRegion>& aClipRegion,
1143 const EventRegionsOverride& aOverrideFlags) {
1144 aNode->SetHitTestData(GetEventRegions(aLayer), aLayer.GetVisibleRegion(),
1145 aLayer.GetRemoteDocumentSize(),
1146 aLayer.GetTransformTyped(), aClipRegion, aOverrideFlags,
1147 aLayer.IsBackfaceHidden(),
1148 aLayer.GetAsyncZoomContainerId());
1149 }
1150
1151 template <class ScrollNode>
PrepareNodeForLayer(const RecursiveMutexAutoLock & aProofOfTreeLock,const ScrollNode & aLayer,const FrameMetrics & aMetrics,LayersId aLayersId,const Maybe<ZoomConstraints> & aZoomConstraints,const AncestorTransform & aAncestorTransform,HitTestingTreeNode * aParent,HitTestingTreeNode * aNextSibling,TreeBuildingState & aState)1152 HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(
1153 const RecursiveMutexAutoLock& aProofOfTreeLock, const ScrollNode& aLayer,
1154 const FrameMetrics& aMetrics, LayersId aLayersId,
1155 const Maybe<ZoomConstraints>& aZoomConstraints,
1156 const AncestorTransform& aAncestorTransform, HitTestingTreeNode* aParent,
1157 HitTestingTreeNode* aNextSibling, TreeBuildingState& aState) {
1158 bool needsApzc = true;
1159 if (!aMetrics.IsScrollable()) {
1160 needsApzc = false;
1161 }
1162
1163 // XXX: As a future optimization we can probably stick these things on the
1164 // TreeBuildingState, and update them as we change layers id during the
1165 // traversal
1166 RefPtr<GeckoContentController> geckoContentController;
1167 RefPtr<MetricsSharingController> crossProcessSharingController;
1168 CompositorBridgeParent::CallWithIndirectShadowTree(
1169 aLayersId, [&](LayerTreeState& lts) -> void {
1170 geckoContentController = lts.mController;
1171 crossProcessSharingController = lts.CrossProcessSharingController();
1172 });
1173
1174 if (!geckoContentController) {
1175 needsApzc = false;
1176 }
1177
1178 bool parentHasPerspective = aState.mParentHasPerspective.top();
1179
1180 if (Maybe<uint64_t> zoomAnimationId = aLayer.GetZoomAnimationId()) {
1181 aState.mZoomAnimationId = zoomAnimationId;
1182 }
1183
1184 RefPtr<HitTestingTreeNode> node = nullptr;
1185 if (!needsApzc) {
1186 // Note: if layer properties must be propagated to nodes, RecvUpdate in
1187 // LayerTransactionParent.cpp must ensure that APZ will be notified
1188 // when those properties change.
1189 node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId);
1190 AttachNodeToTree(node, aParent, aNextSibling);
1191 SetHitTestData(node, aLayer,
1192 (!parentHasPerspective && aLayer.GetClipRect())
1193 ? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
1194 : Nothing(),
1195 aState.mOverrideFlags.top());
1196 node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
1197 aLayer.GetScrollbarData());
1198 node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(),
1199 aLayer.GetFixedPositionSides(),
1200 aLayer.GetFixedPositionAnimationId());
1201 node->SetStickyPosData(aLayer.GetStickyScrollContainerId(),
1202 aLayer.GetStickyScrollRangeOuter(),
1203 aLayer.GetStickyScrollRangeInner(),
1204 aLayer.GetStickyPositionAnimationId());
1205 return node;
1206 }
1207
1208 AsyncPanZoomController* apzc = nullptr;
1209 // If we get here, aLayer is a scrollable layer and somebody
1210 // has registered a GeckoContentController for it, so we need to ensure
1211 // it has an APZC instance to manage its scrolling.
1212
1213 // aState.mApzcMap allows reusing the exact same APZC instance for different
1214 // layers with the same FrameMetrics data. This is needed because in some
1215 // cases content that is supposed to scroll together is split into multiple
1216 // layers because of e.g. non-scrolling content interleaved in z-index order.
1217 ScrollableLayerGuid guid(aLayersId, aMetrics.GetPresShellId(),
1218 aMetrics.GetScrollId());
1219 auto insertResult = aState.mApzcMap.insert(std::make_pair(
1220 guid,
1221 ApzcMapData{static_cast<AsyncPanZoomController*>(nullptr), Nothing()}));
1222 if (!insertResult.second) {
1223 apzc = insertResult.first->second.apzc;
1224 PrintAPZCInfo(aLayer, apzc);
1225 }
1226 APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64
1227 "\n",
1228 apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId);
1229
1230 // If we haven't encountered a layer already with the same metrics, then we
1231 // need to do the full reuse-or-make-an-APZC algorithm, which is contained
1232 // inside the block below.
1233 if (apzc == nullptr) {
1234 apzc = aLayer.GetApzc();
1235
1236 // If the content represented by the scrollable layer has changed (which may
1237 // be possible because of DLBI heuristics) then we don't want to keep using
1238 // the same old APZC for the new content. Also, when reparenting a tab into
1239 // a new window a layer might get moved to a different layer tree with a
1240 // different APZCTreeManager. In these cases we don't want to reuse the same
1241 // APZC, so null it out so we run through the code to find another one or
1242 // create one.
1243 if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
1244 apzc = nullptr;
1245 }
1246
1247 // See if we can find an APZC from the previous tree that matches the
1248 // ScrollableLayerGuid from this layer. If there is one, then we know that
1249 // the layout of the page changed causing the layer tree to be rebuilt, but
1250 // the underlying content for the APZC is still there somewhere. Therefore,
1251 // we want to find the APZC instance and continue using it here.
1252 //
1253 // We particularly want to find the primary-holder node from the previous
1254 // tree that matches, because we don't want that node to get destroyed. If
1255 // it does get destroyed, then the APZC will get destroyed along with it by
1256 // definition, but we want to keep that APZC around in the new tree.
1257 // We leave non-primary-holder nodes in the destroy list because we don't
1258 // care about those nodes getting destroyed.
1259 for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
1260 RefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
1261 if (n->IsPrimaryHolder() && n->GetApzc() && n->GetApzc()->Matches(guid)) {
1262 node = n;
1263 if (apzc != nullptr) {
1264 // If there is an APZC already then it should match the one from the
1265 // old primary-holder node
1266 MOZ_ASSERT(apzc == node->GetApzc());
1267 }
1268 apzc = node->GetApzc();
1269 break;
1270 }
1271 }
1272
1273 // The APZC we get off the layer may have been destroyed previously if the
1274 // layer was inactive or omitted from the layer tree for whatever reason
1275 // from a layers update. If it later comes back it will have a reference to
1276 // a destroyed APZC and so we need to throw that out and make a new one.
1277 bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
1278 if (newApzc) {
1279 apzc = NewAPZCInstance(aLayersId, geckoContentController);
1280 apzc->SetCompositorController(aState.mCompositorController.get());
1281 if (crossProcessSharingController) {
1282 apzc->SetMetricsSharingController(crossProcessSharingController);
1283 } else {
1284 apzc->SetMetricsSharingController(
1285 aState.mInProcessSharingController.get());
1286 }
1287 MOZ_ASSERT(node == nullptr);
1288 node = new HitTestingTreeNode(apzc, true, aLayersId);
1289 } else {
1290 // If we are re-using a node for this layer clear the tree pointers
1291 // so that it doesn't continue pointing to nodes that might no longer
1292 // be in the tree. These pointers will get reset properly as we continue
1293 // building the tree. Also remove it from the set of nodes that are going
1294 // to be destroyed, because it's going to remain active.
1295 aState.mNodesToDestroy.RemoveElement(node);
1296 node->SetPrevSibling(nullptr);
1297 node->SetLastChild(nullptr);
1298 }
1299
1300 if (aMetrics.IsRootContent()) {
1301 apzc->SetZoomAnimationId(aState.mZoomAnimationId);
1302 aState.mZoomAnimationId = Nothing();
1303 }
1304
1305 APZCTM_LOG(
1306 "Using APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n",
1307 apzc, aLayer.GetLayer(), uint64_t(aLayersId), aMetrics.GetScrollId());
1308
1309 apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
1310 aLayersId == aState.mOriginatingLayersId);
1311
1312 // Since this is the first time we are encountering an APZC with this guid,
1313 // the node holding it must be the primary holder. It may be newly-created
1314 // or not, depending on whether it went through the newApzc branch above.
1315 MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() &&
1316 node->GetApzc()->Matches(guid));
1317
1318 Maybe<ParentLayerIntRegion> clipRegion =
1319 parentHasPerspective ? Nothing() : ComputeClipRegion(aLayersId, aLayer);
1320 SetHitTestData(node, aLayer, clipRegion, aState.mOverrideFlags.top());
1321 apzc->SetAncestorTransform(aAncestorTransform);
1322
1323 PrintAPZCInfo(aLayer, apzc);
1324
1325 // Bind the APZC instance into the tree of APZCs
1326 AttachNodeToTree(node, aParent, aNextSibling);
1327
1328 // For testing, log the parent scroll id of every APZC that has a
1329 // parent. This allows test code to reconstruct the APZC tree.
1330 // Note that we currently only do this for APZCs in the layer tree
1331 // that originated the update, because the only identifying information
1332 // we are logging about APZCs is the scroll id, and otherwise we could
1333 // confuse APZCs from different layer trees with the same scroll id.
1334 if (aLayersId == aState.mOriginatingLayersId) {
1335 if (apzc->HasNoParentWithSameLayersId()) {
1336 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1337 "hasNoParentWithSameLayersId", true);
1338 } else {
1339 MOZ_ASSERT(apzc->GetParent());
1340 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1341 "parentScrollId",
1342 apzc->GetParent()->GetGuid().mScrollId);
1343 }
1344 if (aMetrics.IsRootContent()) {
1345 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "isRootContent",
1346 true);
1347 }
1348 // Note that the async scroll offset is in ParentLayer pixels
1349 aState.mPaintLogger.LogTestData(
1350 aMetrics.GetScrollId(), "asyncScrollOffset",
1351 apzc->GetCurrentAsyncScrollOffset(
1352 AsyncPanZoomController::eForHitTesting));
1353 aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
1354 "hasAsyncKeyScrolled",
1355 apzc->TestHasAsyncKeyScrolled());
1356 }
1357
1358 // We must update the zoom constraints even if the apzc isn't new because it
1359 // might have moved.
1360 if (node->IsPrimaryHolder()) {
1361 if (aZoomConstraints) {
1362 apzc->UpdateZoomConstraints(*aZoomConstraints);
1363
1364 #ifdef DEBUG
1365 auto it = mZoomConstraints.find(guid);
1366 if (it != mZoomConstraints.end()) {
1367 MOZ_ASSERT(it->second == *aZoomConstraints);
1368 }
1369 } else {
1370 // We'd like to assert these things (if the first doesn't hold then at
1371 // least the second) but neither are not true because xul root content
1372 // gets zoomable zoom constraints, but which is not zoomable because it
1373 // doesn't have a root scroll frame.
1374 // clang-format off
1375 // MOZ_ASSERT(mZoomConstraints.find(guid) == mZoomConstraints.end());
1376 // auto it = mZoomConstraints.find(guid);
1377 // if (it != mZoomConstraints.end()) {
1378 // MOZ_ASSERT(!it->second.mAllowZoom && !it->second.mAllowDoubleTapZoom);
1379 // }
1380 // clang-format on
1381 #endif
1382 }
1383 }
1384
1385 // Add a guid -> APZC mapping for the newly created APZC.
1386 insertResult.first->second.apzc = apzc;
1387 } else {
1388 // We already built an APZC earlier in this tree walk, but we have another
1389 // layer now that will also be using that APZC. The hit-test region on the
1390 // APZC needs to be updated to deal with the new layer's hit region.
1391
1392 node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId);
1393 AttachNodeToTree(node, aParent, aNextSibling);
1394
1395 // Even though different layers associated with a given APZC may be at
1396 // different levels in the layer tree (e.g. one being an uncle of another),
1397 // we require from Layout that the CSS transforms up to their common
1398 // ancestor be roughly the same. There are cases in which the transforms
1399 // are not exactly the same, for example if the parent is container layer
1400 // for an opacity, and this container layer has a resolution-induced scale
1401 // as its base transform and a prescale that is supposed to undo that scale.
1402 // Due to floating point inaccuracies those transforms can end up not quite
1403 // canceling each other. That's why we're using a fuzzy comparison here
1404 // instead of an exact one.
1405 // In addition, two ancestor transforms are allowed to differ if one of
1406 // them contains a perspective transform component and the other does not.
1407 // This represents situations where some content in a scrollable frame
1408 // is subject to a perspective transform and other content does not.
1409 // In such cases, go with the one that does not include the perspective
1410 // component; the perspective transform is remembered and applied to the
1411 // children instead.
1412 if (!aAncestorTransform.CombinedTransform().FuzzyEqualsMultiplicative(
1413 apzc->GetAncestorTransform())) {
1414 typedef TreeBuildingState::DeferredTransformMap::value_type PairType;
1415 if (!aAncestorTransform.ContainsPerspectiveTransform() &&
1416 !apzc->AncestorTransformContainsPerspective()) {
1417 NS_ASSERTION(false,
1418 "Two layers that scroll together have different ancestor "
1419 "transforms");
1420 } else if (!aAncestorTransform.ContainsPerspectiveTransform()) {
1421 aState.mPerspectiveTransformsDeferredToChildren.insert(
1422 PairType{apzc, apzc->GetAncestorTransformPerspective()});
1423 apzc->SetAncestorTransform(aAncestorTransform);
1424 } else {
1425 aState.mPerspectiveTransformsDeferredToChildren.insert(
1426 PairType{apzc, aAncestorTransform.GetPerspectiveTransform()});
1427 }
1428 }
1429
1430 Maybe<ParentLayerIntRegion> clipRegion =
1431 parentHasPerspective ? Nothing() : ComputeClipRegion(aLayersId, aLayer);
1432 SetHitTestData(node, aLayer, clipRegion, aState.mOverrideFlags.top());
1433 }
1434
1435 // Note: if layer properties must be propagated to nodes, RecvUpdate in
1436 // LayerTransactionParent.cpp must ensure that APZ will be notified
1437 // when those properties change.
1438 node->SetScrollbarData(aLayer.GetScrollbarAnimationId(),
1439 aLayer.GetScrollbarData());
1440 node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId(),
1441 aLayer.GetFixedPositionSides(),
1442 aLayer.GetFixedPositionAnimationId());
1443 node->SetStickyPosData(aLayer.GetStickyScrollContainerId(),
1444 aLayer.GetStickyScrollRangeOuter(),
1445 aLayer.GetStickyScrollRangeInner(),
1446 aLayer.GetStickyPositionAnimationId());
1447 return node;
1448 }
1449
1450 template <typename PanGestureOrScrollWheelInput>
WillHandleInput(const PanGestureOrScrollWheelInput & aPanInput)1451 static bool WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput) {
1452 if (!XRE_IsParentProcess() || !NS_IsMainThread()) {
1453 return true;
1454 }
1455
1456 WidgetWheelEvent wheelEvent = aPanInput.ToWidgetEvent(nullptr);
1457 return APZInputBridge::ActionForWheelEvent(&wheelEvent).isSome();
1458 }
1459
1460 /*static*/
FlushApzRepaints(LayersId aLayersId)1461 void APZCTreeManager::FlushApzRepaints(LayersId aLayersId) {
1462 // Previously, paints were throttled and therefore this method was used to
1463 // ensure any pending paints were flushed. Now, paints are flushed
1464 // immediately, so it is safe to simply send a notification now.
1465 APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n",
1466 uint64_t(aLayersId));
1467 RefPtr<GeckoContentController> controller = GetContentController(aLayersId);
1468 #ifndef MOZ_WIDGET_ANDROID
1469 // On Android, this code is run in production and may actually get a nullptr
1470 // controller here. On other platforms this code is test-only and should never
1471 // get a nullptr.
1472 MOZ_ASSERT(controller);
1473 #endif
1474 if (controller) {
1475 controller->DispatchToRepaintThread(NewRunnableMethod(
1476 "layers::GeckoContentController::NotifyFlushComplete", controller,
1477 &GeckoContentController::NotifyFlushComplete));
1478 }
1479 }
1480
MarkAsDetached(LayersId aLayersId)1481 void APZCTreeManager::MarkAsDetached(LayersId aLayersId) {
1482 RecursiveMutexAutoLock lock(mTreeLock);
1483 mDetachedLayersIds.insert(aLayersId);
1484 }
1485
HasNonLockModifier(Modifiers aModifiers)1486 static bool HasNonLockModifier(Modifiers aModifiers) {
1487 return (aModifiers & (MODIFIER_ALT | MODIFIER_ALTGRAPH | MODIFIER_CONTROL |
1488 MODIFIER_FN | MODIFIER_META | MODIFIER_SHIFT |
1489 MODIFIER_SYMBOL | MODIFIER_OS)) != 0;
1490 }
1491
ReceiveInputEvent(InputData & aEvent)1492 APZEventResult APZCTreeManager::ReceiveInputEvent(InputData& aEvent) {
1493 APZThreadUtils::AssertOnControllerThread();
1494 InputHandlingState state{aEvent};
1495
1496 // Use a RAII class for updating the focus sequence number of this event
1497 AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
1498
1499 switch (aEvent.mInputType) {
1500 case MULTITOUCH_INPUT: {
1501 MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
1502 ProcessTouchInput(state, touchInput);
1503 break;
1504 }
1505 case MOUSE_INPUT: {
1506 MouseInput& mouseInput = aEvent.AsMouseInput();
1507 mouseInput.mHandledByAPZ = true;
1508
1509 SetCurrentMousePosition(mouseInput.mOrigin);
1510
1511 bool startsDrag = DragTracker::StartsDrag(mouseInput);
1512 if (startsDrag) {
1513 // If this is the start of a drag we need to unambiguously know if it's
1514 // going to land on a scrollbar or not. We can't apply an untransform
1515 // here without knowing that, so we need to ensure the untransform is
1516 // a no-op.
1517 FlushRepaintsToClearScreenToGeckoTransform();
1518 }
1519
1520 state.mHit = GetTargetAPZC(mouseInput.mOrigin);
1521 bool hitScrollbar = (bool)state.mHit.mScrollbarNode;
1522
1523 // When the mouse is outside the window we still want to handle dragging
1524 // but we won't find an APZC. Fallback to root APZC then.
1525 { // scope lock
1526 RecursiveMutexAutoLock lock(mTreeLock);
1527 if (!state.mHit.mTargetApzc && mRootNode) {
1528 state.mHit.mTargetApzc = mRootNode->GetApzc();
1529 }
1530 }
1531
1532 if (state.mHit.mTargetApzc) {
1533 if (StaticPrefs::apz_test_logging_enabled() &&
1534 mouseInput.mType == MouseInput::MOUSE_HITTEST) {
1535 ScrollableLayerGuid guid = state.mHit.mTargetApzc->GetGuid();
1536
1537 MutexAutoLock lock(mTestDataLock);
1538 auto it = mTestData.find(guid.mLayersId);
1539 MOZ_ASSERT(it != mTestData.end());
1540 it->second->RecordHitResult(mouseInput.mOrigin, state.mHit.mHitResult,
1541 guid.mLayersId, guid.mScrollId);
1542 }
1543
1544 TargetConfirmationFlags confFlags{state.mHit.mHitResult};
1545 state.mResult = mInputQueue->ReceiveInputEvent(state.mHit.mTargetApzc,
1546 confFlags, mouseInput);
1547
1548 // If we're starting an async scrollbar drag
1549 bool apzDragEnabled = StaticPrefs::apz_drag_enabled();
1550 if (apzDragEnabled && startsDrag && state.mHit.mScrollbarNode &&
1551 state.mHit.mScrollbarNode->IsScrollThumbNode() &&
1552 state.mHit.mScrollbarNode->GetScrollbarData()
1553 .mThumbIsAsyncDraggable) {
1554 SetupScrollbarDrag(mouseInput, state.mHit.mScrollbarNode,
1555 state.mHit.mTargetApzc.get());
1556 }
1557
1558 if (state.mResult.GetStatus() == nsEventStatus_eConsumeDoDefault) {
1559 // This input event is part of a drag block, so whether or not it is
1560 // directed at a scrollbar depends on whether the drag block started
1561 // on a scrollbar.
1562 hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
1563 }
1564
1565 if (!hitScrollbar) {
1566 // The input was not targeted at a scrollbar, so we untransform it
1567 // like we do for other content. Scrollbars are "special" because they
1568 // have special handling in AsyncCompositionManager when resolution is
1569 // applied. TODO: we should find a better way to deal with this.
1570 ScreenToParentLayerMatrix4x4 transformToApzc =
1571 GetScreenToApzcTransform(state.mHit.mTargetApzc);
1572 ParentLayerToScreenMatrix4x4 transformToGecko =
1573 GetApzcToGeckoTransform(state.mHit.mTargetApzc);
1574 ScreenToScreenMatrix4x4 outTransform =
1575 transformToApzc * transformToGecko;
1576 Maybe<ScreenPoint> untransformedRefPoint =
1577 UntransformBy(outTransform, mouseInput.mOrigin);
1578 if (untransformedRefPoint) {
1579 mouseInput.mOrigin = *untransformedRefPoint;
1580 }
1581 } else {
1582 // Likewise, if the input was targeted at a scrollbar, we don't want
1583 // to apply the callback transform in the main thread, so we remove
1584 // the scrollid from the guid. We need to keep the layersId intact so
1585 // that the response from the child process doesn't get discarded.
1586 state.mResult.mTargetGuid.mScrollId =
1587 ScrollableLayerGuid::NULL_SCROLL_ID;
1588 }
1589 }
1590 break;
1591 }
1592 case SCROLLWHEEL_INPUT: {
1593 FlushRepaintsToClearScreenToGeckoTransform();
1594
1595 // Do this before early return for Fission hit testing.
1596 ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
1597 state.mHit = GetTargetAPZC(wheelInput.mOrigin);
1598
1599 wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
1600 if (!wheelInput.mHandledByAPZ) {
1601 return state.Finish();
1602 }
1603
1604 if (state.mHit.mTargetApzc) {
1605 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit);
1606
1607 if (wheelInput.mAPZAction == APZWheelAction::PinchZoom) {
1608 // The mousewheel may have hit a subframe, but we want to send the
1609 // pinch-zoom events to the root-content APZC.
1610 {
1611 RecursiveMutexAutoLock lock(mTreeLock);
1612 state.mHit.mTargetApzc = FindRootContentApzcForLayersId(
1613 state.mHit.mTargetApzc->GetLayersId());
1614 }
1615 if (state.mHit.mTargetApzc) {
1616 SynthesizePinchGestureFromMouseWheel(wheelInput,
1617 state.mHit.mTargetApzc);
1618 }
1619 state.mResult.SetStatusAsConsumeNoDefault();
1620 return state.Finish();
1621 }
1622
1623 MOZ_ASSERT(wheelInput.mAPZAction == APZWheelAction::Scroll);
1624
1625 // For wheel events, the call to ReceiveInputEvent below may result in
1626 // scrolling, which changes the async transform. However, the event we
1627 // want to pass to gecko should be the pre-scroll event coordinates,
1628 // transformed into the gecko space. (pre-scroll because the mouse
1629 // cursor is stationary during wheel scrolling, unlike touchmove
1630 // events). Since we just flushed the pending repaints the transform to
1631 // gecko space should only consist of overscroll-cancelling transforms.
1632 ScreenToScreenMatrix4x4 transformToGecko =
1633 GetScreenToApzcTransform(state.mHit.mTargetApzc) *
1634 GetApzcToGeckoTransform(state.mHit.mTargetApzc);
1635 Maybe<ScreenPoint> untransformedOrigin =
1636 UntransformBy(transformToGecko, wheelInput.mOrigin);
1637
1638 if (!untransformedOrigin) {
1639 return state.Finish();
1640 }
1641
1642 state.mResult = mInputQueue->ReceiveInputEvent(
1643 state.mHit.mTargetApzc,
1644 TargetConfirmationFlags{state.mHit.mHitResult}, wheelInput);
1645
1646 // Update the out-parameters so they are what the caller expects.
1647 wheelInput.mOrigin = *untransformedOrigin;
1648 }
1649 break;
1650 }
1651 case PANGESTURE_INPUT: {
1652 FlushRepaintsToClearScreenToGeckoTransform();
1653
1654 // Do this before early return for Fission hit testing.
1655 PanGestureInput& panInput = aEvent.AsPanGestureInput();
1656 state.mHit = GetTargetAPZC(panInput.mPanStartPoint);
1657
1658 panInput.mHandledByAPZ = WillHandleInput(panInput);
1659 if (!panInput.mHandledByAPZ) {
1660 if (mInputQueue->GetCurrentPanGestureBlock()) {
1661 if (state.mHit.mTargetApzc &&
1662 (panInput.mType == PanGestureInput::PANGESTURE_END ||
1663 panInput.mType == PanGestureInput::PANGESTURE_CANCELLED)) {
1664 // If we've already been processing a pan gesture in an APZC but
1665 // fall into this _if_ branch, which means this pan-end or
1666 // pan-cancelled event will not be proccessed in the APZC, send a
1667 // pan-interrupted event to stop any on-going work for the pan
1668 // gesture, otherwise we will get stuck at an intermidiate state
1669 // becasue we might no longer receive any events which will be
1670 // handled by the APZC.
1671 PanGestureInput panInterrupted(
1672 PanGestureInput::PANGESTURE_INTERRUPTED, panInput.mTime,
1673 panInput.mTimeStamp, panInput.mPanStartPoint,
1674 panInput.mPanDisplacement, panInput.modifiers);
1675 Unused << mInputQueue->ReceiveInputEvent(
1676 state.mHit.mTargetApzc,
1677 TargetConfirmationFlags{state.mHit.mHitResult}, panInterrupted);
1678 }
1679 }
1680 return state.Finish();
1681 }
1682
1683 // If/when we enable support for pan inputs off-main-thread, we'll need
1684 // to duplicate this EventStateManager code or something. See the call to
1685 // GetUserPrefsForWheelEvent in IAPZCTreeManager.cpp for why these fields
1686 // are stored separately.
1687 MOZ_ASSERT(NS_IsMainThread());
1688 WidgetWheelEvent wheelEvent = panInput.ToWidgetEvent(nullptr);
1689 EventStateManager::GetUserPrefsForWheelEvent(
1690 &wheelEvent, &panInput.mUserDeltaMultiplierX,
1691 &panInput.mUserDeltaMultiplierY);
1692
1693 if (state.mHit.mTargetApzc) {
1694 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit);
1695
1696 // For pan gesture events, the call to ReceiveInputEvent below may
1697 // result in scrolling, which changes the async transform. However, the
1698 // event we want to pass to gecko should be the pre-scroll event
1699 // coordinates, transformed into the gecko space. (pre-scroll because
1700 // the mouse cursor is stationary during pan gesture scrolling, unlike
1701 // touchmove events). Since we just flushed the pending repaints the
1702 // transform to gecko space should only consist of overscroll-cancelling
1703 // transforms.
1704 ScreenToScreenMatrix4x4 transformToGecko =
1705 GetScreenToApzcTransform(state.mHit.mTargetApzc) *
1706 GetApzcToGeckoTransform(state.mHit.mTargetApzc);
1707 Maybe<ScreenPoint> untransformedStartPoint =
1708 UntransformBy(transformToGecko, panInput.mPanStartPoint);
1709 Maybe<ScreenPoint> untransformedDisplacement =
1710 UntransformVector(transformToGecko, panInput.mPanDisplacement,
1711 panInput.mPanStartPoint);
1712
1713 if (!untransformedStartPoint || !untransformedDisplacement) {
1714 return state.Finish();
1715 }
1716
1717 state.mResult = mInputQueue->ReceiveInputEvent(
1718 state.mHit.mTargetApzc,
1719 TargetConfirmationFlags{state.mHit.mHitResult}, panInput);
1720
1721 // Update the out-parameters so they are what the caller expects.
1722 panInput.mPanStartPoint = *untransformedStartPoint;
1723 panInput.mPanDisplacement = *untransformedDisplacement;
1724
1725 panInput.mOverscrollBehaviorAllowsSwipe =
1726 state.mHit.mTargetApzc->OverscrollBehaviorAllowsSwipe();
1727 }
1728 break;
1729 }
1730 case PINCHGESTURE_INPUT: {
1731 PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
1732 if (HasNonLockModifier(pinchInput.modifiers)) {
1733 APZCTM_LOG("Discarding pinch input due to modifiers 0x%x\n",
1734 pinchInput.modifiers);
1735 return state.Finish();
1736 }
1737
1738 state.mHit = GetTargetAPZC(pinchInput.mFocusPoint);
1739
1740 // We always handle pinch gestures as pinch zooms.
1741 pinchInput.mHandledByAPZ = true;
1742
1743 if (state.mHit.mTargetApzc) {
1744 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit);
1745
1746 if (!state.mHit.mTargetApzc->IsRootContent()) {
1747 state.mHit.mTargetApzc = FindZoomableApzc(state.mHit.mTargetApzc);
1748 }
1749 }
1750
1751 if (state.mHit.mTargetApzc) {
1752 ScreenToScreenMatrix4x4 outTransform =
1753 GetScreenToApzcTransform(state.mHit.mTargetApzc) *
1754 GetApzcToGeckoTransform(state.mHit.mTargetApzc);
1755 Maybe<ScreenPoint> untransformedFocusPoint =
1756 UntransformBy(outTransform, pinchInput.mFocusPoint);
1757
1758 if (!untransformedFocusPoint) {
1759 return state.Finish();
1760 }
1761
1762 state.mResult = mInputQueue->ReceiveInputEvent(
1763 state.mHit.mTargetApzc,
1764 TargetConfirmationFlags{state.mHit.mHitResult}, pinchInput);
1765
1766 // Update the out-parameters so they are what the caller expects.
1767 pinchInput.mFocusPoint = *untransformedFocusPoint;
1768 }
1769 break;
1770 }
1771 case TAPGESTURE_INPUT: { // note: no one currently sends these
1772 TapGestureInput& tapInput = aEvent.AsTapGestureInput();
1773 state.mHit = GetTargetAPZC(tapInput.mPoint);
1774
1775 if (state.mHit.mTargetApzc) {
1776 MOZ_ASSERT(state.mHit.mHitResult != CompositorHitTestInvisibleToHit);
1777
1778 ScreenToScreenMatrix4x4 outTransform =
1779 GetScreenToApzcTransform(state.mHit.mTargetApzc) *
1780 GetApzcToGeckoTransform(state.mHit.mTargetApzc);
1781 Maybe<ScreenIntPoint> untransformedPoint =
1782 UntransformBy(outTransform, tapInput.mPoint);
1783
1784 if (!untransformedPoint) {
1785 return state.Finish();
1786 }
1787
1788 state.mResult = mInputQueue->ReceiveInputEvent(
1789 state.mHit.mTargetApzc,
1790 TargetConfirmationFlags{state.mHit.mHitResult}, tapInput);
1791
1792 // Update the out-parameters so they are what the caller expects.
1793 tapInput.mPoint = *untransformedPoint;
1794 }
1795 break;
1796 }
1797 case KEYBOARD_INPUT: {
1798 // Disable async keyboard scrolling when accessibility.browsewithcaret is
1799 // enabled
1800 if (!StaticPrefs::apz_keyboard_enabled_AtStartup() ||
1801 StaticPrefs::accessibility_browsewithcaret()) {
1802 APZ_KEY_LOG("Skipping key input from invalid prefs\n");
1803 return state.Finish();
1804 }
1805
1806 KeyboardInput& keyInput = aEvent.AsKeyboardInput();
1807
1808 // Try and find a matching shortcut for this keyboard input
1809 Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput);
1810
1811 if (!shortcut) {
1812 APZ_KEY_LOG("Skipping key input with no shortcut\n");
1813
1814 // If we don't have a shortcut for this key event, then we can keep our
1815 // focus only if we know there are no key event listeners for this
1816 // target
1817 if (mFocusState.CanIgnoreKeyboardShortcutMisses()) {
1818 focusSetter.MarkAsNonFocusChanging();
1819 }
1820 return state.Finish();
1821 }
1822
1823 // Check if this shortcut needs to be dispatched to content. Anything
1824 // matching this is assumed to be able to change focus.
1825 if (shortcut->mDispatchToContent) {
1826 APZ_KEY_LOG("Skipping key input with dispatch-to-content shortcut\n");
1827 return state.Finish();
1828 }
1829
1830 // We know we have an action to execute on whatever is the current focus
1831 // target
1832 const KeyboardScrollAction& action = shortcut->mAction;
1833
1834 // The current focus target depends on which direction the scroll is to
1835 // happen
1836 Maybe<ScrollableLayerGuid> targetGuid;
1837 switch (action.mType) {
1838 case KeyboardScrollAction::eScrollCharacter: {
1839 targetGuid = mFocusState.GetHorizontalTarget();
1840 break;
1841 }
1842 case KeyboardScrollAction::eScrollLine:
1843 case KeyboardScrollAction::eScrollPage:
1844 case KeyboardScrollAction::eScrollComplete: {
1845 targetGuid = mFocusState.GetVerticalTarget();
1846 break;
1847 }
1848 }
1849
1850 // If we don't have a scroll target then either we have a stale focus
1851 // target, the focused element has event listeners, or the focused element
1852 // doesn't have a layerized scroll frame. In any case we need to dispatch
1853 // to content.
1854 if (!targetGuid) {
1855 APZ_KEY_LOG("Skipping key input with no current focus target\n");
1856 return state.Finish();
1857 }
1858
1859 RefPtr<AsyncPanZoomController> targetApzc =
1860 GetTargetAPZC(targetGuid->mLayersId, targetGuid->mScrollId);
1861
1862 if (!targetApzc) {
1863 APZ_KEY_LOG("Skipping key input with focus target but no APZC\n");
1864 return state.Finish();
1865 }
1866
1867 // Attach the keyboard scroll action to the input event for processing
1868 // by the input queue.
1869 keyInput.mAction = action;
1870
1871 APZ_KEY_LOG("Dispatching key input with apzc=%p\n", targetApzc.get());
1872
1873 // Dispatch the event to the input queue.
1874 state.mResult = mInputQueue->ReceiveInputEvent(
1875 targetApzc, TargetConfirmationFlags{true}, keyInput);
1876
1877 // Any keyboard event that is dispatched to the input queue at this point
1878 // should have been consumed
1879 MOZ_ASSERT(state.mResult.GetStatus() == nsEventStatus_eConsumeDoDefault ||
1880 state.mResult.GetStatus() == nsEventStatus_eConsumeNoDefault);
1881
1882 keyInput.mHandledByAPZ = true;
1883 focusSetter.MarkAsNonFocusChanging();
1884
1885 break;
1886 }
1887 }
1888 return state.Finish();
1889 }
1890
ConvertToTouchBehavior(const CompositorHitTestInfo & info)1891 static TouchBehaviorFlags ConvertToTouchBehavior(
1892 const CompositorHitTestInfo& info) {
1893 TouchBehaviorFlags result = AllowedTouchBehavior::UNKNOWN;
1894 if (info == CompositorHitTestInvisibleToHit) {
1895 result = AllowedTouchBehavior::NONE;
1896 } else if (info.contains(CompositorHitTestFlags::eIrregularArea)) {
1897 // Note that eApzAwareListeners and eInactiveScrollframe are similar
1898 // to eIrregularArea in some respects, but are not relevant for the
1899 // purposes of this function, which deals specifically with touch-action.
1900 result = AllowedTouchBehavior::UNKNOWN;
1901 } else {
1902 result = AllowedTouchBehavior::VERTICAL_PAN |
1903 AllowedTouchBehavior::HORIZONTAL_PAN |
1904 AllowedTouchBehavior::PINCH_ZOOM |
1905 AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
1906 if (info.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) {
1907 result &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
1908 }
1909 if (info.contains(CompositorHitTestFlags::eTouchActionPanYDisabled)) {
1910 result &= ~AllowedTouchBehavior::VERTICAL_PAN;
1911 }
1912 if (info.contains(CompositorHitTestFlags::eTouchActionPinchZoomDisabled)) {
1913 result &= ~AllowedTouchBehavior::PINCH_ZOOM;
1914 }
1915 if (info.contains(
1916 CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled)) {
1917 result &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
1918 }
1919 }
1920 return result;
1921 }
1922
GetTouchInputBlockAPZC(const MultiTouchInput & aEvent,nsTArray<TouchBehaviorFlags> * aOutTouchBehaviors)1923 APZCTreeManager::HitTestResult APZCTreeManager::GetTouchInputBlockAPZC(
1924 const MultiTouchInput& aEvent,
1925 nsTArray<TouchBehaviorFlags>* aOutTouchBehaviors) {
1926 HitTestResult hit;
1927 if (aEvent.mTouches.Length() == 0) {
1928 return hit;
1929 }
1930
1931 FlushRepaintsToClearScreenToGeckoTransform();
1932
1933 hit = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint);
1934 // Don't set a layers id on multi-touch events.
1935 if (aEvent.mTouches.Length() != 1) {
1936 hit.mLayersId = LayersId{0};
1937 }
1938
1939 if (aOutTouchBehaviors) {
1940 aOutTouchBehaviors->AppendElement(ConvertToTouchBehavior(hit.mHitResult));
1941 }
1942 for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
1943 HitTestResult hit2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint);
1944 if (aOutTouchBehaviors) {
1945 aOutTouchBehaviors->AppendElement(
1946 ConvertToTouchBehavior(hit2.mHitResult));
1947 }
1948 hit.mTargetApzc = GetZoomableTarget(hit.mTargetApzc, hit2.mTargetApzc);
1949 APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n",
1950 hit.mTargetApzc.get());
1951 // A multi-touch gesture will not be a scrollbar drag, even if the
1952 // first touch point happened to hit a scrollbar.
1953 hit.mScrollbarNode.Clear();
1954
1955 // XXX we should probably be combining the hit results from the different
1956 // touch points somehow, instead of just using the last one.
1957 hit.mHitResult = hit2.mHitResult;
1958 }
1959
1960 return hit;
1961 }
1962
Finish()1963 APZEventResult APZCTreeManager::InputHandlingState::Finish() {
1964 // The validity check here handles both the case where mHit was
1965 // never populated (because this event did not trigger a hit-test),
1966 // and the case where it was populated with an invalid LayersId
1967 // (which can happen e.g. for multi-touch events).
1968 if (mHit.mLayersId.IsValid()) {
1969 mEvent.mLayersId = mHit.mLayersId;
1970 }
1971
1972 // If the event is over an overscroll gutter, do not dispatch it to Gecko.
1973 if (mHit.mHitOverscrollGutter) {
1974 mResult.SetStatusAsConsumeNoDefault();
1975 }
1976
1977 return mResult;
1978 }
1979
ProcessTouchInput(InputHandlingState & aState,MultiTouchInput & aInput)1980 void APZCTreeManager::ProcessTouchInput(InputHandlingState& aState,
1981 MultiTouchInput& aInput) {
1982 aInput.mHandledByAPZ = true;
1983 nsTArray<TouchBehaviorFlags> touchBehaviors;
1984 HitTestingTreeNodeAutoLock hitScrollbarNode;
1985 if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
1986 // If we are panned into overscroll and a second finger goes down,
1987 // ignore that second touch point completely. The touch-start for it is
1988 // dropped completely; subsequent touch events until the touch-end for it
1989 // will have this touch point filtered out.
1990 // (By contrast, if we're in overscroll but not panning, such as after
1991 // putting two fingers down during an overscroll animation, we process the
1992 // second touch and proceed to pinch.)
1993 if (mTouchBlockHitResult.mTargetApzc &&
1994 mTouchBlockHitResult.mTargetApzc->IsInPanningState() &&
1995 BuildOverscrollHandoffChain(mTouchBlockHitResult.mTargetApzc)
1996 ->HasOverscrolledApzc()) {
1997 if (mRetainedTouchIdentifier == -1) {
1998 mRetainedTouchIdentifier =
1999 mTouchBlockHitResult.mTargetApzc->GetLastTouchIdentifier();
2000 }
2001
2002 aState.mResult.SetStatusAsConsumeNoDefault();
2003 return;
2004 }
2005
2006 aState.mHit = GetTouchInputBlockAPZC(aInput, &touchBehaviors);
2007 // Repopulate mTouchBlockHitResult with the fields we care about.
2008 mTouchBlockHitResult = aState.mHit.CopyWithoutScrollbarNode();
2009 hitScrollbarNode = std::move(aState.mHit.mScrollbarNode);
2010
2011 // Check if this event starts a scrollbar touch-drag. The conditions
2012 // checked are similar to the ones we check for MOUSE_INPUT starting
2013 // a scrollbar mouse-drag.
2014 mInScrollbarTouchDrag =
2015 StaticPrefs::apz_drag_enabled() &&
2016 StaticPrefs::apz_drag_touch_enabled() && hitScrollbarNode &&
2017 hitScrollbarNode->IsScrollThumbNode() &&
2018 hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable;
2019
2020 MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
2021 for (size_t i = 0; i < touchBehaviors.Length(); i++) {
2022 APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n",
2023 touchBehaviors[i]);
2024 if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
2025 // If there's any unknown items in the list, throw it out and we'll
2026 // wait for the main thread to send us a notification.
2027 touchBehaviors.Clear();
2028 break;
2029 }
2030 }
2031 } else if (mTouchBlockHitResult.mTargetApzc) {
2032 APZCTM_LOG("Re-using APZC %p as continuation of event block\n",
2033 mTouchBlockHitResult.mTargetApzc.get());
2034 aState.mHit = mTouchBlockHitResult.CopyWithoutScrollbarNode();
2035 }
2036
2037 if (mInScrollbarTouchDrag) {
2038 aState.mResult = ProcessTouchInputForScrollbarDrag(
2039 aInput, hitScrollbarNode, mTouchBlockHitResult.mHitResult);
2040 } else {
2041 // If we receive a touch-cancel, it means all touches are finished, so we
2042 // can stop ignoring any that we were ignoring.
2043 if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
2044 mRetainedTouchIdentifier = -1;
2045 }
2046
2047 // If we are currently ignoring any touch points, filter them out from the
2048 // set of touch points included in this event. Note that we modify aInput
2049 // itself, so that the touch points are also filtered out when the caller
2050 // passes the event on to content.
2051 if (mRetainedTouchIdentifier != -1) {
2052 for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
2053 if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
2054 aInput.mTouches.RemoveElementAt(j);
2055 if (!touchBehaviors.IsEmpty()) {
2056 MOZ_ASSERT(touchBehaviors.Length() > j);
2057 touchBehaviors.RemoveElementAt(j);
2058 }
2059 --j;
2060 }
2061 }
2062 if (aInput.mTouches.IsEmpty()) {
2063 aState.mResult.SetStatusAsConsumeNoDefault();
2064 return;
2065 }
2066 }
2067
2068 if (mTouchBlockHitResult.mTargetApzc) {
2069 MOZ_ASSERT(mTouchBlockHitResult.mHitResult !=
2070 CompositorHitTestInvisibleToHit);
2071
2072 aState.mResult = mInputQueue->ReceiveInputEvent(
2073 mTouchBlockHitResult.mTargetApzc,
2074 TargetConfirmationFlags{mTouchBlockHitResult.mHitResult}, aInput,
2075 touchBehaviors.IsEmpty() ? Nothing()
2076 : Some(std::move(touchBehaviors)));
2077
2078 // For computing the event to pass back to Gecko, use up-to-date
2079 // transforms (i.e. not anything cached in an input block). This ensures
2080 // that transformToApzc and transformToGecko are in sync.
2081 // Note: we are not using ConvertToGecko() here, because we don't
2082 // want to multiply transformToApzc and transformToGecko once
2083 // for each touch point.
2084 ScreenToParentLayerMatrix4x4 transformToApzc =
2085 GetScreenToApzcTransform(mTouchBlockHitResult.mTargetApzc);
2086 ParentLayerToScreenMatrix4x4 transformToGecko =
2087 GetApzcToGeckoTransform(mTouchBlockHitResult.mTargetApzc);
2088 ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
2089
2090 for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
2091 SingleTouchData& touchData = aInput.mTouches[i];
2092 Maybe<ScreenIntPoint> untransformedScreenPoint =
2093 UntransformBy(outTransform, touchData.mScreenPoint);
2094 if (!untransformedScreenPoint) {
2095 aState.mResult.SetStatusAsIgnore();
2096 return;
2097 }
2098 touchData.mScreenPoint = *untransformedScreenPoint;
2099 if (mTouchBlockHitResult.mFixedPosSides != SideBits::eNone) {
2100 MutexAutoLock lock(mMapLock);
2101 touchData.mScreenPoint -= RoundedToInt(apz::ComputeFixedMarginsOffset(
2102 GetCompositorFixedLayerMargins(lock),
2103 mTouchBlockHitResult.mFixedPosSides, mGeckoFixedLayerMargins));
2104 }
2105 }
2106 }
2107 }
2108
2109 mTouchCounter.Update(aInput);
2110
2111 // If it's the end of the touch sequence then clear out variables so we
2112 // don't keep dangling references and leak things.
2113 if (mTouchCounter.GetActiveTouchCount() == 0) {
2114 mTouchBlockHitResult = HitTestResult();
2115 mRetainedTouchIdentifier = -1;
2116 mInScrollbarTouchDrag = false;
2117 }
2118 }
2119
MultiTouchTypeToMouseType(MultiTouchInput::MultiTouchType aType)2120 static MouseInput::MouseType MultiTouchTypeToMouseType(
2121 MultiTouchInput::MultiTouchType aType) {
2122 switch (aType) {
2123 case MultiTouchInput::MULTITOUCH_START:
2124 return MouseInput::MOUSE_DOWN;
2125 case MultiTouchInput::MULTITOUCH_MOVE:
2126 return MouseInput::MOUSE_MOVE;
2127 case MultiTouchInput::MULTITOUCH_END:
2128 case MultiTouchInput::MULTITOUCH_CANCEL:
2129 return MouseInput::MOUSE_UP;
2130 }
2131 MOZ_ASSERT_UNREACHABLE("Invalid multi-touch type");
2132 return MouseInput::MOUSE_NONE;
2133 }
2134
ProcessTouchInputForScrollbarDrag(MultiTouchInput & aTouchInput,const HitTestingTreeNodeAutoLock & aScrollThumbNode,const gfx::CompositorHitTestInfo & aHitInfo)2135 APZEventResult APZCTreeManager::ProcessTouchInputForScrollbarDrag(
2136 MultiTouchInput& aTouchInput,
2137 const HitTestingTreeNodeAutoLock& aScrollThumbNode,
2138 const gfx::CompositorHitTestInfo& aHitInfo) {
2139 MOZ_ASSERT(mRetainedTouchIdentifier == -1);
2140 MOZ_ASSERT(mTouchBlockHitResult.mTargetApzc);
2141 MOZ_ASSERT(aTouchInput.mTouches.Length() == 1);
2142
2143 // Synthesize a mouse event based on the touch event, so that we can
2144 // reuse code in InputQueue and APZC for handling scrollbar mouse-drags.
2145 MouseInput mouseInput{MultiTouchTypeToMouseType(aTouchInput.mType),
2146 MouseInput::PRIMARY_BUTTON,
2147 dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH,
2148 MouseButtonsFlag::ePrimaryFlag,
2149 aTouchInput.mTouches[0].mScreenPoint,
2150 aTouchInput.mTime,
2151 aTouchInput.mTimeStamp,
2152 aTouchInput.modifiers};
2153 mouseInput.mHandledByAPZ = true;
2154
2155 TargetConfirmationFlags targetConfirmed{aHitInfo};
2156 APZEventResult result;
2157 result = mInputQueue->ReceiveInputEvent(mTouchBlockHitResult.mTargetApzc,
2158 targetConfirmed, mouseInput);
2159
2160 // |aScrollThumbNode| is non-null iff. this is the event that starts the drag.
2161 // If so, set up the drag.
2162 if (aScrollThumbNode) {
2163 SetupScrollbarDrag(mouseInput, aScrollThumbNode,
2164 mTouchBlockHitResult.mTargetApzc.get());
2165 }
2166
2167 // Since the input was targeted at a scrollbar:
2168 // - The original touch event (which will be sent on to content) will
2169 // not be untransformed.
2170 // - We don't want to apply the callback transform in the main thread,
2171 // so we remove the scrollid from the guid.
2172 // Both of these match the behaviour of mouse events that target a scrollbar;
2173 // see the code for handling mouse events in ReceiveInputEvent() for
2174 // additional explanation.
2175 result.mTargetGuid.mScrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
2176
2177 return result;
2178 }
2179
SetupScrollbarDrag(MouseInput & aMouseInput,const HitTestingTreeNodeAutoLock & aScrollThumbNode,AsyncPanZoomController * aApzc)2180 void APZCTreeManager::SetupScrollbarDrag(
2181 MouseInput& aMouseInput, const HitTestingTreeNodeAutoLock& aScrollThumbNode,
2182 AsyncPanZoomController* aApzc) {
2183 DragBlockState* dragBlock = mInputQueue->GetCurrentDragBlock();
2184 if (!dragBlock) {
2185 return;
2186 }
2187
2188 const ScrollbarData& thumbData = aScrollThumbNode->GetScrollbarData();
2189 MOZ_ASSERT(thumbData.mDirection.isSome());
2190
2191 // Record the thumb's position at the start of the drag.
2192 // We snap back to this position if, during the drag, the mouse
2193 // gets sufficiently far away from the scrollbar.
2194 dragBlock->SetInitialThumbPos(thumbData.mThumbStart);
2195
2196 // Under some conditions, we can confirm the drag block right away.
2197 // Otherwise, we have to wait for a main-thread confirmation.
2198 if (StaticPrefs::apz_drag_initial_enabled() &&
2199 // check that the scrollbar's target scroll frame is layerized
2200 aScrollThumbNode->GetScrollTargetId() == aApzc->GetGuid().mScrollId &&
2201 !aApzc->IsScrollInfoLayer()) {
2202 uint64_t dragBlockId = dragBlock->GetBlockId();
2203 // AsyncPanZoomController::HandleInputEvent() will call
2204 // TransformToLocal() on the event, but we need its mLocalOrigin now
2205 // to compute a drag start offset for the AsyncDragMetrics.
2206 aMouseInput.TransformToLocal(aApzc->GetTransformToThis());
2207 CSSCoord dragStart =
2208 aApzc->ConvertScrollbarPoint(aMouseInput.mLocalOrigin, thumbData);
2209 // ConvertScrollbarPoint() got the drag start offset relative to
2210 // the scroll track. Now get it relative to the thumb.
2211 // ScrollThumbData::mThumbStart stores the offset of the thumb
2212 // relative to the scroll track at the time of the last paint.
2213 // Since that paint, the thumb may have acquired an async transform
2214 // due to async scrolling, so look that up and apply it.
2215 LayerToParentLayerMatrix4x4 thumbTransform;
2216 {
2217 RecursiveMutexAutoLock lock(mTreeLock);
2218 thumbTransform = ComputeTransformForNode(aScrollThumbNode.Get(lock));
2219 }
2220 // Only consider the translation, since we do not support both
2221 // zooming and scrollbar dragging on any platform.
2222 CSSCoord thumbStart =
2223 thumbData.mThumbStart +
2224 ((*thumbData.mDirection == ScrollDirection::eHorizontal)
2225 ? thumbTransform._41
2226 : thumbTransform._42);
2227 dragStart -= thumbStart;
2228
2229 // Content can't prevent scrollbar dragging with preventDefault(),
2230 // so we don't need to wait for a content response. It's important
2231 // to do this before calling ConfirmDragBlock() since that can
2232 // potentially process and consume the block.
2233 dragBlock->SetContentResponse(false);
2234
2235 NotifyScrollbarDragInitiated(dragBlockId, aApzc->GetGuid(),
2236 *thumbData.mDirection);
2237
2238 mInputQueue->ConfirmDragBlock(
2239 dragBlockId, aApzc,
2240 AsyncDragMetrics(aApzc->GetGuid().mScrollId,
2241 aApzc->GetGuid().mPresShellId, dragBlockId, dragStart,
2242 *thumbData.mDirection));
2243 }
2244 }
2245
SynthesizePinchGestureFromMouseWheel(const ScrollWheelInput & aWheelInput,const RefPtr<AsyncPanZoomController> & aTarget)2246 void APZCTreeManager::SynthesizePinchGestureFromMouseWheel(
2247 const ScrollWheelInput& aWheelInput,
2248 const RefPtr<AsyncPanZoomController>& aTarget) {
2249 MOZ_ASSERT(aTarget);
2250
2251 ScreenPoint focusPoint = aWheelInput.mOrigin;
2252
2253 // Compute span values based on the wheel delta.
2254 ScreenCoord oldSpan = 100;
2255 ScreenCoord newSpan = oldSpan + aWheelInput.mDeltaY;
2256
2257 // There's no ambiguity as to the target for pinch gesture events.
2258 TargetConfirmationFlags confFlags{true};
2259
2260 PinchGestureInput pinchStart{PinchGestureInput::PINCHGESTURE_START,
2261 PinchGestureInput::MOUSEWHEEL,
2262 aWheelInput.mTime,
2263 aWheelInput.mTimeStamp,
2264 ExternalPoint(0, 0),
2265 focusPoint,
2266 oldSpan,
2267 oldSpan,
2268 aWheelInput.modifiers};
2269 PinchGestureInput pinchScale1{PinchGestureInput::PINCHGESTURE_SCALE,
2270 PinchGestureInput::MOUSEWHEEL,
2271 aWheelInput.mTime,
2272 aWheelInput.mTimeStamp,
2273 ExternalPoint(0, 0),
2274 focusPoint,
2275 oldSpan,
2276 oldSpan,
2277 aWheelInput.modifiers};
2278 PinchGestureInput pinchScale2{PinchGestureInput::PINCHGESTURE_SCALE,
2279 PinchGestureInput::MOUSEWHEEL,
2280 aWheelInput.mTime,
2281 aWheelInput.mTimeStamp,
2282 ExternalPoint(0, 0),
2283 focusPoint,
2284 oldSpan,
2285 newSpan,
2286 aWheelInput.modifiers};
2287 PinchGestureInput pinchEnd{PinchGestureInput::PINCHGESTURE_END,
2288 PinchGestureInput::MOUSEWHEEL,
2289 aWheelInput.mTime,
2290 aWheelInput.mTimeStamp,
2291 ExternalPoint(0, 0),
2292 focusPoint,
2293 newSpan,
2294 newSpan,
2295 aWheelInput.modifiers};
2296
2297 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchStart);
2298 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale1);
2299 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchScale2);
2300 mInputQueue->ReceiveInputEvent(aTarget, confFlags, pinchEnd);
2301 }
2302
UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,EventMessage aEventMessage)2303 void APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
2304 EventMessage aEventMessage) {
2305 APZThreadUtils::AssertOnControllerThread();
2306
2307 WheelBlockState* txn = mInputQueue->GetActiveWheelTransaction();
2308 if (!txn) {
2309 return;
2310 }
2311
2312 // If the transaction has simply timed out, we don't need to do anything
2313 // else.
2314 if (txn->MaybeTimeout(TimeStamp::Now())) {
2315 return;
2316 }
2317
2318 switch (aEventMessage) {
2319 case eMouseMove:
2320 case eDragOver: {
2321 ScreenIntPoint point = ViewAs<ScreenPixel>(
2322 aRefPoint,
2323 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
2324
2325 txn->OnMouseMove(point);
2326
2327 return;
2328 }
2329 case eKeyPress:
2330 case eKeyUp:
2331 case eKeyDown:
2332 case eMouseUp:
2333 case eMouseDown:
2334 case eMouseDoubleClick:
2335 case eMouseAuxClick:
2336 case eMouseClick:
2337 case eContextMenu:
2338 case eDrop:
2339 txn->EndTransaction();
2340 return;
2341 default:
2342 break;
2343 }
2344 }
2345
ProcessUnhandledEvent(LayoutDeviceIntPoint * aRefPoint,ScrollableLayerGuid * aOutTargetGuid,uint64_t * aOutFocusSequenceNumber,LayersId * aOutLayersId)2346 void APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
2347 ScrollableLayerGuid* aOutTargetGuid,
2348 uint64_t* aOutFocusSequenceNumber,
2349 LayersId* aOutLayersId) {
2350 APZThreadUtils::AssertOnControllerThread();
2351
2352 // Transform the aRefPoint.
2353 // If the event hits an overscrolled APZC, instruct the caller to ignore it.
2354 PixelCastJustification LDIsScreen =
2355 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent;
2356 ScreenIntPoint refPointAsScreen = ViewAs<ScreenPixel>(*aRefPoint, LDIsScreen);
2357 HitTestResult hit = GetTargetAPZC(refPointAsScreen);
2358 if (aOutLayersId) {
2359 *aOutLayersId = hit.mLayersId;
2360 }
2361 if (hit.mTargetApzc) {
2362 MOZ_ASSERT(hit.mHitResult != CompositorHitTestInvisibleToHit);
2363 hit.mTargetApzc->GetGuid(aOutTargetGuid);
2364 ScreenToParentLayerMatrix4x4 transformToApzc =
2365 GetScreenToApzcTransform(hit.mTargetApzc);
2366 ParentLayerToScreenMatrix4x4 transformToGecko =
2367 GetApzcToGeckoTransform(hit.mTargetApzc);
2368 ScreenToScreenMatrix4x4 outTransform = transformToApzc * transformToGecko;
2369 Maybe<ScreenIntPoint> untransformedRefPoint =
2370 UntransformBy(outTransform, refPointAsScreen);
2371 if (untransformedRefPoint) {
2372 *aRefPoint =
2373 ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
2374 }
2375 }
2376
2377 // Update the focus sequence number and attach it to the event
2378 mFocusState.ReceiveFocusChangingEvent();
2379 *aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
2380 }
2381
SetKeyboardMap(const KeyboardMap & aKeyboardMap)2382 void APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
2383 APZThreadUtils::AssertOnControllerThread();
2384
2385 mKeyboardMap = aKeyboardMap;
2386 }
2387
ZoomToRect(const ScrollableLayerGuid & aGuid,const ZoomTarget & aZoomTarget,const uint32_t aFlags)2388 void APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
2389 const ZoomTarget& aZoomTarget,
2390 const uint32_t aFlags) {
2391 // We could probably move this to run on the updater thread if needed, but
2392 // either way we should restrict it to a single thread. For now let's use the
2393 // controller thread.
2394 APZThreadUtils::AssertOnControllerThread();
2395
2396 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
2397 if (apzc) {
2398 apzc->ZoomToRect(aZoomTarget, aFlags);
2399 }
2400 }
2401
ContentReceivedInputBlock(uint64_t aInputBlockId,bool aPreventDefault)2402 void APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId,
2403 bool aPreventDefault) {
2404 APZThreadUtils::AssertOnControllerThread();
2405
2406 mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
2407 }
2408
SetTargetAPZC(uint64_t aInputBlockId,const nsTArray<ScrollableLayerGuid> & aTargets)2409 void APZCTreeManager::SetTargetAPZC(
2410 uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
2411 APZThreadUtils::AssertOnControllerThread();
2412
2413 RefPtr<AsyncPanZoomController> target = nullptr;
2414 if (aTargets.Length() > 0) {
2415 target = GetTargetAPZC(aTargets[0]);
2416 }
2417 for (size_t i = 1; i < aTargets.Length(); i++) {
2418 RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
2419 target = GetZoomableTarget(target, apzc);
2420 }
2421 if (InputBlockState* block = mInputQueue->GetBlockForId(aInputBlockId)) {
2422 if (block->AsPinchGestureBlock() && aTargets.Length() == 1) {
2423 target = FindZoomableApzc(target);
2424 }
2425 }
2426 mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
2427 }
2428
GuidComparatorIgnoringPresShell(const ScrollableLayerGuid & aOne,const ScrollableLayerGuid & aTwo)2429 static bool GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne,
2430 const ScrollableLayerGuid& aTwo) {
2431 return aOne.mLayersId == aTwo.mLayersId && aOne.mScrollId == aTwo.mScrollId;
2432 }
2433
UpdateZoomConstraints(const ScrollableLayerGuid & aGuid,const Maybe<ZoomConstraints> & aConstraints)2434 void APZCTreeManager::UpdateZoomConstraints(
2435 const ScrollableLayerGuid& aGuid,
2436 const Maybe<ZoomConstraints>& aConstraints) {
2437 if (!GetUpdater()->IsUpdaterThread()) {
2438 // This can happen if we're in the UI process and got a call directly from
2439 // nsBaseWidget or from a content process over PAPZCTreeManager. In that
2440 // case we get this call on the compositor thread, which may be different
2441 // from the updater thread. It can also happen in the GPU process if that is
2442 // enabled, since the call will go over PAPZCTreeManager and arrive on the
2443 // compositor thread in the GPU process.
2444 GetUpdater()->RunOnUpdaterThread(
2445 aGuid.mLayersId,
2446 NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
2447 "APZCTreeManager::UpdateZoomConstraints", this,
2448 &APZCTreeManager::UpdateZoomConstraints, aGuid, aConstraints));
2449 return;
2450 }
2451
2452 AssertOnUpdaterThread();
2453
2454 // Propagate the zoom constraints down to the subtree, stopping at APZCs
2455 // which have their own zoom constraints or are in a different layers id.
2456 if (aConstraints) {
2457 APZCTM_LOG("Recording constraints %s for guid %s\n",
2458 ToString(aConstraints.value()).c_str(), ToString(aGuid).c_str());
2459 mZoomConstraints[aGuid] = aConstraints.ref();
2460 } else {
2461 APZCTM_LOG("Removing constraints for guid %s\n", ToString(aGuid).c_str());
2462 mZoomConstraints.erase(aGuid);
2463 }
2464
2465 RecursiveMutexAutoLock lock(mTreeLock);
2466 RefPtr<HitTestingTreeNode> node = DepthFirstSearchPostOrder<ReverseIterator>(
2467 mRootNode.get(), [&aGuid](HitTestingTreeNode* aNode) {
2468 bool matches = false;
2469 if (auto zoomId = aNode->GetAsyncZoomContainerId()) {
2470 matches = GuidComparatorIgnoringPresShell(
2471 aGuid, ScrollableLayerGuid(aNode->GetLayersId(), 0, *zoomId));
2472 }
2473 return matches;
2474 });
2475
2476 MOZ_ASSERT(!node ||
2477 !node->GetApzc()); // any node returned must not have an APZC
2478
2479 // This does not hold because we can get zoom constraints updates before the
2480 // layer tree update with the async zoom container (I assume).
2481 // clang-format off
2482 // MOZ_ASSERT(node || aConstraints.isNothing() ||
2483 // (!aConstraints->mAllowZoom && !aConstraints->mAllowDoubleTapZoom));
2484 // clang-format on
2485
2486 // If there is no async zoom container then the zoom constraints should not
2487 // allow zooming and building the HTT should have handled clearing the zoom
2488 // constraints from all nodes so we don't have to handle doing anything in
2489 // case there is no async zoom container.
2490
2491 if (node && aConstraints) {
2492 ForEachNode<ReverseIterator>(node.get(), [&aConstraints, &node, &aGuid,
2493 this](HitTestingTreeNode* aNode) {
2494 if (aNode != node) {
2495 // don't go into other async zoom containers
2496 if (auto zoomId = aNode->GetAsyncZoomContainerId()) {
2497 MOZ_ASSERT(!GuidComparatorIgnoringPresShell(
2498 aGuid, ScrollableLayerGuid(aNode->GetLayersId(), 0, *zoomId)));
2499 return TraversalFlag::Skip;
2500 }
2501 if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
2502 if (!GuidComparatorIgnoringPresShell(aGuid, childApzc->GetGuid())) {
2503 // We can have subtrees with their own zoom constraints - leave
2504 // these alone.
2505 if (this->mZoomConstraints.find(childApzc->GetGuid()) !=
2506 this->mZoomConstraints.end()) {
2507 return TraversalFlag::Skip;
2508 }
2509 }
2510 }
2511 }
2512 if (aNode->IsPrimaryHolder()) {
2513 MOZ_ASSERT(aNode->GetApzc());
2514 aNode->GetApzc()->UpdateZoomConstraints(aConstraints.ref());
2515 }
2516 return TraversalFlag::Continue;
2517 });
2518 }
2519 }
2520
FlushRepaintsToClearScreenToGeckoTransform()2521 void APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform() {
2522 // As the name implies, we flush repaint requests for the entire APZ tree in
2523 // order to clear the screen-to-gecko transform (aka the "untransform" applied
2524 // to incoming input events before they can be passed on to Gecko).
2525 //
2526 // The primary reason we do this is to avoid the problem where input events,
2527 // after being untransformed, end up hit-testing differently in Gecko. This
2528 // might happen in cases where the input event lands on content that is async-
2529 // scrolled into view, but Gecko still thinks it is out of view given the
2530 // visible area of a scrollframe.
2531 //
2532 // Another reason we want to clear the untransform is that if our APZ hit-test
2533 // hits a dispatch-to-content region then that's an ambiguous result and we
2534 // need to ask Gecko what actually got hit. In order to do this we need to
2535 // untransform the input event into Gecko space - but to do that we need to
2536 // know which APZC got hit! This leads to a circular dependency; the only way
2537 // to get out of it is to make sure that the untransform for all the possible
2538 // matched APZCs is the same. It is simplest to ensure that by flushing the
2539 // pending repaint requests, which makes all of the untransforms empty (and
2540 // therefore equal).
2541 RecursiveMutexAutoLock lock(mTreeLock);
2542
2543 ForEachNode<ReverseIterator>(mRootNode.get(), [](HitTestingTreeNode* aNode) {
2544 if (aNode->IsPrimaryHolder()) {
2545 MOZ_ASSERT(aNode->GetApzc());
2546 aNode->GetApzc()->FlushRepaintForNewInputBlock();
2547 }
2548 });
2549 }
2550
ClearTree()2551 void APZCTreeManager::ClearTree() {
2552 AssertOnUpdaterThread();
2553
2554 // Ensure that no references to APZCs are alive in any lingering input
2555 // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
2556 // the InputQueue.
2557 APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
2558 "layers::InputQueue::Clear", mInputQueue, &InputQueue::Clear));
2559
2560 RecursiveMutexAutoLock lock(mTreeLock);
2561
2562 // Collect the nodes into a list, and then destroy each one.
2563 // We can't destroy them as we collect them, because ForEachNode()
2564 // does a pre-order traversal of the tree, and Destroy() nulls out
2565 // the fields needed to reach the children of the node.
2566 nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
2567 ForEachNode<ReverseIterator>(mRootNode.get(),
2568 [&nodesToDestroy](HitTestingTreeNode* aNode) {
2569 nodesToDestroy.AppendElement(aNode);
2570 });
2571
2572 for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
2573 nodesToDestroy[i]->Destroy();
2574 }
2575 mRootNode = nullptr;
2576
2577 {
2578 // Also remove references to APZC instances in the map
2579 MutexAutoLock lock(mMapLock);
2580 mApzcMap.clear();
2581 }
2582
2583 RefPtr<APZCTreeManager> self(this);
2584 NS_DispatchToMainThread(
2585 NS_NewRunnableFunction("layers::APZCTreeManager::ClearTree", [self] {
2586 self->mFlushObserver->Unregister();
2587 self->mFlushObserver = nullptr;
2588 }));
2589 }
2590
2591 APZCTreeManager::HitTestResult
CopyWithoutScrollbarNode() const2592 APZCTreeManager::HitTestResult::CopyWithoutScrollbarNode() const {
2593 HitTestResult result;
2594 result.mTargetApzc = mTargetApzc;
2595 result.mHitResult = mHitResult;
2596 result.mLayersId = mLayersId;
2597 result.mFixedPosSides = mFixedPosSides;
2598 result.mHitOverscrollGutter = mHitOverscrollGutter;
2599 return result;
2600 }
2601
GetRootNode() const2602 RefPtr<HitTestingTreeNode> APZCTreeManager::GetRootNode() const {
2603 RecursiveMutexAutoLock lock(mTreeLock);
2604 return mRootNode;
2605 }
2606
2607 /**
2608 * Transform a displacement from the ParentLayer coordinates of a source APZC
2609 * to the ParentLayer coordinates of a target APZC.
2610 * @param aTreeManager the tree manager for the APZC tree containing |aSource|
2611 * and |aTarget|
2612 * @param aSource the source APZC
2613 * @param aTarget the target APZC
2614 * @param aStartPoint the start point of the displacement
2615 * @param aEndPoint the end point of the displacement
2616 * @return true on success, false if aStartPoint or aEndPoint cannot be
2617 * transformed into target's coordinate space
2618 */
TransformDisplacement(APZCTreeManager * aTreeManager,AsyncPanZoomController * aSource,AsyncPanZoomController * aTarget,ParentLayerPoint & aStartPoint,ParentLayerPoint & aEndPoint)2619 static bool TransformDisplacement(APZCTreeManager* aTreeManager,
2620 AsyncPanZoomController* aSource,
2621 AsyncPanZoomController* aTarget,
2622 ParentLayerPoint& aStartPoint,
2623 ParentLayerPoint& aEndPoint) {
2624 if (aSource == aTarget) {
2625 return true;
2626 }
2627
2628 // Convert start and end points to Screen coordinates.
2629 ParentLayerToScreenMatrix4x4 untransformToApzc =
2630 aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
2631 ScreenPoint screenStart = TransformBy(untransformToApzc, aStartPoint);
2632 ScreenPoint screenEnd = TransformBy(untransformToApzc, aEndPoint);
2633
2634 // Convert start and end points to aTarget's ParentLayer coordinates.
2635 ScreenToParentLayerMatrix4x4 transformToApzc =
2636 aTreeManager->GetScreenToApzcTransform(aTarget);
2637 Maybe<ParentLayerPoint> startPoint =
2638 UntransformBy(transformToApzc, screenStart);
2639 Maybe<ParentLayerPoint> endPoint = UntransformBy(transformToApzc, screenEnd);
2640 if (!startPoint || !endPoint) {
2641 return false;
2642 }
2643 aEndPoint = *endPoint;
2644 aStartPoint = *startPoint;
2645
2646 return true;
2647 }
2648
DispatchScroll(AsyncPanZoomController * aPrev,ParentLayerPoint & aStartPoint,ParentLayerPoint & aEndPoint,OverscrollHandoffState & aOverscrollHandoffState)2649 bool APZCTreeManager::DispatchScroll(
2650 AsyncPanZoomController* aPrev, ParentLayerPoint& aStartPoint,
2651 ParentLayerPoint& aEndPoint,
2652 OverscrollHandoffState& aOverscrollHandoffState) {
2653 const OverscrollHandoffChain& overscrollHandoffChain =
2654 aOverscrollHandoffState.mChain;
2655 uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
2656 RefPtr<AsyncPanZoomController> next;
2657 // If we have reached the end of the overscroll handoff chain, there is
2658 // nothing more to scroll, so we ignore the rest of the pan gesture.
2659 if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
2660 // Nothing more to scroll - ignore the rest of the pan gesture.
2661 return false;
2662 }
2663
2664 next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
2665
2666 if (next == nullptr || next->IsDestroyed()) {
2667 return false;
2668 }
2669
2670 // Convert the start and end points from |aPrev|'s coordinate space to
2671 // |next|'s coordinate space.
2672 if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
2673 return false;
2674 }
2675
2676 // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
2677 // again with an incremented index.
2678 if (!next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState)) {
2679 // Transform |aStartPoint| and |aEndPoint| (which now represent the
2680 // portion of the displacement that wasn't consumed by APZCs later
2681 // in the handoff chain) back into |aPrev|'s coordinate space. This
2682 // allows the caller (which is |aPrev|) to interpret the unconsumed
2683 // displacement in its own coordinate space, and make use of it
2684 // (e.g. by going into overscroll).
2685 if (!TransformDisplacement(this, next, aPrev, aStartPoint, aEndPoint)) {
2686 NS_WARNING("Failed to untransform scroll points during dispatch");
2687 }
2688 return false;
2689 }
2690
2691 // Return true to indicate the scroll was consumed entirely.
2692 return true;
2693 }
2694
DispatchFling(AsyncPanZoomController * aPrev,const FlingHandoffState & aHandoffState)2695 ParentLayerPoint APZCTreeManager::DispatchFling(
2696 AsyncPanZoomController* aPrev, const FlingHandoffState& aHandoffState) {
2697 // If immediate handoff is disallowed, do not allow handoff beyond the
2698 // single APZC that's scrolled by the input block that triggered this fling.
2699 if (aHandoffState.mIsHandoff && !StaticPrefs::apz_allow_immediate_handoff() &&
2700 aHandoffState.mScrolledApzc == aPrev) {
2701 FLING_LOG("APZCTM dropping handoff due to disallowed immediate handoff\n");
2702 return aHandoffState.mVelocity;
2703 }
2704
2705 const OverscrollHandoffChain* chain = aHandoffState.mChain;
2706 RefPtr<AsyncPanZoomController> current;
2707 uint32_t overscrollHandoffChainLength = chain->Length();
2708 uint32_t startIndex;
2709
2710 // The fling's velocity needs to be transformed from the screen coordinates
2711 // of |aPrev| to the screen coordinates of |next|. To transform a velocity
2712 // correctly, we need to convert it to a displacement. For now, we do this
2713 // by anchoring it to a start point of (0, 0).
2714 // TODO: For this to be correct in the presence of 3D transforms, we should
2715 // use the end point of the touch that started the fling as the start point
2716 // rather than (0, 0).
2717 ParentLayerPoint startPoint; // (0, 0)
2718 ParentLayerPoint endPoint;
2719
2720 if (aHandoffState.mIsHandoff) {
2721 startIndex = chain->IndexOf(aPrev) + 1;
2722
2723 // IndexOf will return aOverscrollHandoffChain->Length() if
2724 // |aPrev| is not found.
2725 if (startIndex >= overscrollHandoffChainLength) {
2726 return aHandoffState.mVelocity;
2727 }
2728 } else {
2729 startIndex = 0;
2730 }
2731
2732 // This will store any velocity left over after the entire handoff.
2733 ParentLayerPoint finalResidualVelocity = aHandoffState.mVelocity;
2734
2735 ParentLayerPoint currentVelocity = aHandoffState.mVelocity;
2736 for (; startIndex < overscrollHandoffChainLength; startIndex++) {
2737 current = chain->GetApzcAtIndex(startIndex);
2738
2739 // Make sure the apzc about to be handled can be handled
2740 if (current == nullptr || current->IsDestroyed()) {
2741 break;
2742 }
2743
2744 endPoint = startPoint + currentVelocity;
2745
2746 RefPtr<AsyncPanZoomController> prevApzc =
2747 (startIndex > 0) ? chain->GetApzcAtIndex(startIndex - 1) : nullptr;
2748
2749 // Only transform when current apzc can be transformed with previous
2750 if (prevApzc) {
2751 if (!TransformDisplacement(this, prevApzc, current, startPoint,
2752 endPoint)) {
2753 break;
2754 }
2755 }
2756
2757 ParentLayerPoint availableVelocity = (endPoint - startPoint);
2758 ParentLayerPoint residualVelocity;
2759
2760 FlingHandoffState transformedHandoffState = aHandoffState;
2761 transformedHandoffState.mVelocity = availableVelocity;
2762
2763 // Obey overscroll-behavior.
2764 if (prevApzc) {
2765 residualVelocity += prevApzc->AdjustHandoffVelocityForOverscrollBehavior(
2766 transformedHandoffState.mVelocity);
2767 }
2768
2769 residualVelocity += current->AttemptFling(transformedHandoffState);
2770
2771 // If there's no residual velocity, there's nothing more to hand off.
2772 if (IsZero(residualVelocity)) {
2773 return ParentLayerPoint();
2774 }
2775
2776 // If any of the velocity available to be handed off was consumed,
2777 // subtract the proportion of consumed velocity from finalResidualVelocity.
2778 // Note: it's important to compare |residualVelocity| to |availableVelocity|
2779 // here and not to |transformedHandoffState.mVelocity|, since the latter
2780 // may have been modified by AdjustHandoffVelocityForOverscrollBehavior().
2781 if (!FuzzyEqualsAdditive(availableVelocity.x, residualVelocity.x,
2782 COORDINATE_EPSILON)) {
2783 finalResidualVelocity.x *= (residualVelocity.x / availableVelocity.x);
2784 }
2785 if (!FuzzyEqualsAdditive(availableVelocity.y, residualVelocity.y,
2786 COORDINATE_EPSILON)) {
2787 finalResidualVelocity.y *= (residualVelocity.y / availableVelocity.y);
2788 }
2789
2790 currentVelocity = residualVelocity;
2791 }
2792
2793 // Return any residual velocity left over after the entire handoff process.
2794 return finalResidualVelocity;
2795 }
2796
GetTargetAPZC(const ScrollableLayerGuid & aGuid)2797 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC(
2798 const ScrollableLayerGuid& aGuid) {
2799 RecursiveMutexAutoLock lock(mTreeLock);
2800 RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
2801 MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
2802 RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
2803 return apzc.forget();
2804 }
2805
GetTargetAPZC(const LayersId & aLayersId,const ScrollableLayerGuid::ViewID & aScrollId) const2806 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC(
2807 const LayersId& aLayersId,
2808 const ScrollableLayerGuid::ViewID& aScrollId) const {
2809 MutexAutoLock lock(mMapLock);
2810 ScrollableLayerGuid guid(aLayersId, 0, aScrollId);
2811 auto it = mApzcMap.find(guid);
2812 RefPtr<AsyncPanZoomController> apzc =
2813 (it != mApzcMap.end() ? it->second.apzc : nullptr);
2814 return apzc.forget();
2815 }
2816
GetTargetNode(const ScrollableLayerGuid & aGuid,GuidComparator aComparator) const2817 already_AddRefed<HitTestingTreeNode> APZCTreeManager::GetTargetNode(
2818 const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const {
2819 mTreeLock.AssertCurrentThreadIn();
2820 RefPtr<HitTestingTreeNode> target =
2821 DepthFirstSearchPostOrder<ReverseIterator>(
2822 mRootNode.get(), [&aGuid, &aComparator](HitTestingTreeNode* node) {
2823 bool matches = false;
2824 if (node->GetApzc()) {
2825 if (aComparator) {
2826 matches = aComparator(aGuid, node->GetApzc()->GetGuid());
2827 } else {
2828 matches = node->GetApzc()->Matches(aGuid);
2829 }
2830 }
2831 return matches;
2832 });
2833 return target.forget();
2834 }
2835
GetTargetAPZC(const ScreenPoint & aPoint)2836 APZCTreeManager::HitTestResult APZCTreeManager::GetTargetAPZC(
2837 const ScreenPoint& aPoint) {
2838 RecursiveMutexAutoLock lock(mTreeLock);
2839 if (mIsUsingWebRender) {
2840 return GetAPZCAtPointWR(aPoint, lock);
2841 }
2842 return GetAPZCAtPoint(aPoint, lock);
2843 }
2844
GetAPZCAtPointWR(const ScreenPoint & aHitTestPoint,const RecursiveMutexAutoLock & aProofOfTreeLock)2845 APZCTreeManager::HitTestResult APZCTreeManager::GetAPZCAtPointWR(
2846 const ScreenPoint& aHitTestPoint,
2847 const RecursiveMutexAutoLock& aProofOfTreeLock) {
2848 HitTestResult hit;
2849 RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPI();
2850 if (!wr) {
2851 // If WebRender isn't running, fall back to the root APZC.
2852 // This is mostly for the benefit of GTests which do not
2853 // run a WebRender instance, but gracefully falling back
2854 // here allows those tests which are not specifically
2855 // testing the hit-test algorithm to still work.
2856 hit.mTargetApzc = FindRootApzcForLayersId(mRootLayersId);
2857 hit.mHitResult = CompositorHitTestFlags::eVisibleToHitTest;
2858 return hit;
2859 }
2860
2861 APZCTM_LOG("Hit-testing point %s with WR\n", ToString(aHitTestPoint).c_str());
2862 std::vector<wr::WrHitResult> results =
2863 wr->HitTest(wr::ToWorldPoint(aHitTestPoint));
2864
2865 Maybe<wr::WrHitResult> chosenResult;
2866 for (const wr::WrHitResult& result : results) {
2867 ScrollableLayerGuid guid{result.mLayersId, 0, result.mScrollId};
2868 APZCTM_LOG("Examining result with guid %s hit info 0x%x... ",
2869 ToString(guid).c_str(), result.mHitInfo.serialize());
2870 if (result.mHitInfo == CompositorHitTestInvisibleToHit) {
2871 APZCTM_LOG("skipping due to invisibility.\n");
2872 continue;
2873 }
2874 RefPtr<HitTestingTreeNode> node =
2875 GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
2876 if (!node) {
2877 APZCTM_LOG("no corresponding node found, falling back to root.\n");
2878 // We can enter here during normal codepaths for cases where the
2879 // nsDisplayCompositorHitTestInfo item emitted a scrollId of
2880 // NULL_SCROLL_ID to the webrender display list. The semantics of that
2881 // is to fall back to the root APZC for the layers id, so that's what
2882 // we do here.
2883 // If we enter this codepath and scrollId is not NULL_SCROLL_ID, then
2884 // that's more likely to be due to a race condition between rebuilding
2885 // the APZ tree and updating the WR scene/hit-test information, resulting
2886 // in WR giving us a hit result for a scene that is not active in APZ.
2887 // Such a scenario would need debugging and fixing.
2888 MOZ_ASSERT(result.mScrollId == ScrollableLayerGuid::NULL_SCROLL_ID);
2889 node = FindRootNodeForLayersId(result.mLayersId);
2890 if (!node) {
2891 // Should never happen, but handle gracefully in release builds just
2892 // in case.
2893 MOZ_ASSERT(false);
2894 chosenResult = Some(result);
2895 break;
2896 }
2897 }
2898 MOZ_ASSERT(node->GetApzc()); // any node returned must have an APZC
2899 EventRegionsOverride flags = node->GetEventRegionsOverride();
2900 if (flags & EventRegionsOverride::ForceEmptyHitRegion) {
2901 // This result is inside a subtree that is invisible to hit-testing.
2902 APZCTM_LOG("skipping due to FEHR subtree.\n");
2903 continue;
2904 }
2905
2906 APZCTM_LOG("selecting as chosen result.\n");
2907 chosenResult = Some(result);
2908 hit.mTargetApzc = node->GetApzc();
2909 if (flags & EventRegionsOverride::ForceDispatchToContent) {
2910 chosenResult->mHitInfo += CompositorHitTestFlags::eApzAwareListeners;
2911 }
2912 break;
2913 }
2914 if (!chosenResult) {
2915 return hit;
2916 }
2917
2918 MOZ_ASSERT(hit.mTargetApzc);
2919 hit.mLayersId = chosenResult->mLayersId;
2920 ScrollableLayerGuid::ViewID scrollId = chosenResult->mScrollId;
2921 gfx::CompositorHitTestInfo hitInfo = chosenResult->mHitInfo;
2922 SideBits sideBits = chosenResult->mSideBits;
2923
2924 APZCTM_LOG("Successfully matched APZC %p (hit result 0x%x)\n",
2925 hit.mTargetApzc.get(), hitInfo.serialize());
2926
2927 const bool isScrollbar =
2928 hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbar);
2929 const bool isScrollbarThumb =
2930 hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbarThumb);
2931 const ScrollDirection direction =
2932 hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbarVertical)
2933 ? ScrollDirection::eVertical
2934 : ScrollDirection::eHorizontal;
2935 HitTestingTreeNode* scrollbarNode = nullptr;
2936 if (isScrollbar || isScrollbarThumb) {
2937 scrollbarNode = BreadthFirstSearch<ReverseIterator>(
2938 mRootNode.get(), [&](HitTestingTreeNode* aNode) {
2939 return (aNode->GetLayersId() == hit.mLayersId) &&
2940 (aNode->IsScrollbarNode() == isScrollbar) &&
2941 (aNode->IsScrollThumbNode() == isScrollbarThumb) &&
2942 (aNode->GetScrollbarDirection() == direction) &&
2943 (aNode->GetScrollTargetId() == scrollId);
2944 });
2945 }
2946
2947 hit.mHitResult = hitInfo;
2948
2949 if (scrollbarNode) {
2950 RefPtr<HitTestingTreeNode> scrollbarRef = scrollbarNode;
2951 hit.mScrollbarNode.Initialize(aProofOfTreeLock, scrollbarRef.forget(),
2952 mTreeLock);
2953 }
2954
2955 hit.mFixedPosSides = sideBits;
2956
2957 hit.mHitOverscrollGutter =
2958 hit.mTargetApzc && hit.mTargetApzc->IsInOverscrollGutter(aHitTestPoint);
2959
2960 return hit;
2961 }
2962
FindHandoffParent(const AsyncPanZoomController * aApzc)2963 AsyncPanZoomController* APZCTreeManager::FindHandoffParent(
2964 const AsyncPanZoomController* aApzc) {
2965 RefPtr<HitTestingTreeNode> node = GetTargetNode(aApzc->GetGuid(), nullptr);
2966 while (node) {
2967 if (auto* apzc = GetTargetApzcForNode(node->GetParent())) {
2968 // avoid infinite recursion in the overscroll handoff chain.
2969 if (apzc != aApzc) {
2970 return apzc;
2971 }
2972 }
2973 node = node->GetParent();
2974 }
2975
2976 return nullptr;
2977 }
2978
2979 RefPtr<const OverscrollHandoffChain>
BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController> & aInitialTarget)2980 APZCTreeManager::BuildOverscrollHandoffChain(
2981 const RefPtr<AsyncPanZoomController>& aInitialTarget) {
2982 // Scroll grabbing is a mechanism that allows content to specify that
2983 // the initial target of a pan should be not the innermost scrollable
2984 // frame at the touch point (which is what GetTargetAPZC finds), but
2985 // something higher up in the tree.
2986 // It's not sufficient to just find the initial target, however, as
2987 // overscroll can be handed off to another APZC. Without scroll grabbing,
2988 // handoff just occurs from child to parent. With scroll grabbing, the
2989 // handoff order can be different, so we build a chain of APZCs in the
2990 // order in which scroll will be handed off to them.
2991
2992 // Grab tree lock since we'll be walking the APZC tree.
2993 RecursiveMutexAutoLock lock(mTreeLock);
2994
2995 // Build the chain. If there is a scroll parent link, we use that. This is
2996 // needed to deal with scroll info layers, because they participate in handoff
2997 // but do not follow the expected layer tree structure. If there are no
2998 // scroll parent links we just walk up the tree to find the scroll parent.
2999 OverscrollHandoffChain* result = new OverscrollHandoffChain;
3000 AsyncPanZoomController* apzc = aInitialTarget;
3001 while (apzc != nullptr) {
3002 result->Add(apzc);
3003
3004 if (apzc->GetScrollHandoffParentId() ==
3005 ScrollableLayerGuid::NULL_SCROLL_ID) {
3006 if (!apzc->IsRootForLayersId()) {
3007 // This probably indicates a bug or missed case in layout code
3008 NS_WARNING("Found a non-root APZ with no handoff parent");
3009 }
3010
3011 // Find the parent APZC by using HitTestingTree (i.e. by using
3012 // GetTargetApzcForNode) so that we can properly find the parent APZC for
3013 // cases where cross-process iframes are inside position:fixed subtree.
3014 apzc = FindHandoffParent(apzc);
3015 continue;
3016 }
3017
3018 // Guard against a possible infinite-loop condition. If we hit this, the
3019 // layout code that generates the handoff parents did something wrong.
3020 MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
3021 RefPtr<AsyncPanZoomController> scrollParent = GetTargetAPZC(
3022 apzc->GetGuid().mLayersId, apzc->GetScrollHandoffParentId());
3023 apzc = scrollParent.get();
3024 }
3025
3026 // Now adjust the chain to account for scroll grabbing. Sorting is a bit
3027 // of an overkill here, but scroll grabbing will likely be generalized
3028 // to scroll priorities, so we might as well do it this way.
3029 result->SortByScrollPriority();
3030
3031 // Print the overscroll chain for debugging.
3032 for (uint32_t i = 0; i < result->Length(); ++i) {
3033 APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i,
3034 result->GetApzcAtIndex(i).get());
3035 }
3036
3037 return result;
3038 }
3039
SetLongTapEnabled(bool aLongTapEnabled)3040 void APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled) {
3041 APZThreadUtils::AssertOnControllerThread();
3042 GestureEventListener::SetLongTapEnabled(aLongTapEnabled);
3043 }
3044
AddInputBlockCallback(uint64_t aInputBlockId,InputBlockCallback && aCallback)3045 void APZCTreeManager::AddInputBlockCallback(uint64_t aInputBlockId,
3046 InputBlockCallback&& aCallback) {
3047 mInputQueue->AddInputBlockCallback(aInputBlockId, std::move(aCallback));
3048 }
3049
FindScrollThumbNode(const AsyncDragMetrics & aDragMetrics,LayersId aLayersId,HitTestingTreeNodeAutoLock & aOutThumbNode)3050 void APZCTreeManager::FindScrollThumbNode(
3051 const AsyncDragMetrics& aDragMetrics, LayersId aLayersId,
3052 HitTestingTreeNodeAutoLock& aOutThumbNode) {
3053 if (!aDragMetrics.mDirection) {
3054 // The AsyncDragMetrics has not been initialized yet - there will be
3055 // no matching node, so don't bother searching the tree.
3056 return;
3057 }
3058
3059 RecursiveMutexAutoLock lock(mTreeLock);
3060
3061 RefPtr<HitTestingTreeNode> result = DepthFirstSearch<ReverseIterator>(
3062 mRootNode.get(), [&aDragMetrics, &aLayersId](HitTestingTreeNode* aNode) {
3063 return aNode->MatchesScrollDragMetrics(aDragMetrics, aLayersId);
3064 });
3065 if (result) {
3066 aOutThumbNode.Initialize(lock, result.forget(), mTreeLock);
3067 }
3068 }
3069
GetTargetApzcForNode(HitTestingTreeNode * aNode)3070 AsyncPanZoomController* APZCTreeManager::GetTargetApzcForNode(
3071 HitTestingTreeNode* aNode) {
3072 for (const HitTestingTreeNode* n = aNode;
3073 n && n->GetLayersId() == aNode->GetLayersId(); n = n->GetParent()) {
3074 if (n->GetApzc()) {
3075 APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
3076 return n->GetApzc();
3077 }
3078 if (n->GetFixedPosTarget() != ScrollableLayerGuid::NULL_SCROLL_ID) {
3079 RefPtr<AsyncPanZoomController> fpTarget =
3080 GetTargetAPZC(n->GetLayersId(), n->GetFixedPosTarget());
3081 APZCTM_LOG("Found target APZC %p using fixed-pos lookup on %" PRIu64 "\n",
3082 fpTarget.get(), n->GetFixedPosTarget());
3083 return fpTarget.get();
3084 }
3085 }
3086 return nullptr;
3087 }
3088
GetAPZCAtPoint(const ScreenPoint & aHitTestPoint,const RecursiveMutexAutoLock & aProofOfTreeLock)3089 APZCTreeManager::HitTestResult APZCTreeManager::GetAPZCAtPoint(
3090 const ScreenPoint& aHitTestPoint,
3091 const RecursiveMutexAutoLock& aProofOfTreeLock) {
3092 HitTestResult hit;
3093 // This walks the tree in depth-first, reverse order, so that it encounters
3094 // APZCs front-to-back on the screen.
3095 HitTestingTreeNode* resultNode = nullptr;
3096 HitTestingTreeNode* root = mRootNode;
3097 HitTestingTreeNode* scrollbarNode = nullptr;
3098 std::stack<LayerPoint> hitTestPoints;
3099 ParentLayerPoint point = ViewAs<ParentLayerPixel>(
3100 aHitTestPoint, PixelCastJustification::ScreenIsParentLayerForRoot);
3101 hitTestPoints.push(
3102 ViewAs<LayerPixel>(point, PixelCastJustification::MovingDownToChildren));
3103
3104 ForEachNode<ReverseIterator>(
3105 root,
3106 [&resultNode, &hitTestPoints, &hit, this](HitTestingTreeNode* aNode) {
3107 ParentLayerPoint hitTestPointForParent = ViewAs<ParentLayerPixel>(
3108 hitTestPoints.top(), PixelCastJustification::MovingDownToChildren);
3109 if (aNode->IsOutsideClip(hitTestPointForParent)) {
3110 // If the point being tested is outside the clip region for this node
3111 // then we don't need to test against this node or any of its
3112 // children. Just skip it and move on.
3113 APZCTM_LOG("Point %s outside clip for node %p\n",
3114 ToString(hitTestPointForParent).c_str(), aNode);
3115 return TraversalFlag::Skip;
3116 }
3117 // If this node has a transform that includes an overscroll transform,
3118 // check if the point is inside the corresponding APZC's overscroll
3119 // gutter. We do this here in the pre-action because we need the
3120 // hit-test point in ParentLayer coordinates for this check. If the
3121 // point is in the gutter, we can abort the search and target this node.
3122 // (Note that no descendant node would be a match if we're in the
3123 // gutter.)
3124 const AsyncPanZoomController* sourceOfOverscrollTransform = nullptr;
3125 auto transform =
3126 ComputeTransformForNode(aNode, &sourceOfOverscrollTransform);
3127 if (sourceOfOverscrollTransform &&
3128 sourceOfOverscrollTransform->IsInOverscrollGutter(
3129 hitTestPointForParent)) {
3130 APZCTM_LOG(
3131 "ParentLayer point %s in overscroll gutter of APZC %p (node "
3132 "%p)\n",
3133 ToString(hitTestPointForParent).c_str(), aNode->GetApzc(), aNode);
3134 resultNode = aNode;
3135 // We want to target the overscrolled APZC, but if we're over the
3136 // gutter then we're not over its hit or DTC regions. Use
3137 // {eVisibleToHitTest} as the hit result because the event won't be
3138 // sent to gecko (so DTC flags are irrelevant), and we do want
3139 // browser default actions to work (e.g. scrolling to relieve the
3140 // overscroll).
3141 hit.mHitResult = {CompositorHitTestFlags::eVisibleToHitTest};
3142 hit.mHitOverscrollGutter = true;
3143 return TraversalFlag::Abort;
3144 }
3145 // First check the subtree rooted at this node, because deeper nodes
3146 // are more "in front".
3147 Maybe<LayerPoint> hitTestPoint =
3148 aNode->Untransform(hitTestPointForParent, transform);
3149 APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
3150 ToString(hitTestPointForParent).c_str(),
3151 hitTestPoint ? ToString(hitTestPoint.ref()).c_str() : "nil");
3152 if (!hitTestPoint) {
3153 return TraversalFlag::Skip;
3154 }
3155 hitTestPoints.push(hitTestPoint.ref());
3156 return TraversalFlag::Continue;
3157 },
3158 [&resultNode, &hitTestPoints, &hit](HitTestingTreeNode* aNode) {
3159 CompositorHitTestInfo hitResult = aNode->HitTest(hitTestPoints.top());
3160 hitTestPoints.pop();
3161 APZCTM_LOG("Testing Layer point %s against node %p\n",
3162 ToString(hitTestPoints.top()).c_str(), aNode);
3163 if (hitResult != CompositorHitTestInvisibleToHit) {
3164 resultNode = aNode;
3165 hit.mHitResult = hitResult;
3166 return TraversalFlag::Abort;
3167 }
3168 return TraversalFlag::Continue;
3169 });
3170
3171 if (hit.mHitResult != CompositorHitTestInvisibleToHit) {
3172 MOZ_ASSERT(resultNode);
3173 for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
3174 if (n->IsScrollbarNode()) {
3175 scrollbarNode = n;
3176 hit.mHitResult += CompositorHitTestFlags::eScrollbar;
3177 if (n->IsScrollThumbNode()) {
3178 hit.mHitResult += CompositorHitTestFlags::eScrollbarThumb;
3179 }
3180 if (n->GetScrollbarDirection() == ScrollDirection::eVertical) {
3181 hit.mHitResult += CompositorHitTestFlags::eScrollbarVertical;
3182 }
3183
3184 // If we hit a scrollbar, target the APZC for the content scrolled
3185 // by the scrollbar. (The scrollbar itself doesn't scroll with the
3186 // scrolled content, so it doesn't carry the scrolled content's
3187 // scroll metadata).
3188 RefPtr<AsyncPanZoomController> scrollTarget =
3189 GetTargetAPZC(n->GetLayersId(), n->GetScrollTargetId());
3190 if (scrollTarget) {
3191 hit.mLayersId = n->GetLayersId();
3192 hit.mTargetApzc = std::move(scrollTarget);
3193 RefPtr<HitTestingTreeNode> scrollbarRef = scrollbarNode;
3194 hit.mScrollbarNode.Initialize(aProofOfTreeLock, scrollbarRef.forget(),
3195 mTreeLock);
3196 return hit;
3197 }
3198 } else if (IsFixedToRootContent(n)) {
3199 hit.mFixedPosSides = n->GetFixedPosSides();
3200 }
3201 }
3202
3203 hit.mTargetApzc = GetTargetApzcForNode(resultNode);
3204 if (!hit.mTargetApzc) {
3205 hit.mTargetApzc = FindRootApzcForLayersId(resultNode->GetLayersId());
3206 MOZ_ASSERT(hit.mTargetApzc);
3207 APZCTM_LOG("Found target %p using root lookup\n", hit.mTargetApzc.get());
3208 }
3209 APZCTM_LOG("Successfully matched APZC %p via node %p (hit result 0x%x)\n",
3210 hit.mTargetApzc.get(), resultNode, hit.mHitResult.serialize());
3211 hit.mLayersId = resultNode->GetLayersId();
3212 }
3213
3214 // If we found an APZC that wasn't directly on the result node, we haven't
3215 // checked if we're in its overscroll gutter, so check now.
3216 if (hit.mTargetApzc && resultNode &&
3217 (hit.mTargetApzc != resultNode->GetApzc())) {
3218 hit.mHitOverscrollGutter =
3219 hit.mTargetApzc->IsInOverscrollGutter(aHitTestPoint);
3220 }
3221
3222 return hit;
3223 }
3224
FindRootNodeForLayersId(LayersId aLayersId) const3225 HitTestingTreeNode* APZCTreeManager::FindRootNodeForLayersId(
3226 LayersId aLayersId) const {
3227 mTreeLock.AssertCurrentThreadIn();
3228
3229 HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(
3230 mRootNode.get(), [aLayersId](HitTestingTreeNode* aNode) {
3231 AsyncPanZoomController* apzc = aNode->GetApzc();
3232 return apzc && apzc->GetLayersId() == aLayersId &&
3233 apzc->IsRootForLayersId();
3234 });
3235 return resultNode;
3236 }
3237
FindRootApzcForLayersId(LayersId aLayersId) const3238 AsyncPanZoomController* APZCTreeManager::FindRootApzcForLayersId(
3239 LayersId aLayersId) const {
3240 mTreeLock.AssertCurrentThreadIn();
3241
3242 HitTestingTreeNode* resultNode = FindRootNodeForLayersId(aLayersId);
3243 return resultNode ? resultNode->GetApzc() : nullptr;
3244 }
3245
FindZoomableApzc(AsyncPanZoomController * aStart) const3246 already_AddRefed<AsyncPanZoomController> APZCTreeManager::FindZoomableApzc(
3247 AsyncPanZoomController* aStart) const {
3248 return GetZoomableTarget(aStart, aStart);
3249 }
3250
GetGeckoFixedLayerMargins() const3251 ScreenMargin APZCTreeManager::GetGeckoFixedLayerMargins() const {
3252 RecursiveMutexAutoLock lock(mTreeLock);
3253 return mGeckoFixedLayerMargins;
3254 }
3255
GetCompositorFixedLayerMargins() const3256 ScreenMargin APZCTreeManager::GetCompositorFixedLayerMargins() const {
3257 RecursiveMutexAutoLock lock(mTreeLock);
3258 return mCompositorFixedLayerMargins;
3259 }
3260
FindRootContentApzcForLayersId(LayersId aLayersId) const3261 AsyncPanZoomController* APZCTreeManager::FindRootContentApzcForLayersId(
3262 LayersId aLayersId) const {
3263 mTreeLock.AssertCurrentThreadIn();
3264
3265 HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(
3266 mRootNode.get(), [aLayersId](HitTestingTreeNode* aNode) {
3267 AsyncPanZoomController* apzc = aNode->GetApzc();
3268 return apzc && apzc->GetLayersId() == aLayersId &&
3269 apzc->IsRootContent();
3270 });
3271 return resultNode ? resultNode->GetApzc() : nullptr;
3272 }
3273
3274 // clang-format off
3275 /* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
3276 some useful transformations that input events may need applied. This is best
3277 illustrated with an example. Consider a chain of layers, L, M, N, O, P, Q, R. Layer L
3278 is the layer that corresponds to the argument |aApzc|, and layer R is the root
3279 of the layer tree. Layer M is the parent of L, N is the parent of M, and so on.
3280 When layer L is displayed to the screen by the compositor, the set of transforms that
3281 are applied to L are (in order from top to bottom):
3282
3283 L's CSS transform (hereafter referred to as transform matrix LC)
3284 L's nontransient async transform (hereafter referred to as transform matrix LN)
3285 L's transient async transform (hereafter referred to as transform matrix LT)
3286 M's CSS transform (hereafter referred to as transform matrix MC)
3287 M's nontransient async transform (hereafter referred to as transform matrix MN)
3288 M's transient async transform (hereafter referred to as transform matrix MT)
3289 ...
3290 R's CSS transform (hereafter referred to as transform matrix RC)
3291 R's nontransient async transform (hereafter referred to as transform matrix RN)
3292 R's transient async transform (hereafter referred to as transform matrix RT)
3293
3294 Also, for any layer, the async transform is the combination of its transient and non-transient
3295 parts. That is, for any layer L:
3296 LA === LN * LT
3297 LA.Inverse() === LT.Inverse() * LN.Inverse()
3298
3299 If we want user input to modify L's transient async transform, we have to first convert
3300 user input from screen space to the coordinate space of L's transient async transform. Doing
3301 this involves applying the following transforms (in order from top to bottom):
3302 RT.Inverse()
3303 RN.Inverse()
3304 RC.Inverse()
3305 ...
3306 MT.Inverse()
3307 MN.Inverse()
3308 MC.Inverse()
3309 This combined transformation is returned by GetScreenToApzcTransform().
3310
3311 Next, if we want user inputs sent to gecko for event-dispatching, we will need to strip
3312 out all of the async transforms that are involved in this chain. This is because async
3313 transforms are stored only in the compositor and gecko does not account for them when
3314 doing display-list-based hit-testing for event dispatching.
3315 Furthermore, because these input events are processed by Gecko in a FIFO queue that
3316 includes other things (specifically paint requests), it is possible that by time the
3317 input event reaches gecko, it will have painted something else. Therefore, we need to
3318 apply another transform to the input events to account for the possible disparity between
3319 what we know gecko last painted and the last paint request we sent to gecko. Let this
3320 transform be represented by LD, MD, ... RD.
3321 Therefore, given a user input in screen space, the following transforms need to be applied
3322 (in order from top to bottom):
3323 RT.Inverse()
3324 RN.Inverse()
3325 RC.Inverse()
3326 ...
3327 MT.Inverse()
3328 MN.Inverse()
3329 MC.Inverse()
3330 LT.Inverse()
3331 LN.Inverse()
3332 LC.Inverse()
3333 LC
3334 LD
3335 MC
3336 MD
3337 ...
3338 RC
3339 RD
3340 This sequence can be simplified and refactored to the following:
3341 GetScreenToApzcTransform()
3342 LA.Inverse()
3343 LD
3344 MC
3345 MD
3346 ...
3347 RC
3348 RD
3349 Since GetScreenToApzcTransform() can be obtained by calling that function, GetApzcToGeckoTransform()
3350 returns the remaining transforms (LA.Inverse() * LD * ... * RD), so that the caller code can
3351 combine it with GetScreenToApzcTransform() to get the final transform required in this case.
3352
3353 Note that for many of these layers, there will be no AsyncPanZoomController attached, and
3354 so the async transform will be the identity transform. So, in the example above, if layers
3355 L and P have APZC instances attached, MT, MN, MD, NT, NN, ND, OT, ON, OD, QT, QN, QD, RT,
3356 RN and RD will be identity transforms.
3357 Additionally, for space-saving purposes, each APZC instance stores its layer's individual
3358 CSS transform and the accumulation of CSS transforms to its parent APZC. So the APZC for
3359 layer L would store LC and (MC * NC * OC), and the layer P would store PC and (QC * RC).
3360 The APZC instances track the last dispatched paint request and so are able to calculate LD and
3361 PD using those internally stored values.
3362 The APZCs also obviously have LT, LN, PT, and PN, so all of the above transformation combinations
3363 required can be generated.
3364 */
3365 // clang-format on
3366
3367 /*
3368 * See the long comment above for a detailed explanation of this function.
3369 */
GetScreenToApzcTransform(const AsyncPanZoomController * aApzc) const3370 ScreenToParentLayerMatrix4x4 APZCTreeManager::GetScreenToApzcTransform(
3371 const AsyncPanZoomController* aApzc) const {
3372 Matrix4x4 result;
3373 RecursiveMutexAutoLock lock(mTreeLock);
3374
3375 // The comments below assume there is a chain of layers L..R with L and P
3376 // having APZC instances as explained in the comment above. This function is
3377 // called with aApzc at L, and the loop below performs one iteration, where
3378 // parent is at P. The comments explain what values are stored in the
3379 // variables at these two levels. All the comments use standard matrix
3380 // notation where the leftmost matrix in a multiplication is applied first.
3381
3382 // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() *
3383 // MC.Inverse()
3384 Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
3385
3386 // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() *
3387 // MC.Inverse()
3388 result = ancestorUntransform;
3389
3390 for (AsyncPanZoomController* parent = aApzc->GetParent(); parent;
3391 parent = parent->GetParent()) {
3392 // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent
3393 // == P
3394 ancestorUntransform = parent->GetAncestorTransform().Inverse();
3395 // asyncUntransform is updated to PA.Inverse() when parent == P
3396 Matrix4x4 asyncUntransform = parent
3397 ->GetCurrentAsyncTransformWithOverscroll(
3398 AsyncPanZoomController::eForHitTesting)
3399 .Inverse()
3400 .ToUnknownMatrix();
3401 // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
3402 Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
3403
3404 // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() *
3405 // OC.Inverse() * NC.Inverse() * MC.Inverse()
3406 result = untransformSinceLastApzc * result;
3407
3408 // The above value for result when parent == P matches the required output
3409 // as explained in the comment above this method. Note that any missing
3410 // terms are guaranteed to be identity transforms.
3411 }
3412
3413 return ViewAs<ScreenToParentLayerMatrix4x4>(result);
3414 }
3415
3416 /*
3417 * See the long comment above GetScreenToApzcTransform() for a detailed
3418 * explanation of this function.
3419 */
GetApzcToGeckoTransform(const AsyncPanZoomController * aApzc) const3420 ParentLayerToScreenMatrix4x4 APZCTreeManager::GetApzcToGeckoTransform(
3421 const AsyncPanZoomController* aApzc) const {
3422 Matrix4x4 result;
3423 RecursiveMutexAutoLock lock(mTreeLock);
3424
3425 // The comments below assume there is a chain of layers L..R with L and P
3426 // having APZC instances as explained in the comment above. This function is
3427 // called with aApzc at L, and the loop below performs one iteration, where
3428 // parent is at P. The comments explain what values are stored in the
3429 // variables at these two levels. All the comments use standard matrix
3430 // notation where the leftmost matrix in a multiplication is applied first.
3431
3432 // asyncUntransform is LA.Inverse()
3433 Matrix4x4 asyncUntransform = aApzc
3434 ->GetCurrentAsyncTransformWithOverscroll(
3435 AsyncPanZoomController::eForHitTesting)
3436 .Inverse()
3437 .ToUnknownMatrix();
3438
3439 // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC *
3440 // PC
3441 result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() *
3442 aApzc->GetAncestorTransform();
3443
3444 for (AsyncPanZoomController* parent = aApzc->GetParent(); parent;
3445 parent = parent->GetParent()) {
3446 // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC *
3447 // RC
3448 result = result * parent->GetTransformToLastDispatchedPaint() *
3449 parent->GetAncestorTransform();
3450
3451 // The above value for result when parent == P matches the required output
3452 // as explained in the comment above this method. Note that any missing
3453 // terms are guaranteed to be identity transforms.
3454 }
3455
3456 return ViewAs<ParentLayerToScreenMatrix4x4>(result);
3457 }
3458
GetCurrentMousePosition() const3459 ScreenPoint APZCTreeManager::GetCurrentMousePosition() const {
3460 auto pos = mCurrentMousePosition.Lock();
3461 return pos.ref();
3462 }
3463
SetCurrentMousePosition(const ScreenPoint & aNewPos)3464 void APZCTreeManager::SetCurrentMousePosition(const ScreenPoint& aNewPos) {
3465 auto pos = mCurrentMousePosition.Lock();
3466 pos.ref() = aNewPos;
3467 }
3468
GetApzcWithDifferentLayersIdByWalkingParents(AsyncPanZoomController * aApzc)3469 static AsyncPanZoomController* GetApzcWithDifferentLayersIdByWalkingParents(
3470 AsyncPanZoomController* aApzc) {
3471 if (!aApzc) {
3472 return nullptr;
3473 }
3474 AsyncPanZoomController* parent = aApzc->GetParent();
3475 while (parent && (parent->GetLayersId() == aApzc->GetLayersId())) {
3476 parent = parent->GetParent();
3477 }
3478 return parent;
3479 }
3480
GetZoomableTarget(AsyncPanZoomController * aApzc1,AsyncPanZoomController * aApzc2) const3481 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetZoomableTarget(
3482 AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const {
3483 RecursiveMutexAutoLock lock(mTreeLock);
3484 RefPtr<AsyncPanZoomController> apzc;
3485 // For now, we only ever want to do pinching on the root-content APZC for
3486 // a given layers id.
3487 if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
3488 // If the two APZCs have the same layers id, find the root-content APZC
3489 // for that layers id. Don't call CommonAncestor() because there may not
3490 // be a common ancestor for the layers id (e.g. if one APZCs is inside a
3491 // fixed-position element).
3492 apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
3493 if (apzc) {
3494 return apzc.forget();
3495 }
3496 }
3497
3498 // Otherwise, find the common ancestor (to reach a common layers id), and then
3499 // walk up the apzc tree until we find a root-content APZC.
3500 apzc = CommonAncestor(aApzc1, aApzc2);
3501 RefPtr<AsyncPanZoomController> zoomable;
3502 while (apzc && !zoomable) {
3503 zoomable = FindRootContentApzcForLayersId(apzc->GetLayersId());
3504 apzc = GetApzcWithDifferentLayersIdByWalkingParents(apzc);
3505 }
3506
3507 return zoomable.forget();
3508 }
3509
ConvertToGecko(const ScreenIntPoint & aPoint,AsyncPanZoomController * aApzc)3510 Maybe<ScreenIntPoint> APZCTreeManager::ConvertToGecko(
3511 const ScreenIntPoint& aPoint, AsyncPanZoomController* aApzc) {
3512 RecursiveMutexAutoLock lock(mTreeLock);
3513 ScreenToScreenMatrix4x4 transformScreenToGecko =
3514 GetScreenToApzcTransform(aApzc) * GetApzcToGeckoTransform(aApzc);
3515 Maybe<ScreenIntPoint> geckoPoint =
3516 UntransformBy(transformScreenToGecko, aPoint);
3517 if (geckoPoint) {
3518 if (mTouchBlockHitResult.mFixedPosSides != SideBits::eNone) {
3519 MutexAutoLock mapLock(mMapLock);
3520 *geckoPoint -= RoundedToInt(apz::ComputeFixedMarginsOffset(
3521 GetCompositorFixedLayerMargins(mapLock),
3522 mTouchBlockHitResult.mFixedPosSides, mGeckoFixedLayerMargins));
3523 }
3524 }
3525 return geckoPoint;
3526 }
3527
CommonAncestor(AsyncPanZoomController * aApzc1,AsyncPanZoomController * aApzc2) const3528 already_AddRefed<AsyncPanZoomController> APZCTreeManager::CommonAncestor(
3529 AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const {
3530 mTreeLock.AssertCurrentThreadIn();
3531 RefPtr<AsyncPanZoomController> ancestor;
3532
3533 // If either aApzc1 or aApzc2 is null, min(depth1, depth2) will be 0 and this
3534 // function will return null.
3535
3536 // Calculate depth of the APZCs in the tree
3537 int depth1 = 0, depth2 = 0;
3538 for (AsyncPanZoomController* parent = aApzc1; parent;
3539 parent = parent->GetParent()) {
3540 depth1++;
3541 }
3542 for (AsyncPanZoomController* parent = aApzc2; parent;
3543 parent = parent->GetParent()) {
3544 depth2++;
3545 }
3546
3547 // At most one of the following two loops will be executed; the deeper APZC
3548 // pointer will get walked up to the depth of the shallower one.
3549 int minDepth = depth1 < depth2 ? depth1 : depth2;
3550 while (depth1 > minDepth) {
3551 depth1--;
3552 aApzc1 = aApzc1->GetParent();
3553 }
3554 while (depth2 > minDepth) {
3555 depth2--;
3556 aApzc2 = aApzc2->GetParent();
3557 }
3558
3559 // Walk up the ancestor chains of both APZCs, always staying at the same depth
3560 // for either APZC, and return the the first common ancestor encountered.
3561 while (true) {
3562 if (aApzc1 == aApzc2) {
3563 ancestor = aApzc1;
3564 break;
3565 }
3566 if (depth1 <= 0) {
3567 break;
3568 }
3569 aApzc1 = aApzc1->GetParent();
3570 aApzc2 = aApzc2->GetParent();
3571 }
3572 return ancestor.forget();
3573 }
3574
IsFixedToRootContent(const HitTestingTreeNode * aNode) const3575 bool APZCTreeManager::IsFixedToRootContent(
3576 const HitTestingTreeNode* aNode) const {
3577 MutexAutoLock lock(mMapLock);
3578 return IsFixedToRootContent(FixedPositionInfo(aNode), lock);
3579 }
3580
IsFixedToRootContent(const FixedPositionInfo & aFixedInfo,const MutexAutoLock & aProofOfMapLock) const3581 bool APZCTreeManager::IsFixedToRootContent(
3582 const FixedPositionInfo& aFixedInfo,
3583 const MutexAutoLock& aProofOfMapLock) const {
3584 ScrollableLayerGuid::ViewID fixedTarget = aFixedInfo.mFixedPosTarget;
3585 if (fixedTarget == ScrollableLayerGuid::NULL_SCROLL_ID) {
3586 return false;
3587 }
3588 auto it =
3589 mApzcMap.find(ScrollableLayerGuid(aFixedInfo.mLayersId, 0, fixedTarget));
3590 if (it == mApzcMap.end()) {
3591 return false;
3592 }
3593 RefPtr<AsyncPanZoomController> targetApzc = it->second.apzc;
3594 return targetApzc && targetApzc->IsRootContent();
3595 }
3596
SidesStuckToRootContent(const HitTestingTreeNode * aNode) const3597 SideBits APZCTreeManager::SidesStuckToRootContent(
3598 const HitTestingTreeNode* aNode) const {
3599 MutexAutoLock lock(mMapLock);
3600 return SidesStuckToRootContent(StickyPositionInfo(aNode), lock);
3601 }
3602
SidesStuckToRootContent(const StickyPositionInfo & aStickyInfo,const MutexAutoLock & aProofOfMapLock) const3603 SideBits APZCTreeManager::SidesStuckToRootContent(
3604 const StickyPositionInfo& aStickyInfo,
3605 const MutexAutoLock& aProofOfMapLock) const {
3606 SideBits result = SideBits::eNone;
3607
3608 ScrollableLayerGuid::ViewID stickyTarget = aStickyInfo.mStickyPosTarget;
3609 if (stickyTarget == ScrollableLayerGuid::NULL_SCROLL_ID) {
3610 return result;
3611 }
3612
3613 // We support the dynamic toolbar at top and bottom.
3614 if ((aStickyInfo.mFixedPosSides & SideBits::eTopBottom) == SideBits::eNone) {
3615 return result;
3616 }
3617
3618 auto it = mApzcMap.find(
3619 ScrollableLayerGuid(aStickyInfo.mLayersId, 0, stickyTarget));
3620 if (it == mApzcMap.end()) {
3621 return result;
3622 }
3623 RefPtr<AsyncPanZoomController> stickyTargetApzc = it->second.apzc;
3624 if (!stickyTargetApzc || !stickyTargetApzc->IsRootContent()) {
3625 return result;
3626 }
3627
3628 ParentLayerPoint translation =
3629 stickyTargetApzc
3630 ->GetCurrentAsyncTransform(
3631 AsyncPanZoomController::eForHitTesting,
3632 AsyncTransformComponents{AsyncTransformComponent::eLayout})
3633 .mTranslation;
3634
3635 if (apz::IsStuckAtTop(translation.y, aStickyInfo.mStickyScrollRangeInner,
3636 aStickyInfo.mStickyScrollRangeOuter)) {
3637 result |= SideBits::eTop;
3638 }
3639 if (apz::IsStuckAtBottom(translation.y, aStickyInfo.mStickyScrollRangeInner,
3640 aStickyInfo.mStickyScrollRangeOuter)) {
3641 result |= SideBits::eBottom;
3642 }
3643 return result;
3644 }
3645
ComputeTransformForNode(const HitTestingTreeNode * aNode,const AsyncPanZoomController ** aOutSourceOfOverscrollTransform) const3646 LayerToParentLayerMatrix4x4 APZCTreeManager::ComputeTransformForNode(
3647 const HitTestingTreeNode* aNode,
3648 const AsyncPanZoomController** aOutSourceOfOverscrollTransform) const {
3649 mTreeLock.AssertCurrentThreadIn();
3650 // The async transforms applied here for hit-testing purposes, are intended
3651 // to match the ones AsyncCompositionManager (or equivalent WebRender code)
3652 // applies for rendering purposes.
3653 // Note that with containerless scrolling, the layer structure looks like
3654 // this:
3655 //
3656 // root container layer
3657 // async zoom container layer
3658 // scrollable content layers (with scroll metadata)
3659 // fixed content layers (no scroll metadta, annotated isFixedPosition)
3660 // scrollbar layers
3661 //
3662 // The intended async transforms in this case are:
3663 // * On the async zoom container layer, the "visual" portion of the root
3664 // content APZC's async transform (which includes the zoom, and async
3665 // scrolling of the visual viewport relative to the layout viewport).
3666 // * On the scrollable layers bearing the root content APZC's scroll
3667 // metadata, the "layout" portion of the root content APZC's async
3668 // transform (which includes async scrolling of the layout viewport
3669 // relative to the scrollable rect origin).
3670 if (AsyncPanZoomController* apzc = aNode->GetApzc()) {
3671 // If the node represents scrollable content, apply the async transform
3672 // from its APZC.
3673 bool visualTransformIsInheritedFromAncestor =
3674 /* we're the APZC whose visual transform might be on the async
3675 zoom container */
3676 apzc->IsRootContent() &&
3677 /* there is an async zoom container on this subtree */
3678 mAsyncZoomContainerSubtree == Some(aNode->GetLayersId()) &&
3679 /* it's not us */
3680 !aNode->GetAsyncZoomContainerId();
3681 AsyncTransformComponents components =
3682 visualTransformIsInheritedFromAncestor
3683 ? AsyncTransformComponents{AsyncTransformComponent::eLayout}
3684 : LayoutAndVisual;
3685 if (aOutSourceOfOverscrollTransform &&
3686 components.contains(AsyncTransformComponent::eVisual)) {
3687 *aOutSourceOfOverscrollTransform = apzc;
3688 }
3689 return aNode->GetTransform() *
3690 CompleteAsyncTransform(apzc->GetCurrentAsyncTransformWithOverscroll(
3691 AsyncPanZoomController::eForHitTesting, components));
3692 } else if (aNode->GetAsyncZoomContainerId()) {
3693 if (AsyncPanZoomController* rootContent =
3694 FindRootContentApzcForLayersId(aNode->GetLayersId())) {
3695 if (aOutSourceOfOverscrollTransform) {
3696 *aOutSourceOfOverscrollTransform = rootContent;
3697 }
3698 return aNode->GetTransform() *
3699 CompleteAsyncTransform(
3700 rootContent->GetCurrentAsyncTransformWithOverscroll(
3701 AsyncPanZoomController::eForHitTesting,
3702 {AsyncTransformComponent::eVisual}));
3703 }
3704 } else if (aNode->IsScrollThumbNode()) {
3705 // If the node represents a scrollbar thumb, compute and apply the
3706 // transformation that will be applied to the thumb in
3707 // AsyncCompositionManager.
3708 ScrollableLayerGuid guid{aNode->GetLayersId(), 0,
3709 aNode->GetScrollTargetId()};
3710 if (RefPtr<HitTestingTreeNode> scrollTargetNode =
3711 GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
3712 AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
3713 MOZ_ASSERT(scrollTargetApzc);
3714 return scrollTargetApzc->CallWithLastContentPaintMetrics(
3715 [&](const FrameMetrics& aMetrics) {
3716 return ComputeTransformForScrollThumb(
3717 aNode->GetTransform() * AsyncTransformMatrix(),
3718 scrollTargetNode->GetTransform().ToUnknownMatrix(),
3719 scrollTargetApzc, aMetrics, aNode->GetScrollbarData(),
3720 scrollTargetNode->IsAncestorOf(aNode), nullptr);
3721 });
3722 }
3723 } else if (IsFixedToRootContent(aNode)) {
3724 ParentLayerPoint translation;
3725 {
3726 MutexAutoLock mapLock(mMapLock);
3727 translation = ViewAs<ParentLayerPixel>(
3728 apz::ComputeFixedMarginsOffset(
3729 GetCompositorFixedLayerMargins(mapLock),
3730 aNode->GetFixedPosSides(), mGeckoFixedLayerMargins),
3731 PixelCastJustification::ScreenIsParentLayerForRoot);
3732 }
3733 return aNode->GetTransform() *
3734 CompleteAsyncTransform(
3735 AsyncTransformComponentMatrix::Translation(translation));
3736 }
3737 SideBits sides = SidesStuckToRootContent(aNode);
3738 if (sides != SideBits::eNone) {
3739 ParentLayerPoint translation;
3740 {
3741 MutexAutoLock mapLock(mMapLock);
3742 translation = ViewAs<ParentLayerPixel>(
3743 apz::ComputeFixedMarginsOffset(
3744 GetCompositorFixedLayerMargins(mapLock), sides,
3745 // For sticky layers, we don't need to factor
3746 // mGeckoFixedLayerMargins because Gecko doesn't shift the
3747 // position of sticky elements for dynamic toolbar movements.
3748 ScreenMargin()),
3749 PixelCastJustification::ScreenIsParentLayerForRoot);
3750 }
3751 return aNode->GetTransform() *
3752 CompleteAsyncTransform(
3753 AsyncTransformComponentMatrix::Translation(translation));
3754 }
3755 // Otherwise, the node does not have an async transform.
3756 return aNode->GetTransform() * AsyncTransformMatrix();
3757 }
3758
GetWebRenderAPI() const3759 already_AddRefed<wr::WebRenderAPI> APZCTreeManager::GetWebRenderAPI() const {
3760 RefPtr<wr::WebRenderAPI> api;
3761 CompositorBridgeParent::CallWithIndirectShadowTree(
3762 mRootLayersId, [&](LayerTreeState& aState) -> void {
3763 if (aState.mWrBridge) {
3764 api = aState.mWrBridge->GetWebRenderAPI();
3765 }
3766 });
3767 return api.forget();
3768 }
3769
3770 /*static*/
GetContentController(LayersId aLayersId)3771 already_AddRefed<GeckoContentController> APZCTreeManager::GetContentController(
3772 LayersId aLayersId) {
3773 RefPtr<GeckoContentController> controller;
3774 CompositorBridgeParent::CallWithIndirectShadowTree(
3775 aLayersId,
3776 [&](LayerTreeState& aState) -> void { controller = aState.mController; });
3777 return controller.forget();
3778 }
3779
GetCompositorFixedLayerMargins(const MutexAutoLock & aProofOfMapLock) const3780 ScreenMargin APZCTreeManager::GetCompositorFixedLayerMargins(
3781 const MutexAutoLock& aProofOfMapLock) const {
3782 ScreenMargin result = mCompositorFixedLayerMargins;
3783 if (StaticPrefs::apz_fixed_margin_override_enabled()) {
3784 result.top = StaticPrefs::apz_fixed_margin_override_top();
3785 result.bottom = StaticPrefs::apz_fixed_margin_override_bottom();
3786 }
3787 return result;
3788 }
3789
GetAPZTestData(LayersId aLayersId,APZTestData * aOutData)3790 bool APZCTreeManager::GetAPZTestData(LayersId aLayersId,
3791 APZTestData* aOutData) {
3792 AssertOnUpdaterThread();
3793
3794 { // copy the relevant test data into aOutData while holding the
3795 // mTestDataLock
3796 MutexAutoLock lock(mTestDataLock);
3797 auto it = mTestData.find(aLayersId);
3798 if (it == mTestData.end()) {
3799 return false;
3800 }
3801 *aOutData = *(it->second);
3802 }
3803
3804 { // add some additional "current state" into the returned APZTestData
3805 MutexAutoLock mapLock(mMapLock);
3806
3807 ClippedCompositionBoundsMap clippedCompBounds;
3808 for (const auto& mapping : mApzcMap) {
3809 if (mapping.first.mLayersId != aLayersId) {
3810 continue;
3811 }
3812
3813 ParentLayerRect clippedBounds = ComputeClippedCompositionBounds(
3814 mapLock, clippedCompBounds, mapping.first);
3815 AsyncPanZoomController* apzc = mapping.second.apzc;
3816 std::string viewId = std::to_string(mapping.first.mScrollId);
3817 std::string apzcState;
3818 if (apzc->GetCheckerboardMagnitude(clippedBounds)) {
3819 apzcState += "checkerboarding,";
3820 }
3821 aOutData->RecordAdditionalData(viewId, apzcState);
3822 }
3823 }
3824 return true;
3825 }
3826
SendSubtreeTransformsToChromeMainThread(const AsyncPanZoomController * aAncestor)3827 void APZCTreeManager::SendSubtreeTransformsToChromeMainThread(
3828 const AsyncPanZoomController* aAncestor) {
3829 RefPtr<GeckoContentController> controller =
3830 GetContentController(mRootLayersId);
3831 if (!controller) {
3832 return;
3833 }
3834 nsTArray<MatrixMessage> messages;
3835 bool underAncestor = (aAncestor == nullptr);
3836 {
3837 RecursiveMutexAutoLock lock(mTreeLock);
3838 if (!mRootNode) {
3839 // Event dispatched during shutdown, after ClearTree().
3840 // Note, mRootNode needs to be checked with mTreeLock held.
3841 return;
3842 }
3843 // This formulation duplicates matrix multiplications closer
3844 // to the root of the tree. For now, aiming for separation
3845 // of concerns rather than minimum number of multiplications.
3846 ForEachNode<ReverseIterator>(
3847 mRootNode.get(),
3848 [&](HitTestingTreeNode* aNode) {
3849 bool atAncestor = (aAncestor && aNode->GetApzc() == aAncestor);
3850 MOZ_ASSERT(!(underAncestor && atAncestor));
3851 underAncestor |= atAncestor;
3852 if (!underAncestor) {
3853 return;
3854 }
3855 LayersId layersId = aNode->GetLayersId();
3856 HitTestingTreeNode* parent = aNode->GetParent();
3857 if (!parent) {
3858 messages.AppendElement(MatrixMessage(Some(LayerToScreenMatrix4x4()),
3859 ScreenRect(), layersId));
3860 } else if (layersId != parent->GetLayersId()) {
3861 if (mDetachedLayersIds.find(layersId) != mDetachedLayersIds.end()) {
3862 messages.AppendElement(
3863 MatrixMessage(Nothing(), ScreenRect(), layersId));
3864 } else {
3865 messages.AppendElement(MatrixMessage(
3866 Some(parent->GetTransformToGecko()),
3867 parent->GetRemoteDocumentScreenRect(), layersId));
3868 }
3869 }
3870 },
3871 [&](HitTestingTreeNode* aNode) {
3872 bool atAncestor = (aAncestor && aNode->GetApzc() == aAncestor);
3873 if (atAncestor) {
3874 MOZ_ASSERT(underAncestor);
3875 underAncestor = false;
3876 }
3877 });
3878 }
3879 controller->NotifyLayerTransforms(std::move(messages));
3880 }
3881
SetFixedLayerMargins(ScreenIntCoord aTop,ScreenIntCoord aBottom)3882 void APZCTreeManager::SetFixedLayerMargins(ScreenIntCoord aTop,
3883 ScreenIntCoord aBottom) {
3884 MutexAutoLock lock(mMapLock);
3885 mCompositorFixedLayerMargins.top = aTop;
3886 mCompositorFixedLayerMargins.bottom = aBottom;
3887 }
3888
3889 /*static*/
ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4 & aCurrentTransform,const Matrix4x4 & aScrollableContentTransform,AsyncPanZoomController * aApzc,const FrameMetrics & aMetrics,const ScrollbarData & aScrollbarData,bool aScrollbarIsDescendant,AsyncTransformComponentMatrix * aOutClipTransform)3890 LayerToParentLayerMatrix4x4 APZCTreeManager::ComputeTransformForScrollThumb(
3891 const LayerToParentLayerMatrix4x4& aCurrentTransform,
3892 const Matrix4x4& aScrollableContentTransform, AsyncPanZoomController* aApzc,
3893 const FrameMetrics& aMetrics, const ScrollbarData& aScrollbarData,
3894 bool aScrollbarIsDescendant,
3895 AsyncTransformComponentMatrix* aOutClipTransform) {
3896 return apz::ComputeTransformForScrollThumb(
3897 aCurrentTransform, aScrollableContentTransform, aApzc, aMetrics,
3898 aScrollbarData, aScrollbarIsDescendant, aOutClipTransform);
3899 }
3900
GetSampler() const3901 APZSampler* APZCTreeManager::GetSampler() const {
3902 // We should always have a sampler here, since in practice the sampler
3903 // is destroyed at the same time that this APZCTreeMAnager instance is.
3904 MOZ_ASSERT(mSampler);
3905 return mSampler;
3906 }
3907
AssertOnSamplerThread()3908 void APZCTreeManager::AssertOnSamplerThread() {
3909 GetSampler()->AssertOnSamplerThread();
3910 }
3911
GetUpdater() const3912 APZUpdater* APZCTreeManager::GetUpdater() const {
3913 // We should always have an updater here, since in practice the updater
3914 // is destroyed at the same time that this APZCTreeManager instance is.
3915 MOZ_ASSERT(mUpdater);
3916 return mUpdater;
3917 }
3918
AssertOnUpdaterThread()3919 void APZCTreeManager::AssertOnUpdaterThread() {
3920 GetUpdater()->AssertOnUpdaterThread();
3921 }
3922
LockTree()3923 void APZCTreeManager::LockTree() {
3924 AssertOnUpdaterThread();
3925 mTreeLock.Lock();
3926 }
3927
UnlockTree()3928 void APZCTreeManager::UnlockTree() {
3929 AssertOnUpdaterThread();
3930 mTreeLock.Unlock();
3931 }
3932
SetDPI(float aDpiValue)3933 void APZCTreeManager::SetDPI(float aDpiValue) {
3934 APZThreadUtils::AssertOnControllerThread();
3935 mDPI = aDpiValue;
3936 }
3937
GetDPI() const3938 float APZCTreeManager::GetDPI() const {
3939 APZThreadUtils::AssertOnControllerThread();
3940 return mDPI;
3941 }
3942
FixedPositionInfo(const HitTestingTreeNode * aNode)3943 APZCTreeManager::FixedPositionInfo::FixedPositionInfo(
3944 const HitTestingTreeNode* aNode) {
3945 mFixedPositionAnimationId = aNode->GetFixedPositionAnimationId();
3946 mFixedPosSides = aNode->GetFixedPosSides();
3947 mFixedPosTarget = aNode->GetFixedPosTarget();
3948 mLayersId = aNode->GetLayersId();
3949 }
3950
StickyPositionInfo(const HitTestingTreeNode * aNode)3951 APZCTreeManager::StickyPositionInfo::StickyPositionInfo(
3952 const HitTestingTreeNode* aNode) {
3953 mStickyPositionAnimationId = aNode->GetStickyPositionAnimationId();
3954 mFixedPosSides = aNode->GetFixedPosSides();
3955 mStickyPosTarget = aNode->GetStickyPosTarget();
3956 mLayersId = aNode->GetLayersId();
3957 mStickyScrollRangeInner = aNode->GetStickyScrollRangeInner();
3958 mStickyScrollRangeOuter = aNode->GetStickyScrollRangeOuter();
3959 }
3960
3961 } // namespace layers
3962 } // namespace mozilla
3963