tor-browser

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

CanvasShutdownManager.cpp (6180B)


      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 "CanvasShutdownManager.h"
      8 #include "mozilla/AppShutdown.h"
      9 #include "mozilla/dom/CanvasRenderingContext2D.h"
     10 #include "mozilla/dom/WorkerPrivate.h"
     11 #include "mozilla/dom/WorkerRef.h"
     12 #include "mozilla/dom/WorkerRunnable.h"
     13 #include "mozilla/gfx/CanvasManagerChild.h"
     14 #include "mozilla/layers/PersistentBufferProvider.h"
     15 
     16 namespace mozilla::gfx {
     17 
     18 using dom::CanvasRenderingContext2D;
     19 using dom::StrongWorkerRef;
     20 using dom::WorkerPrivate;
     21 
     22 StaticMutex CanvasShutdownManager::sManagersMutex;
     23 MOZ_RUNINIT std::set<CanvasShutdownManager*> CanvasShutdownManager::sManagers;
     24 
     25 // The owning thread will tell us to close when it is shutdown, either via
     26 // CanvasShutdownManager::Shutdown for the main thread, or via a shutdown
     27 // callback from ThreadSafeWorkerRef for worker threads.
     28 MOZ_THREAD_LOCAL(CanvasShutdownManager*) CanvasShutdownManager::sLocalManager;
     29 
     30 CanvasShutdownManager::CanvasShutdownManager(StrongWorkerRef* aWorkerRef)
     31    : mWorkerRef(new dom::ThreadSafeWorkerRef(aWorkerRef)) {}
     32 
     33 CanvasShutdownManager::CanvasShutdownManager() = default;
     34 CanvasShutdownManager::~CanvasShutdownManager() = default;
     35 
     36 void CanvasShutdownManager::Destroy() {
     37  std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas);
     38  for (const auto& i : activeCanvas) {
     39    i->OnShutdown();
     40  }
     41 
     42  CanvasManagerChild::Shutdown();
     43  mWorkerRef = nullptr;
     44 }
     45 
     46 /* static */ void CanvasShutdownManager::Shutdown() {
     47  auto* manager = MaybeGet();
     48  if (!manager) {
     49    return;
     50  }
     51 
     52  {
     53    StaticMutexAutoLock lock(sManagersMutex);
     54    sManagers.erase(manager);
     55  }
     56 
     57  sLocalManager.set(nullptr);
     58  manager->Destroy();
     59  delete manager;
     60 }
     61 
     62 /* static */ CanvasShutdownManager* CanvasShutdownManager::MaybeGet() {
     63  if (NS_WARN_IF(!sLocalManager.init())) {
     64    return nullptr;
     65  }
     66 
     67  return sLocalManager.get();
     68 }
     69 
     70 /* static */ CanvasShutdownManager* CanvasShutdownManager::Get() {
     71  if (NS_WARN_IF(!sLocalManager.init())) {
     72    return nullptr;
     73  }
     74 
     75  CanvasShutdownManager* managerWeak = sLocalManager.get();
     76  if (managerWeak) {
     77    return managerWeak;
     78  }
     79 
     80  if (WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate()) {
     81    // The ThreadSafeWorkerRef will let us know when the worker is shutting
     82    // down. This will let us clear our threadlocal reference and close the
     83    // actor. We rely upon an explicit shutdown for the main thread.
     84    RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
     85        worker, "CanvasShutdownManager", []() { Shutdown(); });
     86    if (NS_WARN_IF(!workerRef)) {
     87      return nullptr;
     88    }
     89 
     90    CanvasShutdownManager* manager = new CanvasShutdownManager(workerRef);
     91    sLocalManager.set(manager);
     92 
     93    StaticMutexAutoLock lock(sManagersMutex);
     94    sManagers.insert(manager);
     95    return manager;
     96  }
     97 
     98  if (NS_IsMainThread()) {
     99    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    100      return nullptr;
    101    }
    102 
    103    CanvasShutdownManager* manager = new CanvasShutdownManager();
    104    sLocalManager.set(manager);
    105 
    106    StaticMutexAutoLock lock(sManagersMutex);
    107    sManagers.insert(manager);
    108    return manager;
    109  }
    110 
    111  MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!");
    112  return nullptr;
    113 }
    114 
    115 void CanvasShutdownManager::AddShutdownObserver(
    116    dom::CanvasRenderingContext2D* aCanvas) {
    117  mActiveCanvas.insert(aCanvas);
    118 }
    119 
    120 void CanvasShutdownManager::RemoveShutdownObserver(
    121    dom::CanvasRenderingContext2D* aCanvas) {
    122  mActiveCanvas.erase(aCanvas);
    123 }
    124 
    125 void CanvasShutdownManager::OnRemoteCanvasLost() {
    126  // Note that the canvas cannot do anything that mutates our state. It will
    127  // dispatch for anything that risks re-entrancy.
    128  for (const auto& canvas : mActiveCanvas) {
    129    canvas->OnRemoteCanvasLost();
    130  }
    131 }
    132 
    133 void CanvasShutdownManager::OnRemoteCanvasRestored() {
    134  // Note that the canvas cannot do anything that mutates our state. It will
    135  // dispatch for anything that risks re-entrancy.
    136  for (const auto& canvas : mActiveCanvas) {
    137    canvas->OnRemoteCanvasRestored();
    138  }
    139 }
    140 
    141 void CanvasShutdownManager::OnRemoteCanvasReset(
    142    const nsTArray<layers::RemoteTextureOwnerId>& aOwnerIds) {
    143  if (aOwnerIds.IsEmpty()) {
    144    return;
    145  }
    146 
    147  for (const auto& canvas : mActiveCanvas) {
    148    auto* bufferProvider = canvas->GetBufferProvider();
    149    if (!bufferProvider) {
    150      continue;
    151    }
    152 
    153    Maybe<layers::RemoteTextureOwnerId> ownerId =
    154        bufferProvider->GetRemoteTextureOwnerId();
    155    if (!ownerId) {
    156      continue;
    157    }
    158 
    159    if (aOwnerIds.Contains(*ownerId)) {
    160      canvas->OnRemoteCanvasLost();
    161      canvas->OnRemoteCanvasRestored();
    162    }
    163  }
    164 }
    165 
    166 /* static */ void CanvasShutdownManager::MaybeRestoreRemoteCanvas() {
    167  // Calling Get will recreate the CanvasManagerChild, which in turn will
    168  // cause us to call OnRemoteCanvasRestore upon success.
    169  if (CanvasShutdownManager* manager = MaybeGet()) {
    170    if (!manager->mActiveCanvas.empty()) {
    171      CanvasManagerChild::Get();
    172    }
    173  }
    174 }
    175 
    176 /* static */ void CanvasShutdownManager::OnCompositorManagerRestored() {
    177  MOZ_ASSERT(NS_IsMainThread());
    178 
    179  class RestoreRunnable final : public dom::MainThreadWorkerRunnable {
    180   public:
    181    RestoreRunnable()
    182        : MainThreadWorkerRunnable("CanvasShutdownManager::RestoreRunnable") {}
    183 
    184    bool WorkerRun(JSContext*, WorkerPrivate*) override {
    185      MaybeRestoreRemoteCanvas();
    186      return true;
    187    }
    188  };
    189 
    190  // We can restore the main thread canvases immediately.
    191  MaybeRestoreRemoteCanvas();
    192 
    193  // And dispatch to restore any DOM worker canvases. This is safe because we
    194  // remove the manager from sManagers before clearing mWorkerRef during DOM
    195  // worker shutdown.
    196  StaticMutexAutoLock lock(sManagersMutex);
    197  for (const auto& manager : sManagers) {
    198    if (manager->mWorkerRef) {
    199      auto task = MakeRefPtr<RestoreRunnable>();
    200      task->Dispatch(manager->mWorkerRef->Private());
    201    }
    202  }
    203 }
    204 
    205 }  // namespace mozilla::gfx