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