tor-browser

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

CanvasManagerChild.cpp (9440B)


      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 "CanvasManagerChild.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/gfx/2D.h"
     13 #include "mozilla/gfx/CanvasShutdownManager.h"
     14 #include "mozilla/gfx/Swizzle.h"
     15 #include "mozilla/ipc/Endpoint.h"
     16 #include "mozilla/layers/ActiveResource.h"
     17 #include "mozilla/layers/CanvasChild.h"
     18 #include "mozilla/layers/CompositorManagerChild.h"
     19 #include "mozilla/webgpu/WebGPUChild.h"
     20 
     21 namespace mozilla::gfx {
     22 
     23 using namespace layers;
     24 
     25 // The IPDL actor holds a strong reference to CanvasManagerChild which we use
     26 // to keep it alive. The owning thread will tell us to close when it is
     27 // shutdown, either via CanvasManagerChild::Shutdown for the main thread, or
     28 // via a shutdown callback from ThreadSafeWorkerRef for worker threads.
     29 MOZ_THREAD_LOCAL(CanvasManagerChild*) CanvasManagerChild::sLocalManager;
     30 
     31 Atomic<uint32_t> CanvasManagerChild::sNextId(1);
     32 
     33 CanvasManagerChild::CanvasManagerChild(dom::ThreadSafeWorkerRef* aWorkerRef,
     34                                       uint32_t aId)
     35    : mWorkerRef(aWorkerRef), mId(aId) {}
     36 
     37 CanvasManagerChild::~CanvasManagerChild() = default;
     38 
     39 void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) {
     40  DestroyInternal();
     41  if (sLocalManager.get() == this) {
     42    sLocalManager.set(nullptr);
     43  }
     44  mWorkerRef = nullptr;
     45 }
     46 
     47 void CanvasManagerChild::DestroyInternal() {
     48  if (mActiveResourceTracker) {
     49    mActiveResourceTracker->AgeAllGenerations();
     50    mActiveResourceTracker.reset();
     51  }
     52 
     53  if (mCanvasChild) {
     54    mCanvasChild->Destroy();
     55    mCanvasChild = nullptr;
     56  }
     57 
     58  if (auto* shutdownManager = CanvasShutdownManager::Get()) {
     59    shutdownManager->OnRemoteCanvasLost();
     60  }
     61 }
     62 
     63 void CanvasManagerChild::Destroy() {
     64  DestroyInternal();
     65 
     66  // The caller has a strong reference. ActorDestroy will clear sLocalManager
     67  // and mWorkerRef.
     68  Close();
     69 }
     70 
     71 /* static */ void CanvasManagerChild::Shutdown() {
     72  if (sLocalManager.init()) {
     73    RefPtr<CanvasManagerChild> manager = sLocalManager.get();
     74    if (manager) {
     75      manager->Destroy();
     76    }
     77  }
     78 }
     79 
     80 /* static */ bool CanvasManagerChild::CreateParent(
     81    ipc::Endpoint<PCanvasManagerParent>&& aEndpoint) {
     82  MOZ_ASSERT(NS_IsMainThread());
     83 
     84  CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
     85  if (!manager || !manager->CanSend()) {
     86    return false;
     87  }
     88 
     89  return manager->SendInitCanvasManager(std::move(aEndpoint));
     90 }
     91 
     92 /* static */ CanvasManagerChild* CanvasManagerChild::Get() {
     93  if (NS_WARN_IF(!sLocalManager.init())) {
     94    return nullptr;
     95  }
     96 
     97  CanvasManagerChild* managerWeak = sLocalManager.get();
     98  if (managerWeak) {
     99    return managerWeak;
    100  }
    101 
    102  auto* shutdownManager = CanvasShutdownManager::Get();
    103  if (NS_WARN_IF(!shutdownManager)) {
    104    return nullptr;
    105  }
    106 
    107  // We are only used on the main thread, or on worker threads.
    108  dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate();
    109  MOZ_ASSERT_IF(!worker, NS_IsMainThread());
    110 
    111  ipc::Endpoint<PCanvasManagerParent> parentEndpoint;
    112  ipc::Endpoint<PCanvasManagerChild> childEndpoint;
    113 
    114  ipc::EndpointProcInfo compositorInfo =
    115      CompositorManagerChild::GetCompositorProcInfo();
    116  if (NS_WARN_IF(compositorInfo == ipc::EndpointProcInfo::Invalid())) {
    117    return nullptr;
    118  }
    119 
    120  nsresult rv = PCanvasManager::CreateEndpoints(
    121      compositorInfo, ipc::EndpointProcInfo::Current(), &parentEndpoint,
    122      &childEndpoint);
    123  if (NS_WARN_IF(NS_FAILED(rv))) {
    124    return nullptr;
    125  }
    126 
    127  auto manager = MakeRefPtr<CanvasManagerChild>(shutdownManager->GetWorkerRef(),
    128                                                sNextId++);
    129  if (NS_WARN_IF(!childEndpoint.Bind(manager))) {
    130    return nullptr;
    131  }
    132 
    133  // We can't talk to the compositor process directly from worker threads, but
    134  // the main thread can via CompositorManagerChild.
    135  if (worker) {
    136    worker->DispatchToMainThread(NS_NewRunnableFunction(
    137        "CanvasManagerChild::CreateParent",
    138        [parentEndpoint = std::move(parentEndpoint)]() {
    139          CreateParent(
    140              std::move(const_cast<ipc::Endpoint<PCanvasManagerParent>&&>(
    141                  parentEndpoint)));
    142        }));
    143  } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint)))) {
    144    return nullptr;
    145  }
    146 
    147  manager->SendInitialize(manager->Id());
    148  shutdownManager->OnRemoteCanvasRestored();
    149  sLocalManager.set(manager);
    150  return manager;
    151 }
    152 
    153 /* static */ CanvasManagerChild* CanvasManagerChild::MaybeGet() {
    154  if (!sLocalManager.initialized()) {
    155    return nullptr;
    156  }
    157 
    158  return sLocalManager.get();
    159 }
    160 
    161 void CanvasManagerChild::EndCanvasTransaction() {
    162  if (!mCanvasChild) {
    163    return;
    164  }
    165 
    166  mCanvasChild->EndTransaction();
    167  if (mCanvasChild->ShouldBeCleanedUp()) {
    168    mCanvasChild->Destroy();
    169    mCanvasChild = nullptr;
    170  }
    171 }
    172 
    173 void CanvasManagerChild::ClearCachedResources() {
    174  if (mCanvasChild) {
    175    mCanvasChild->ClearCachedResources();
    176  }
    177 }
    178 
    179 void CanvasManagerChild::DeactivateCanvas() {
    180  mActive = false;
    181  if (mCanvasChild) {
    182    mCanvasChild->Destroy();
    183    mCanvasChild = nullptr;
    184  }
    185 }
    186 
    187 void CanvasManagerChild::BlockCanvas() { mBlocked = true; }
    188 
    189 RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() {
    190  if (mBlocked) {
    191    return nullptr;
    192  }
    193 
    194  if (!mActive) {
    195    MOZ_ASSERT(!mCanvasChild);
    196    return nullptr;
    197  }
    198 
    199  if (!mCanvasChild) {
    200    mCanvasChild = MakeAndAddRef<layers::CanvasChild>(mWorkerRef);
    201    if (!SendPCanvasConstructor(mCanvasChild)) {
    202      mCanvasChild->Destroy();
    203      mCanvasChild = nullptr;
    204    }
    205  }
    206 
    207  return mCanvasChild;
    208 }
    209 
    210 RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() {
    211  if (PWebGPUChild* actor = LoneManagedOrNullAsserts(ManagedPWebGPUChild())) {
    212    return static_cast<webgpu::WebGPUChild*>(actor);
    213  }
    214 
    215  auto actor = MakeRefPtr<webgpu::WebGPUChild>();
    216  if (!SendPWebGPUConstructor(actor)) {
    217    return nullptr;
    218  }
    219  return actor;
    220 }
    221 
    222 layers::ActiveResourceTracker* CanvasManagerChild::GetActiveResourceTracker() {
    223  if (!mActiveResourceTracker) {
    224    mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(
    225        1000, "CanvasManagerChild"_ns, GetCurrentSerialEventTarget());
    226  }
    227  return mActiveResourceTracker.get();
    228 }
    229 
    230 already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot(
    231    uint32_t aManagerId, ActorId aProtocolId,
    232    const Maybe<RemoteTextureOwnerId>& aOwnerId,
    233    const Maybe<RawId>& aCommandEncoderId, const Maybe<RawId>& aCommandBufferId,
    234    SurfaceFormat aFormat, bool aPremultiply, bool aYFlip) {
    235  if (!CanSend()) {
    236    return nullptr;
    237  }
    238 
    239  webgl::FrontBufferSnapshotIpc res;
    240  if (!SendGetSnapshot(aManagerId, aProtocolId, aOwnerId, aCommandEncoderId,
    241                       aCommandBufferId, &res)) {
    242    return nullptr;
    243  }
    244 
    245  if (!res.shmem || !res.shmem->IsReadable()) {
    246    return nullptr;
    247  }
    248 
    249  auto guard = MakeScopeExit([&] { DeallocShmem(res.shmem.ref()); });
    250 
    251  if (!res.surfSize.x || !res.surfSize.y || res.surfSize.x > INT32_MAX ||
    252      res.surfSize.y > INT32_MAX) {
    253    return nullptr;
    254  }
    255 
    256  IntSize size(res.surfSize.x, res.surfSize.y);
    257  CheckedInt32 stride = CheckedInt32(res.byteStride);
    258  if (!stride.isValid()) {
    259    return nullptr;
    260  }
    261 
    262  CheckedInt32 length = stride * size.height;
    263  if (!length.isValid() ||
    264      size_t(length.value()) != res.shmem->Size<uint8_t>()) {
    265    return nullptr;
    266  }
    267 
    268  SurfaceFormat format =
    269      IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
    270  RefPtr<DataSourceSurface> surface =
    271      Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(),
    272                                                 /* aZero */ false);
    273  if (!surface) {
    274    return nullptr;
    275  }
    276 
    277  gfx::DataSourceSurface::ScopedMap map(surface,
    278                                        gfx::DataSourceSurface::READ_WRITE);
    279  if (!map.IsMapped()) {
    280    return nullptr;
    281  }
    282 
    283  // The buffer we may readback from the canvas could be R8G8B8A8, not
    284  // premultiplied, and/or has its rows iverted. For the general case, we want
    285  // surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
    286  // to bottom. Given this path is used for screenshots/SurfaceFromElement,
    287  // that's the representation we need.
    288  if (aYFlip) {
    289    if (aPremultiply) {
    290      if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
    291                                aFormat, map.GetData(), map.GetStride(), format,
    292                                size)) {
    293        return nullptr;
    294      }
    295    } else {
    296      if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
    297                            map.GetData(), map.GetStride(), format, size)) {
    298        return nullptr;
    299      }
    300    }
    301  } else if (aPremultiply) {
    302    if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
    303                         map.GetData(), map.GetStride(), format, size)) {
    304      return nullptr;
    305    }
    306  } else {
    307    if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
    308                     map.GetData(), map.GetStride(), format, size)) {
    309      return nullptr;
    310    }
    311  }
    312  return surface.forget();
    313 }
    314 
    315 }  // namespace mozilla::gfx