tor-browser

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

APZUpdater.cpp (24155B)


      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 "mozilla/layers/APZUpdater.h"
      8 
      9 #include "APZCTreeManager.h"
     10 #include "AsyncPanZoomController.h"
     11 #include "base/task.h"
     12 #include "mozilla/ClearOnShutdown.h"
     13 #include "mozilla/layers/APZThreadUtils.h"
     14 #include "mozilla/layers/CompositorThread.h"
     15 #include "mozilla/layers/SynchronousTask.h"
     16 #include "mozilla/layers/WebRenderScrollDataWrapper.h"
     17 #include "mozilla/webrender/WebRenderAPI.h"
     18 
     19 namespace mozilla {
     20 namespace layers {
     21 
     22 StaticMutex APZUpdater::sWindowIdLock;
     23 StaticAutoPtr<std::unordered_map<uint64_t, APZUpdater*>>
     24    APZUpdater::sWindowIdMap;
     25 
     26 APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz,
     27                       bool aConnectedToWebRender)
     28    : mApz(aApz),
     29      mDestroyed(false),
     30      mConnectedToWebRender(aConnectedToWebRender),
     31      mThreadIdLock("APZUpdater::ThreadIdLock"),
     32      mQueueLock("APZUpdater::QueueLock") {
     33  MOZ_ASSERT(aApz);
     34  mApz->SetUpdater(this);
     35 }
     36 
     37 APZUpdater::~APZUpdater() {
     38  mApz->SetUpdater(nullptr);
     39 
     40  StaticMutexAutoLock lock(sWindowIdLock);
     41  if (mWindowId) {
     42    MOZ_ASSERT(sWindowIdMap);
     43    // Ensure that ClearTree was called and the task got run
     44    MOZ_ASSERT(sWindowIdMap->find(wr::AsUint64(*mWindowId)) ==
     45               sWindowIdMap->end());
     46  }
     47 }
     48 
     49 bool APZUpdater::HasTreeManager(const RefPtr<APZCTreeManager>& aApz) {
     50  return aApz.get() == mApz.get();
     51 }
     52 
     53 void APZUpdater::SetWebRenderWindowId(const wr::WindowId& aWindowId) {
     54  StaticMutexAutoLock lock(sWindowIdLock);
     55  MOZ_ASSERT(!mWindowId);
     56  mWindowId = Some(aWindowId);
     57  if (!sWindowIdMap) {
     58    sWindowIdMap = new std::unordered_map<uint64_t, APZUpdater*>();
     59    NS_DispatchToMainThread(NS_NewRunnableFunction(
     60        "APZUpdater::ClearOnShutdown", [] { ClearOnShutdown(&sWindowIdMap); }));
     61  }
     62  (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this;
     63 }
     64 
     65 /*static*/
     66 void APZUpdater::SetUpdaterThread(const wr::WrWindowId& aWindowId) {
     67  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     68    MutexAutoLock lock(updater->mThreadIdLock);
     69    updater->mUpdaterThreadId = Some(PlatformThread::CurrentId());
     70  }
     71 }
     72 
     73 // Takes a conditional lock!
     74 /*static*/
     75 void APZUpdater::PrepareForSceneSwap(const wr::WrWindowId& aWindowId)
     76    MOZ_NO_THREAD_SAFETY_ANALYSIS {
     77  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     78    updater->mApz->LockTree();
     79  }
     80 }
     81 
     82 // Assumes we took a conditional lock!
     83 /*static*/
     84 void APZUpdater::CompleteSceneSwap(const wr::WrWindowId& aWindowId,
     85                                   const wr::WrPipelineInfo& aInfo) {
     86  RefPtr<APZUpdater> updater = GetUpdater(aWindowId);
     87  if (!updater) {
     88    // This should only happen in cases where PrepareForSceneSwap also got a
     89    // null updater. No updater-thread tasks get run between PrepareForSceneSwap
     90    // and this function, so there is no opportunity for the updater mapping
     91    // to have gotten removed from sWindowIdMap in between the two calls.
     92    return;
     93  }
     94  updater->mApz->mTreeLock.AssertCurrentThreadIn();
     95 
     96  for (const auto& removedPipeline : aInfo.removed_pipelines) {
     97    LayersId layersId = wr::AsLayersId(removedPipeline.pipeline_id);
     98    updater->mEpochData.erase(layersId);
     99  }
    100  // Reset the built info for all pipelines, then put it back for the ones
    101  // that got built in this scene swap.
    102  for (auto& i : updater->mEpochData) {
    103    i.second.mBuilt = Nothing();
    104  }
    105  for (const auto& epoch : aInfo.epochs) {
    106    LayersId layersId = wr::AsLayersId(epoch.pipeline_id);
    107    updater->mEpochData[layersId].mBuilt = Some(epoch.epoch);
    108  }
    109 
    110  // Run any tasks that got unblocked, then unlock the tree. The order is
    111  // important because we want to run all the tasks up to and including the
    112  // UpdateHitTestingTree calls corresponding to the built epochs, and we
    113  // want to run those before we release the lock (i.e. atomically with the
    114  // scene swap). This ensures that any hit-tests always encounter a consistent
    115  // state between the APZ tree and the built scene in WR.
    116  //
    117  // While we could add additional information to the queued tasks to figure
    118  // out the minimal set of tasks we want to run here, it's easier and harmless
    119  // to just run all the queued and now-unblocked tasks inside the lock.
    120  //
    121  // Note that the ProcessQueue here might remove the window id -> APZUpdater
    122  // mapping from sWindowIdMap, but we still unlock the tree successfully to
    123  // leave things in a good state.
    124  updater->ProcessQueue();
    125 
    126  updater->mApz->UnlockTree();
    127 }
    128 
    129 /*static*/
    130 void APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId) {
    131  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
    132    updater->ProcessQueue();
    133  }
    134 }
    135 
    136 void APZUpdater::ClearTree(LayersId aRootLayersId) {
    137  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    138  RefPtr<APZUpdater> self = this;
    139  RunOnUpdaterThread(
    140      aRootLayersId,
    141      NS_NewRunnableFunction("APZUpdater::ClearTree",
    142                             [=]() {
    143                               self->mApz->ClearTree();
    144                               self->mDestroyed = true;
    145 
    146                               // Once ClearTree is called on the
    147                               // APZCTreeManager, we are in a shutdown phase.
    148                               // After this point it's ok if WebRender cannot
    149                               // get a hold of the updater via the window id,
    150                               // and it's a good point to remove the mapping
    151                               // and avoid leaving a dangling pointer to this
    152                               // object.
    153                               StaticMutexAutoLock lock(sWindowIdLock);
    154                               if (self->mWindowId) {
    155                                 MOZ_ASSERT(sWindowIdMap);
    156                                 sWindowIdMap->erase(
    157                                     wr::AsUint64(*(self->mWindowId)));
    158                               }
    159                             }),
    160      DuringShutdown::Yes);
    161 }
    162 
    163 void APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
    164                                  LayersId aOriginatingLayersId,
    165                                  const FocusTarget& aFocusTarget) {
    166  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    167  RunOnUpdaterThread(aOriginatingLayersId,
    168                     NewRunnableMethod<LayersId, LayersId, FocusTarget>(
    169                         "APZUpdater::UpdateFocusState", mApz,
    170                         &APZCTreeManager::UpdateFocusState, aRootLayerTreeId,
    171                         aOriginatingLayersId, aFocusTarget));
    172 }
    173 
    174 void APZUpdater::UpdateScrollDataAndTreeState(
    175    LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
    176    const wr::Epoch& aEpoch, WebRenderScrollData&& aScrollData) {
    177  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    178  RefPtr<APZUpdater> self = this;
    179  // Insert an epoch requirement update into the queue, so that
    180  // tasks inserted into the queue after this point only get executed
    181  // once the epoch requirement is satisfied. In particular, the
    182  // UpdateHitTestingTree call below needs to wait until the epoch requirement
    183  // is satisfied, which is why it is a separate task in the queue.
    184  RunOnUpdaterThread(
    185      aOriginatingLayersId,
    186      NS_NewRunnableFunction("APZUpdater::UpdateEpochRequirement", [=]() {
    187        if (aRootLayerTreeId == aOriginatingLayersId) {
    188          self->mEpochData[aOriginatingLayersId].mIsRoot = true;
    189        }
    190        self->mEpochData[aOriginatingLayersId].mRequired = aEpoch;
    191      }));
    192  RunOnUpdaterThread(
    193      aOriginatingLayersId,
    194      NS_NewRunnableFunction(
    195          "APZUpdater::UpdateHitTestingTree",
    196          [=, aScrollData = std::move(aScrollData)]() mutable {
    197            auto isFirstPaint = aScrollData.IsFirstPaint();
    198            auto paintSequenceNumber = aScrollData.GetPaintSequenceNumber();
    199 
    200            auto previous = self->mScrollData.find(aOriginatingLayersId);
    201            // If there's the previous scroll data which hasn't yet been
    202            // processed, we need to merge the previous scroll position updates
    203            // into the latest one.
    204            if (previous != self->mScrollData.end()) {
    205              WebRenderScrollData& previousData = previous->second;
    206              if (previousData.GetWasUpdateSkipped()) {
    207                MOZ_ASSERT(previousData.IsFirstPaint());
    208                aScrollData.PrependUpdates(previousData);
    209              }
    210            }
    211 
    212            self->mScrollData[aOriginatingLayersId] = std::move(aScrollData);
    213            auto root = self->mScrollData.find(aRootLayerTreeId);
    214            if (root == self->mScrollData.end()) {
    215              return;
    216            }
    217 
    218            auto updatedIds = self->mApz->UpdateHitTestingTree(
    219                WebRenderScrollDataWrapper(*self, &(root->second)),
    220                aOriginatingLayersId, paintSequenceNumber);
    221            bool originatingLayersIdWasSkipped = true;
    222            for (auto id : updatedIds) {
    223              if (id == aOriginatingLayersId) {
    224                originatingLayersIdWasSkipped = false;
    225              }
    226 
    227              // Reset `mWasUpdateSkipped` flag forcibly here even if the
    228              // `aOriginatingLayersId` is different from the layersId for the
    229              // data, otherwise the skipped scroll position updates will be
    230              // re-processed again.
    231              self->mScrollData[id].SetWasUpdateSkipped(false);
    232            }
    233 
    234            if (isFirstPaint) {
    235              if (originatingLayersIdWasSkipped) {
    236                // If the given |aOriginatingLayersId| data wasn't used for
    237                // updating, it's likely that the parent process hasn't yet
    238                // received the LayersId as "ReferentId", thus we need to
    239                // process it in a subsequent update where we got the
    240                // "ReferentId".
    241                //
    242                // NOTE: We restrict the above previous scroll data prepending
    243                // to the first paint case, otherwise the cumulative scroll data
    244                // may be exploded if we have never received the "ReferenceId".
    245                self->mScrollData[aOriginatingLayersId].SetWasUpdateSkipped(
    246                    true);
    247              } else {
    248                // Clobber the first-paint flag so that we will never run into
    249                // the first-paint branch in
    250                // AsyncPanZoomController::NotifyLayersUpdated even if the next
    251                // transaction is a paint-skip transaction.
    252                self->mScrollData[aOriginatingLayersId].SetIsFirstPaint(false);
    253              }
    254            }
    255          }));
    256 }
    257 
    258 void APZUpdater::UpdateScrollOffsets(LayersId aRootLayerTreeId,
    259                                     LayersId aOriginatingLayersId,
    260                                     ScrollUpdatesMap&& aUpdates,
    261                                     uint32_t aPaintSequenceNumber) {
    262  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    263  RefPtr<APZUpdater> self = this;
    264  RunOnUpdaterThread(
    265      aOriginatingLayersId,
    266      NS_NewRunnableFunction(
    267          "APZUpdater::UpdateScrollOffsets",
    268          [=, updates = std::move(aUpdates)]() mutable {
    269            self->mScrollData[aOriginatingLayersId].ApplyUpdates(
    270                std::move(updates), aPaintSequenceNumber);
    271            auto root = self->mScrollData.find(aRootLayerTreeId);
    272            if (root == self->mScrollData.end()) {
    273              return;
    274            }
    275            self->mApz->UpdateHitTestingTree(
    276                WebRenderScrollDataWrapper(*self, &(root->second)),
    277                aOriginatingLayersId, aPaintSequenceNumber);
    278          }));
    279 }
    280 
    281 void APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
    282                                        const RefPtr<APZUpdater>& aOldUpdater) {
    283  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    284  RunOnUpdaterThread(aLayersId,
    285                     NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
    286                         "APZUpdater::NotifyLayerTreeAdopted", mApz,
    287                         &APZCTreeManager::NotifyLayerTreeAdopted, aLayersId,
    288                         aOldUpdater ? aOldUpdater->mApz : nullptr));
    289 }
    290 
    291 void APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId) {
    292  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    293  RefPtr<APZUpdater> self = this;
    294  RunOnUpdaterThread(
    295      aLayersId,
    296      NS_NewRunnableFunction("APZUpdater::NotifyLayerTreeRemoved", [=]() {
    297        self->mEpochData.erase(aLayersId);
    298        self->mScrollData.erase(aLayersId);
    299        self->mApz->NotifyLayerTreeRemoved(aLayersId);
    300      }));
    301 }
    302 
    303 bool APZUpdater::GetAPZTestData(LayersId aLayersId, APZTestData* aOutData) {
    304  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    305 
    306  RefPtr<APZCTreeManager> apz = mApz;
    307  bool ret = false;
    308  SynchronousTask waiter("APZUpdater::GetAPZTestData");
    309  RunOnUpdaterThread(
    310      aLayersId, NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() {
    311        AutoCompleteTask notifier(&waiter);
    312        ret = apz->GetAPZTestData(aLayersId, aOutData);
    313      }));
    314 
    315  // Wait until the task posted above has run and populated aOutData and ret
    316  waiter.Wait();
    317 
    318  return ret;
    319 }
    320 
    321 void APZUpdater::SetTestAsyncScrollOffset(
    322    LayersId aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
    323    const CSSPoint& aOffset) {
    324  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    325  RefPtr<APZCTreeManager> apz = mApz;
    326  RunOnUpdaterThread(
    327      aLayersId,
    328      NS_NewRunnableFunction("APZUpdater::SetTestAsyncScrollOffset", [=]() {
    329        RefPtr<AsyncPanZoomController> apzc =
    330            apz->GetTargetAPZC(aLayersId, aScrollId);
    331        if (apzc) {
    332          apzc->SetTestAsyncScrollOffset(aOffset);
    333        } else {
    334          NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
    335        }
    336      }));
    337 }
    338 
    339 void APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
    340                                  const ScrollableLayerGuid::ViewID& aScrollId,
    341                                  const LayerToParentLayerScale& aZoom) {
    342  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    343  RefPtr<APZCTreeManager> apz = mApz;
    344  RunOnUpdaterThread(
    345      aLayersId, NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() {
    346        RefPtr<AsyncPanZoomController> apzc =
    347            apz->GetTargetAPZC(aLayersId, aScrollId);
    348        if (apzc) {
    349          apzc->SetTestAsyncZoom(aZoom);
    350        } else {
    351          NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
    352        }
    353      }));
    354 }
    355 
    356 const WebRenderScrollData* APZUpdater::GetScrollData(LayersId aLayersId) const {
    357  AssertOnUpdaterThread();
    358  auto it = mScrollData.find(aLayersId);
    359  return (it == mScrollData.end() ? nullptr : &(it->second));
    360 }
    361 
    362 void APZUpdater::AssertOnUpdaterThread() const {
    363  if (APZThreadUtils::GetThreadAssertionsEnabled()) {
    364    MOZ_ASSERT(IsUpdaterThread());
    365  }
    366 }
    367 
    368 void APZUpdater::RunOnUpdaterThread(LayersId aLayersId,
    369                                    already_AddRefed<Runnable> aTask,
    370                                    DuringShutdown aDuringShutdown) {
    371  RefPtr<Runnable> task = aTask;
    372 
    373  // In the scenario where IsConnectedToWebRender() is true, this function
    374  // might get called early (before mUpdaterThreadId is set). In that case
    375  // IsUpdaterThread() will return false and we'll queue the task onto
    376  // mUpdaterQueue. This is fine; the task is still guaranteed to run (barring
    377  // catastrophic failure) because the WakeSceneBuilder call will still trigger
    378  // the callback to run tasks.
    379 
    380  if (IsUpdaterThread()) {
    381    // This function should only be called from the updater thread in test
    382    // scenarios where we are not connected to WebRender. If it were called from
    383    // the updater thread when we are connected to WebRender, running the task
    384    // right away would be incorrect (we'd need to check that |aLayersId|
    385    // isn't blocked, and if it is then enqueue the task instead).
    386    MOZ_ASSERT(!IsConnectedToWebRender());
    387    task->Run();
    388    return;
    389  }
    390 
    391  if (IsConnectedToWebRender()) {
    392    // If the updater thread is a WebRender thread, and we're not on it
    393    // right now, save the task in the queue. We will run tasks from the queue
    394    // during the callback from the updater thread, which we trigger by the
    395    // call to WakeSceneBuilder.
    396 
    397    bool sendWakeMessage = aDuringShutdown == DuringShutdown::No;
    398    {  // scope lock
    399      MutexAutoLock lock(mQueueLock);
    400      if (sendWakeMessage) {
    401        for (const auto& queuedTask : mUpdaterQueue) {
    402          if (queuedTask.mLayersId == aLayersId) {
    403            // If there's already a task in the queue with this layers id, then
    404            // we must have previously sent a WakeSceneBuilder message (when
    405            // adding the first task with this layers id to the queue). Either
    406            // that hasn't been fully processed yet, or the layers id is blocked
    407            // waiting for an epoch - in either case there's no point in sending
    408            // another WakeSceneBuilder message.
    409            sendWakeMessage = false;
    410            break;
    411          }
    412        }
    413      }
    414      mUpdaterQueue.push_back(QueuedTask{aLayersId, task});
    415    }
    416    if (sendWakeMessage) {
    417      RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
    418      if (api) {
    419        api->WakeSceneBuilder();
    420      } else {
    421        // Not sure if this can happen, but it might be possible. If it does,
    422        // the task is in the queue, but if we didn't get a WebRenderAPI it
    423        // might never run, or it might run later if we manage to get a
    424        // WebRenderAPI later. For now let's just emit a warning, this can
    425        // probably be upgraded to an assert later.
    426        NS_WARNING("Possibly dropping task posted to updater thread");
    427      }
    428    }
    429    return;
    430  }
    431 
    432  if (CompositorThread()) {
    433    CompositorThread()->Dispatch(task.forget());
    434  } else {
    435    // Could happen during startup
    436    NS_WARNING("Dropping task posted to updater thread");
    437  }
    438 }
    439 
    440 bool APZUpdater::IsUpdaterThread() const {
    441  if (IsConnectedToWebRender()) {
    442    // If the updater thread id isn't set yet then we cannot be running on the
    443    // updater thread (because we will have the thread id before we run any
    444    // C++ code on it, and this function is only ever invoked from C++ code),
    445    // so return false in that scenario.
    446    MutexAutoLock lock(mThreadIdLock);
    447    return mUpdaterThreadId && PlatformThread::CurrentId() == *mUpdaterThreadId;
    448  }
    449  return CompositorThreadHolder::IsInCompositorThread();
    450 }
    451 
    452 void APZUpdater::RunOnControllerThread(LayersId aLayersId,
    453                                       already_AddRefed<Runnable> aTask) {
    454  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
    455 
    456  RefPtr<Runnable> task = aTask;
    457 
    458  RunOnUpdaterThread(
    459      aLayersId,
    460      NewRunnableFunction("APZUpdater::RunOnControllerThread",
    461                          &APZThreadUtils::RunOnControllerThread,
    462                          std::move(task), nsIThread::DISPATCH_NORMAL));
    463 }
    464 
    465 bool APZUpdater::IsConnectedToWebRender() const {
    466  return mConnectedToWebRender;
    467 }
    468 
    469 /*static*/
    470 already_AddRefed<APZUpdater> APZUpdater::GetUpdater(
    471    const wr::WrWindowId& aWindowId) {
    472  RefPtr<APZUpdater> updater;
    473  StaticMutexAutoLock lock(sWindowIdLock);
    474  if (sWindowIdMap) {
    475    auto it = sWindowIdMap->find(wr::AsUint64(aWindowId));
    476    if (it != sWindowIdMap->end()) {
    477      updater = it->second;
    478    }
    479  }
    480  return updater.forget();
    481 }
    482 
    483 void APZUpdater::ProcessQueue() {
    484  MOZ_ASSERT(!mDestroyed);
    485 
    486  {  // scope lock to check for emptiness
    487    MutexAutoLock lock(mQueueLock);
    488    if (mUpdaterQueue.empty()) {
    489      return;
    490    }
    491  }
    492 
    493  std::deque<QueuedTask> blockedTasks;
    494  while (true) {
    495    QueuedTask task;
    496 
    497    {  // scope lock to extract a task
    498      MutexAutoLock lock(mQueueLock);
    499      if (mUpdaterQueue.empty()) {
    500        // If we're done processing mUpdaterQueue, swap the tasks that are
    501        // still blocked back in and finish
    502        std::swap(mUpdaterQueue, blockedTasks);
    503        break;
    504      }
    505      task = mUpdaterQueue.front();
    506      mUpdaterQueue.pop_front();
    507    }
    508 
    509    // We check the task to see if it is blocked. Note that while this
    510    // ProcessQueue function is executing, a particular layers id cannot go
    511    // from blocked to unblocked, because only CompleteSceneSwap can unblock
    512    // a layers id, and that also runs on the updater thread. If somehow
    513    // a layers id gets unblocked while we're processing the queue, then it
    514    // might result in tasks getting executed out of order.
    515 
    516    auto it = mEpochData.find(task.mLayersId);
    517    if (it != mEpochData.end() && it->second.IsBlocked()) {
    518      // If this task is blocked, put it into the blockedTasks queue that
    519      // we will replace mUpdaterQueue with
    520      blockedTasks.push_back(task);
    521    } else {
    522      // Run and discard the task
    523      task.mRunnable->Run();
    524    }
    525  }
    526 
    527  if (mDestroyed) {
    528    // If we get here, then we must have just run the ClearTree task for
    529    // this updater. There might be tasks in the queue from content subtrees
    530    // of this window that are blocked due to stale epochs. This can happen
    531    // if the tasks were queued after the root pipeline was removed in
    532    // WebRender, which prevents scene builds (and therefore prevents us
    533    // from getting updated epochs via CompleteSceneSwap). See bug 1465658
    534    // comment 43 for some more context.
    535    // To avoid leaking these tasks, we discard the contents of the queue.
    536    // This happens during window shutdown so if we don't run the tasks it's
    537    // not going to matter much.
    538    MutexAutoLock lock(mQueueLock);
    539    if (!mUpdaterQueue.empty()) {
    540      mUpdaterQueue.clear();
    541    }
    542  }
    543 }
    544 
    545 void APZUpdater::MarkAsDetached(LayersId aLayersId) {
    546  mApz->MarkAsDetached(aLayersId);
    547 }
    548 
    549 APZUpdater::EpochState::EpochState() : mRequired{0}, mIsRoot(false) {}
    550 
    551 bool APZUpdater::EpochState::IsBlocked() const {
    552  // The root is a special case because we basically assume it is "visible"
    553  // even before it is built for the first time. This is because building the
    554  // scene automatically makes it visible, and we need to make sure the APZ
    555  // scroll data gets applied atomically with that happening.
    556  //
    557  // Layer subtrees on the other hand do not automatically become visible upon
    558  // being built, because there must be a another layer tree update to change
    559  // the visibility (i.e. an ancestor layer tree update that adds the necessary
    560  // reflayer to complete the chain of reflayers).
    561  //
    562  // So in the case of non-visible subtrees, we know that no hit-test will
    563  // actually end up hitting that subtree either before or after the scene swap,
    564  // because the subtree will remain non-visible. That in turns means that we
    565  // can apply the APZ scroll data for that subtree epoch before the scene is
    566  // built, because it's not going to get used anyway. And that means we don't
    567  // need to block the queue for non-visible subtrees. Which is a good thing,
    568  // because in practice it seems like we often have non-visible subtrees sent
    569  // to the compositor from content.
    570  if (mIsRoot && !mBuilt) {
    571    return true;
    572  }
    573  return mBuilt && (*mBuilt < mRequired);
    574 }
    575 
    576 }  // namespace layers
    577 }  // namespace mozilla
    578 
    579 // Rust callback implementations
    580 
    581 void apz_register_updater(mozilla::wr::WrWindowId aWindowId) {
    582  mozilla::layers::APZUpdater::SetUpdaterThread(aWindowId);
    583 }
    584 
    585 void apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId) {
    586  mozilla::layers::APZUpdater::PrepareForSceneSwap(aWindowId);
    587 }
    588 
    589 void apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId,
    590                         const mozilla::wr::WrPipelineInfo* aInfo) {
    591  mozilla::layers::APZUpdater::CompleteSceneSwap(aWindowId, *aInfo);
    592 }
    593 
    594 void apz_run_updater(mozilla::wr::WrWindowId aWindowId) {
    595  mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
    596 }
    597 
    598 void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId) {
    599  // Run anything that's still left.
    600  mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
    601 }