tor-browser

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

OffscreenCanvasDisplayHelper.cpp (20427B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 "OffscreenCanvasDisplayHelper.h"
      8 
      9 #include "mozilla/SVGObserverUtils.h"
     10 #include "mozilla/StaticPrefs_gfx.h"
     11 #include "mozilla/dom/Document.h"
     12 #include "mozilla/dom/WorkerRef.h"
     13 #include "mozilla/dom/WorkerRunnable.h"
     14 #include "mozilla/gfx/2D.h"
     15 #include "mozilla/gfx/CanvasManagerChild.h"
     16 #include "mozilla/gfx/DataSurfaceHelpers.h"
     17 #include "mozilla/gfx/Swizzle.h"
     18 #include "mozilla/layers/ImageBridgeChild.h"
     19 #include "mozilla/layers/PersistentBufferProvider.h"
     20 #include "mozilla/layers/TextureClientSharedSurface.h"
     21 #include "mozilla/layers/TextureWrapperImage.h"
     22 #include "nsICanvasRenderingContextInternal.h"
     23 #include "nsRFPService.h"
     24 
     25 namespace mozilla::dom {
     26 
     27 OffscreenCanvasDisplayHelper::OffscreenCanvasDisplayHelper(
     28    HTMLCanvasElement* aCanvasElement, uint32_t aWidth, uint32_t aHeight)
     29    : mMutex("mozilla::dom::OffscreenCanvasDisplayHelper"),
     30      mCanvasElement(aCanvasElement),
     31      mImageProducerID(layers::ImageContainer::AllocateProducerID()) {
     32  mData.mSize.width = aWidth;
     33  mData.mSize.height = aHeight;
     34 }
     35 
     36 OffscreenCanvasDisplayHelper::~OffscreenCanvasDisplayHelper() {
     37  MutexAutoLock lock(mMutex);
     38  NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
     39                         mExpandedReader.forget());
     40 }
     41 
     42 void OffscreenCanvasDisplayHelper::DestroyElement() {
     43  MOZ_ASSERT(NS_IsMainThread());
     44 
     45  MutexAutoLock lock(mMutex);
     46  if (mImageContainer) {
     47    mImageContainer->ClearImagesInHost(layers::ClearImagesType::All);
     48    mImageContainer = nullptr;
     49  }
     50  mFrontBufferSurface = nullptr;
     51  mCanvasElement = nullptr;
     52 }
     53 
     54 void OffscreenCanvasDisplayHelper::DestroyCanvas() {
     55  if (auto* cm = gfx::CanvasManagerChild::Get()) {
     56    cm->EndCanvasTransaction();
     57  }
     58 
     59  MutexAutoLock lock(mMutex);
     60  if (mImageContainer) {
     61    mImageContainer->ClearImagesInHost(layers::ClearImagesType::All);
     62    mImageContainer = nullptr;
     63  }
     64  mFrontBufferSurface = nullptr;
     65  mOffscreenCanvas = nullptr;
     66  mWorkerRef = nullptr;
     67 }
     68 
     69 void OffscreenCanvasDisplayHelper::SetWriteOnly(nsIPrincipal* aExpandedReader) {
     70  MutexAutoLock lock(mMutex);
     71  NS_ReleaseOnMainThread("OffscreenCanvasDisplayHelper::mExpandedReader",
     72                         mExpandedReader.forget());
     73  mExpandedReader = aExpandedReader;
     74  mIsWriteOnly = true;
     75 }
     76 
     77 bool OffscreenCanvasDisplayHelper::CallerCanRead(
     78    nsIPrincipal& aPrincipal) const {
     79  MutexAutoLock lock(mMutex);
     80  if (!mIsWriteOnly) {
     81    return true;
     82  }
     83 
     84  // If mExpandedReader is set, this canvas was tainted only by
     85  // mExpandedReader's resources. So allow reading if the subject
     86  // principal subsumes mExpandedReader.
     87  if (mExpandedReader && aPrincipal.Subsumes(mExpandedReader)) {
     88    return true;
     89  }
     90 
     91  return nsContentUtils::PrincipalHasPermission(aPrincipal,
     92                                                nsGkAtoms::all_urlsPermission);
     93 }
     94 
     95 bool OffscreenCanvasDisplayHelper::CanElementCaptureStream() const {
     96  MutexAutoLock lock(mMutex);
     97  return !!mWorkerRef;
     98 }
     99 
    100 bool OffscreenCanvasDisplayHelper::UsingElementCaptureStream() const {
    101  MutexAutoLock lock(mMutex);
    102 
    103  if (NS_WARN_IF(!NS_IsMainThread())) {
    104    MOZ_ASSERT_UNREACHABLE("Should not call off main-thread!");
    105    return !!mCanvasElement;
    106  }
    107 
    108  return mCanvasElement && mCanvasElement->UsingCaptureStream();
    109 }
    110 
    111 CanvasContextType OffscreenCanvasDisplayHelper::GetContextType() const {
    112  MutexAutoLock lock(mMutex);
    113  return mType;
    114 }
    115 
    116 RefPtr<layers::ImageContainer> OffscreenCanvasDisplayHelper::GetImageContainer()
    117    const {
    118  MutexAutoLock lock(mMutex);
    119  return mImageContainer;
    120 }
    121 
    122 void OffscreenCanvasDisplayHelper::UpdateContext(
    123    OffscreenCanvas* aOffscreenCanvas, RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
    124    CanvasContextType aType, const Maybe<mozilla::ipc::ActorId>& aChildId) {
    125  MutexAutoLock lock(mMutex);
    126 
    127  // Only create ImageContainer if we don't already have one (Bug 2004797).
    128  // Recreating it would discard any existing frames, causing flicker.
    129  if (!mImageContainer) {
    130    mImageContainer = MakeRefPtr<layers::ImageContainer>(
    131        layers::ImageUsageType::OffscreenCanvas,
    132        layers::ImageContainer::ASYNCHRONOUS);
    133  }
    134 
    135  mOffscreenCanvas = aOffscreenCanvas;
    136  mWorkerRef = std::move(aWorkerRef);
    137  mType = aType;
    138  mContextChildId = aChildId;
    139 
    140  if (aChildId) {
    141    mContextManagerId = Some(gfx::CanvasManagerChild::Get()->Id());
    142  } else {
    143    mContextManagerId.reset();
    144  }
    145 
    146  MaybeQueueInvalidateElement();
    147 }
    148 
    149 void OffscreenCanvasDisplayHelper::FlushForDisplay() {
    150  MOZ_ASSERT(NS_IsMainThread());
    151 
    152  MutexAutoLock lock(mMutex);
    153 
    154  // Without an OffscreenCanvas object bound to us, we either have not drawn
    155  // using the canvas at all, or we have already destroyed it.
    156  if (!mOffscreenCanvas) {
    157    return;
    158  }
    159 
    160  // We assign/destroy the worker ref at the same time as the OffscreenCanvas
    161  // ref, so we can only have the canvas without a worker ref if it exists on
    162  // the main thread.
    163  if (!mWorkerRef) {
    164    // We queue to ensure that we have the same asynchronous update behaviour
    165    // for a main thread and a worker based OffscreenCanvas.
    166    mOffscreenCanvas->QueueCommitToCompositor();
    167    return;
    168  }
    169 
    170  class FlushWorkerRunnable final : public MainThreadWorkerRunnable {
    171   public:
    172    explicit FlushWorkerRunnable(OffscreenCanvasDisplayHelper* aDisplayHelper)
    173        : MainThreadWorkerRunnable("FlushWorkerRunnable"),
    174          mDisplayHelper(aDisplayHelper) {}
    175 
    176    bool WorkerRun(JSContext*, WorkerPrivate*) override {
    177      // The OffscreenCanvas can only be freed on the worker thread, so we
    178      // cannot be racing with an OffscreenCanvas::DestroyCanvas call and its
    179      // destructor. We just need to make sure we don't call into
    180      // OffscreenCanvas while holding the lock since it calls back into the
    181      // OffscreenCanvasDisplayHelper.
    182      RefPtr<OffscreenCanvas> canvas;
    183      {
    184        MutexAutoLock lock(mDisplayHelper->mMutex);
    185        canvas = mDisplayHelper->mOffscreenCanvas;
    186      }
    187 
    188      if (canvas) {
    189        canvas->CommitFrameToCompositor();
    190      }
    191      return true;
    192    }
    193 
    194   private:
    195    RefPtr<OffscreenCanvasDisplayHelper> mDisplayHelper;
    196  };
    197 
    198  // Otherwise we are calling from the main thread during painting to a canvas
    199  // on a worker thread.
    200  auto task = MakeRefPtr<FlushWorkerRunnable>(this);
    201  task->Dispatch(mWorkerRef->Private());
    202 }
    203 
    204 bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
    205    nsICanvasRenderingContextInternal* aContext,
    206    const Maybe<OffscreenCanvasDisplayData>& aData) {
    207  auto endTransaction = MakeScopeExit([&]() {
    208    if (auto* cm = gfx::CanvasManagerChild::Get()) {
    209      cm->EndCanvasTransaction();
    210    }
    211  });
    212 
    213  MutexAutoLock lock(mMutex);
    214 
    215  gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
    216  layers::TextureFlags flags = layers::TextureFlags::IMMUTABLE;
    217 
    218  if (!mCanvasElement) {
    219    // Our weak reference to the canvas element has been cleared, so we cannot
    220    // present directly anymore.
    221    return false;
    222  }
    223 
    224  if (aData) {
    225    mData = aData.ref();
    226    MaybeQueueInvalidateElement();
    227  }
    228 
    229  if (!mImageContainer) {
    230    return false;
    231  }
    232 
    233  if (mData.mIsOpaque) {
    234    flags |= layers::TextureFlags::IS_OPAQUE;
    235    format = gfx::SurfaceFormat::B8G8R8X8;
    236  } else if (!mData.mIsAlphaPremult) {
    237    flags |= layers::TextureFlags::NON_PREMULTIPLIED;
    238  }
    239 
    240  switch (mData.mOriginPos) {
    241    case gl::OriginPos::BottomLeft:
    242      flags |= layers::TextureFlags::ORIGIN_BOTTOM_LEFT;
    243      break;
    244    case gl::OriginPos::TopLeft:
    245      break;
    246    default:
    247      MOZ_ASSERT_UNREACHABLE("Unhandled origin position!");
    248      break;
    249  }
    250 
    251  auto imageBridge = layers::ImageBridgeChild::GetSingleton();
    252  if (!imageBridge) {
    253    return false;
    254  }
    255 
    256  bool paintCallbacks = mData.mDoPaintCallbacks;
    257  bool hasRemoteTextureDesc = false;
    258  RefPtr<layers::Image> image;
    259  RefPtr<layers::TextureClient> texture;
    260  RefPtr<gfx::SourceSurface> surface;
    261  Maybe<layers::SurfaceDescriptor> desc;
    262  RefPtr<layers::FwdTransactionTracker> tracker;
    263 
    264  {
    265    MutexAutoUnlock unlock(mMutex);
    266    if (paintCallbacks) {
    267      aContext->OnBeforePaintTransaction();
    268    }
    269 
    270    desc = aContext->PresentFrontBuffer(nullptr);
    271    if (desc) {
    272      hasRemoteTextureDesc =
    273          desc->type() ==
    274          layers::SurfaceDescriptor::TSurfaceDescriptorRemoteTexture;
    275      if (hasRemoteTextureDesc) {
    276        tracker = aContext->UseCompositableForwarder(imageBridge);
    277        if (tracker) {
    278          flags |= layers::TextureFlags::WAIT_FOR_REMOTE_TEXTURE_OWNER;
    279        }
    280      }
    281    } else {
    282      if (layers::PersistentBufferProvider* provider =
    283              aContext->GetBufferProvider()) {
    284        texture = provider->GetTextureClient();
    285      }
    286 
    287      if (!texture) {
    288        surface =
    289            aContext->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
    290        if (surface && surface->GetType() == gfx::SurfaceType::WEBGL) {
    291          // Ensure we can map in the surface. If we get a SourceSurfaceWebgl
    292          // surface, then it may not be backed by raw pixels yet. We need to
    293          // map it on the owning thread rather than the ImageBridge thread.
    294          gfx::DataSourceSurface::ScopedMap map(
    295              static_cast<gfx::DataSourceSurface*>(surface.get()),
    296              gfx::DataSourceSurface::READ);
    297          if (!map.IsMapped()) {
    298            surface = nullptr;
    299          }
    300        }
    301      }
    302    }
    303 
    304    if (paintCallbacks) {
    305      aContext->OnDidPaintTransaction();
    306    }
    307  }
    308 
    309  // We save any current surface because we might need it in GetSnapshot. If we
    310  // are on a worker thread and not WebGL, then this will be the only way we can
    311  // access the pixel data on the main thread.
    312  mFrontBufferSurface = surface;
    313 
    314  // We do not use the ImageContainer plumbing with remote textures, so if we
    315  // have that, we can just early return here.
    316  if (hasRemoteTextureDesc) {
    317    const auto& textureDesc = desc->get_SurfaceDescriptorRemoteTexture();
    318    imageBridge->UpdateCompositable(mImageContainer, textureDesc.textureId(),
    319                                    textureDesc.ownerId(), mData.mSize, flags,
    320                                    tracker);
    321    return true;
    322  }
    323 
    324  if (surface) {
    325    auto surfaceImage = MakeRefPtr<layers::SourceSurfaceImage>(surface);
    326    surfaceImage->SetTextureFlags(flags);
    327    image = surfaceImage;
    328  } else {
    329    if (desc && !texture) {
    330      texture = layers::SharedSurfaceTextureData::CreateTextureClient(
    331          *desc, format, mData.mSize, flags, imageBridge);
    332    }
    333    if (texture) {
    334      image = new layers::TextureWrapperImage(
    335          texture, gfx::IntRect(gfx::IntPoint(0, 0), texture->GetSize()));
    336    }
    337  }
    338 
    339  if (image) {
    340    AutoTArray<layers::ImageContainer::NonOwningImage, 1> imageList;
    341    imageList.AppendElement(layers::ImageContainer::NonOwningImage(
    342        image, TimeStamp(), mLastFrameID++, mImageProducerID));
    343    mImageContainer->SetCurrentImages(imageList);
    344  } else {
    345    mImageContainer->ClearImagesInHost(layers::ClearImagesType::All);
    346  }
    347 
    348  return true;
    349 }
    350 
    351 void OffscreenCanvasDisplayHelper::MaybeQueueInvalidateElement() {
    352  if (!mPendingInvalidate) {
    353    mPendingInvalidate = true;
    354    NS_DispatchToMainThread(NS_NewRunnableFunction(
    355        "OffscreenCanvasDisplayHelper::InvalidateElement",
    356        [self = RefPtr{this}] { self->InvalidateElement(); }));
    357  }
    358 }
    359 
    360 void OffscreenCanvasDisplayHelper::InvalidateElement() {
    361  MOZ_ASSERT(NS_IsMainThread());
    362 
    363  HTMLCanvasElement* canvasElement;
    364  gfx::IntSize size;
    365 
    366  {
    367    MutexAutoLock lock(mMutex);
    368    MOZ_ASSERT(mPendingInvalidate);
    369    mPendingInvalidate = false;
    370    canvasElement = mCanvasElement;
    371    size = mData.mSize;
    372  }
    373 
    374  if (canvasElement) {
    375    SVGObserverUtils::InvalidateDirectRenderingObservers(canvasElement);
    376    canvasElement->InvalidateCanvasPlaceholder(size.width, size.height);
    377    canvasElement->InvalidateCanvasContent(nullptr);
    378  }
    379 }
    380 
    381 already_AddRefed<gfx::SourceSurface>
    382 OffscreenCanvasDisplayHelper::TransformSurface(gfx::SourceSurface* aSurface,
    383                                               bool aHasAlpha,
    384                                               bool aIsAlphaPremult,
    385                                               gl::OriginPos aOriginPos) const {
    386  if (!aSurface) {
    387    return nullptr;
    388  }
    389 
    390  if (aOriginPos == gl::OriginPos::TopLeft && (!aHasAlpha || aIsAlphaPremult)) {
    391    // If we don't need to y-flip, and it is either opaque or premultiplied,
    392    // we can just return the same surface.
    393    return do_AddRef(aSurface);
    394  }
    395 
    396  // Otherwise we need to copy and apply the necessary transformations.
    397  RefPtr<gfx::DataSourceSurface> srcSurface = aSurface->GetDataSurface();
    398  if (!srcSurface) {
    399    return nullptr;
    400  }
    401 
    402  const auto size = srcSurface->GetSize();
    403  const auto format = srcSurface->GetFormat();
    404 
    405  RefPtr<gfx::DataSourceSurface> dstSurface =
    406      gfx::Factory::CreateDataSourceSurface(size, format, /* aZero */ false);
    407  if (!dstSurface) {
    408    return nullptr;
    409  }
    410 
    411  gfx::DataSourceSurface::ScopedMap srcMap(srcSurface,
    412                                           gfx::DataSourceSurface::READ);
    413  gfx::DataSourceSurface::ScopedMap dstMap(dstSurface,
    414                                           gfx::DataSourceSurface::WRITE);
    415  if (!srcMap.IsMapped() || !dstMap.IsMapped()) {
    416    return nullptr;
    417  }
    418 
    419  bool success;
    420  switch (aOriginPos) {
    421    case gl::OriginPos::BottomLeft:
    422      if (aHasAlpha && !aIsAlphaPremult) {
    423        success = gfx::PremultiplyYFlipData(
    424            srcMap.GetData(), srcMap.GetStride(), format, dstMap.GetData(),
    425            dstMap.GetStride(), format, size);
    426      } else {
    427        success = gfx::SwizzleYFlipData(srcMap.GetData(), srcMap.GetStride(),
    428                                        format, dstMap.GetData(),
    429                                        dstMap.GetStride(), format, size);
    430      }
    431      break;
    432    case gl::OriginPos::TopLeft:
    433      if (aHasAlpha && !aIsAlphaPremult) {
    434        success = gfx::PremultiplyData(srcMap.GetData(), srcMap.GetStride(),
    435                                       format, dstMap.GetData(),
    436                                       dstMap.GetStride(), format, size);
    437      } else {
    438        success = gfx::SwizzleData(srcMap.GetData(), srcMap.GetStride(), format,
    439                                   dstMap.GetData(), dstMap.GetStride(), format,
    440                                   size);
    441      }
    442      break;
    443    default:
    444      MOZ_ASSERT_UNREACHABLE("Unhandled origin position!");
    445      success = false;
    446      break;
    447  }
    448 
    449  if (!success) {
    450    return nullptr;
    451  }
    452 
    453  return dstSurface.forget();
    454 }
    455 
    456 already_AddRefed<gfx::SourceSurface>
    457 OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() {
    458  MOZ_ASSERT(NS_IsMainThread());
    459 
    460  class SnapshotWorkerRunnable final : public MainThreadWorkerRunnable {
    461   public:
    462    explicit SnapshotWorkerRunnable(
    463        OffscreenCanvasDisplayHelper* aDisplayHelper)
    464        : MainThreadWorkerRunnable("SnapshotWorkerRunnable"),
    465          mMonitor("SnapshotWorkerRunnable::mMonitor"),
    466          mDisplayHelper(aDisplayHelper) {}
    467 
    468    bool WorkerRun(JSContext*, WorkerPrivate*) override {
    469      // The OffscreenCanvas can only be freed on the worker thread, so we
    470      // cannot be racing with an OffscreenCanvas::DestroyCanvas call and its
    471      // destructor. We just need to make sure we don't call into
    472      // OffscreenCanvas while holding the lock since it calls back into the
    473      // OffscreenCanvasDisplayHelper.
    474      RefPtr<OffscreenCanvas> canvas;
    475      {
    476        MutexAutoLock lock(mDisplayHelper->mMutex);
    477        canvas = mDisplayHelper->mOffscreenCanvas;
    478      }
    479 
    480      // Now that we are on the correct thread, we can extract the snapshot. If
    481      // it is a Skia surface, perform a copy to threading issues.
    482      RefPtr<gfx::SourceSurface> surface;
    483      if (canvas) {
    484        if (auto* context = canvas->GetContext()) {
    485          surface =
    486              context->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
    487          if (surface && surface->GetType() == gfx::SurfaceType::SKIA) {
    488            surface = gfx::Factory::CopyDataSourceSurface(
    489                static_cast<gfx::DataSourceSurface*>(surface.get()));
    490          }
    491        }
    492      }
    493 
    494      MonitorAutoLock lock(mMonitor);
    495      mSurface = std::move(surface);
    496      mComplete = true;
    497      lock.NotifyAll();
    498      return true;
    499    }
    500 
    501    already_AddRefed<gfx::SourceSurface> Wait(int32_t aTimeoutMs) {
    502      MonitorAutoLock lock(mMonitor);
    503 
    504      TimeDuration timeout = TimeDuration::FromMilliseconds(aTimeoutMs);
    505      while (!mComplete) {
    506        if (lock.Wait(timeout) == CVStatus::Timeout) {
    507          return nullptr;
    508        }
    509      }
    510 
    511      return mSurface.forget();
    512    }
    513 
    514   private:
    515    Monitor mMonitor;
    516    RefPtr<OffscreenCanvasDisplayHelper> mDisplayHelper;
    517    RefPtr<gfx::SourceSurface> mSurface MOZ_GUARDED_BY(mMonitor);
    518    bool mComplete MOZ_GUARDED_BY(mMonitor) = false;
    519  };
    520 
    521  bool hasAlpha;
    522  bool isAlphaPremult;
    523  gl::OriginPos originPos;
    524  HTMLCanvasElement* canvasElement;
    525  RefPtr<gfx::SourceSurface> surface;
    526  RefPtr<SnapshotWorkerRunnable> workerRunnable;
    527 
    528  {
    529    MutexAutoLock lock(mMutex);
    530 #ifdef MOZ_WIDGET_ANDROID
    531    // On Android, we cannot both display a GL context and read back the pixels.
    532    if (mCanvasElement) {
    533      return nullptr;
    534    }
    535 #endif
    536 
    537    hasAlpha = !mData.mIsOpaque;
    538    isAlphaPremult = mData.mIsAlphaPremult;
    539    originPos = mData.mOriginPos;
    540    canvasElement = mCanvasElement;
    541    if (mWorkerRef) {
    542      workerRunnable = MakeRefPtr<SnapshotWorkerRunnable>(this);
    543      workerRunnable->Dispatch(mWorkerRef->Private());
    544    }
    545  }
    546 
    547  if (workerRunnable) {
    548    // We transferred to a DOM worker, so we need to do the readback on the
    549    // owning thread and wait for the result.
    550    surface = workerRunnable->Wait(
    551        StaticPrefs::gfx_offscreencanvas_snapshot_timeout_ms());
    552  } else if (canvasElement) {
    553    // If we have a context, it is owned by the main thread.
    554    const auto* offscreenCanvas = canvasElement->GetOffscreenCanvas();
    555    if (nsICanvasRenderingContextInternal* context =
    556            offscreenCanvas->GetContext()) {
    557      surface =
    558          context->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
    559    }
    560  }
    561 
    562  return TransformSurface(surface, hasAlpha, isAlphaPremult, originPos);
    563 }
    564 
    565 already_AddRefed<layers::Image> OffscreenCanvasDisplayHelper::GetAsImage() {
    566  MOZ_ASSERT(NS_IsMainThread());
    567 
    568  RefPtr<gfx::SourceSurface> surface = GetSurfaceSnapshot();
    569  if (!surface) {
    570    return nullptr;
    571  }
    572  return MakeAndAddRef<layers::SourceSurfaceImage>(surface);
    573 }
    574 
    575 UniquePtr<uint8_t[]> OffscreenCanvasDisplayHelper::GetImageBuffer(
    576    CanvasUtils::ImageExtraction aExtractionBehavior, int32_t* aOutFormat,
    577    gfx::IntSize* aOutImageSize) {
    578  RefPtr<gfx::SourceSurface> surface = GetSurfaceSnapshot();
    579  if (!surface) {
    580    return nullptr;
    581  }
    582 
    583  RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
    584  if (!dataSurface) {
    585    return nullptr;
    586  }
    587 
    588  *aOutFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
    589  *aOutImageSize = dataSurface->GetSize();
    590 
    591  UniquePtr<uint8_t[]> imageBuffer = gfx::SurfaceToPackedBGRA(dataSurface);
    592  if (!imageBuffer) {
    593    return nullptr;
    594  }
    595 
    596  nsIPrincipal* principal = nullptr;
    597  nsICookieJarSettings* cookieJarSettings = nullptr;
    598  {
    599    // This function is never called with mOffscreenCanvas set, so we skip
    600    // the check for it.
    601    MutexAutoLock lock(mMutex);
    602    MOZ_ASSERT(!mOffscreenCanvas);
    603 
    604    if (mCanvasElement) {
    605      principal = mCanvasElement->NodePrincipal();
    606      cookieJarSettings = mCanvasElement->OwnerDoc()->CookieJarSettings();
    607    }
    608  }
    609  nsRFPService::PotentiallyDumpImage(
    610      principal, imageBuffer.get(), dataSurface->GetSize().width,
    611      dataSurface->GetSize().height,
    612      dataSurface->GetSize().width * dataSurface->GetSize().height * 4);
    613  if (aExtractionBehavior == CanvasUtils::ImageExtraction::Randomize) {
    614    nsRFPService::RandomizePixels(
    615        cookieJarSettings, principal, imageBuffer.get(),
    616        dataSurface->GetSize().width, dataSurface->GetSize().height,
    617        dataSurface->GetSize().width * dataSurface->GetSize().height * 4,
    618        gfx::SurfaceFormat::A8R8G8B8_UINT32);
    619  }
    620 
    621  return imageBuffer;
    622 }
    623 
    624 }  // namespace mozilla::dom