tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

APZCTreeManager.cpp (165054B)


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