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