tor-browser

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

OffscreenCanvas.cpp (23317B)


      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 "OffscreenCanvas.h"
      8 
      9 #include "CanvasRenderingContext2D.h"
     10 #include "CanvasUtils.h"
     11 #include "ClientWebGLContext.h"
     12 #include "GLContext.h"
     13 #include "GLScreenBuffer.h"
     14 #include "ImageBitmap.h"
     15 #include "ImageBitmapRenderingContext.h"
     16 #include "WebGLChild.h"
     17 #include "mozilla/Atomics.h"
     18 #include "mozilla/CheckedInt.h"
     19 #include "mozilla/Logging.h"
     20 #include "mozilla/dom/BlobImpl.h"
     21 #include "mozilla/dom/DocumentInlines.h"
     22 #include "mozilla/dom/OffscreenCanvasBinding.h"
     23 #include "mozilla/dom/OffscreenCanvasDisplayHelper.h"
     24 #include "mozilla/dom/OffscreenCanvasRenderingContext2D.h"
     25 #include "mozilla/dom/Promise.h"
     26 #include "mozilla/dom/WorkerPrivate.h"
     27 #include "mozilla/dom/WorkerRef.h"
     28 #include "mozilla/dom/WorkerScope.h"
     29 #include "mozilla/layers/ImageBridgeChild.h"
     30 #include "mozilla/webgpu/CanvasContext.h"
     31 #include "nsContentUtils.h"
     32 #include "nsIPermissionManager.h"
     33 #include "nsProxyRelease.h"
     34 
     35 namespace mozilla::dom {
     36 
     37 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
     38 
     39 OffscreenCanvasCloneData::OffscreenCanvasCloneData(
     40    OffscreenCanvasDisplayHelper* aDisplay, uint32_t aWidth, uint32_t aHeight,
     41    layers::LayersBackend aCompositorBackend, bool aNeutered, bool aIsWriteOnly,
     42    nsIPrincipal* aExpandedReader)
     43    : mDisplay(aDisplay),
     44      mWidth(aWidth),
     45      mHeight(aHeight),
     46      mCompositorBackendType(aCompositorBackend),
     47      mNeutered(aNeutered),
     48      mIsWriteOnly(aIsWriteOnly),
     49      mExpandedReader(aExpandedReader) {}
     50 
     51 OffscreenCanvasCloneData::~OffscreenCanvasCloneData() {
     52  NS_ReleaseOnMainThread("OffscreenCanvasCloneData::mExpandedReader",
     53                         mExpandedReader.forget());
     54 }
     55 
     56 OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth,
     57                                 uint32_t aHeight)
     58    : DOMEventTargetHelper(aGlobal),
     59      mWidth(aWidth),
     60      mHeight(aHeight),
     61      mFontVisibility(ComputeFontVisibility()) {}
     62 
     63 OffscreenCanvas::OffscreenCanvas(
     64    nsIGlobalObject* aGlobal, uint32_t aWidth, uint32_t aHeight,
     65    layers::LayersBackend aCompositorBackend,
     66    already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay)
     67    : DOMEventTargetHelper(aGlobal),
     68      mWidth(aWidth),
     69      mHeight(aHeight),
     70      mCompositorBackendType(aCompositorBackend),
     71      mDisplay(aDisplay),
     72      mFontVisibility(ComputeFontVisibility()) {}
     73 
     74 OffscreenCanvas::~OffscreenCanvas() {
     75  Destroy();
     76  NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
     77                         mExpandedReader.forget());
     78 }
     79 
     80 void OffscreenCanvas::Destroy() {
     81  if (mDisplay) {
     82    mDisplay->DestroyCanvas();
     83  }
     84 }
     85 
     86 JSObject* OffscreenCanvas::WrapObject(JSContext* aCx,
     87                                      JS::Handle<JSObject*> aGivenProto) {
     88  return OffscreenCanvas_Binding::Wrap(aCx, this, aGivenProto);
     89 }
     90 
     91 /* static */
     92 already_AddRefed<OffscreenCanvas> OffscreenCanvas::Constructor(
     93    const GlobalObject& aGlobal, uint32_t aWidth, uint32_t aHeight,
     94    ErrorResult& aRv) {
     95  // CanvasRenderingContextHelper::GetWidthHeight wants us to return
     96  // an nsIntSize, so make sure that that will work.
     97  if (!CheckedInt<int32_t>(aWidth).isValid()) {
     98    aRv.ThrowRangeError(
     99        nsPrintfCString("OffscreenCanvas width %u is out of range: must be no "
    100                        "greater than 2147483647.",
    101                        aWidth));
    102    return nullptr;
    103  }
    104  if (!CheckedInt<int32_t>(aHeight).isValid()) {
    105    aRv.ThrowRangeError(
    106        nsPrintfCString("OffscreenCanvas height %u is out of range: must be no "
    107                        "greater than 2147483647.",
    108                        aHeight));
    109    return nullptr;
    110  }
    111 
    112  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    113  RefPtr<OffscreenCanvas> offscreenCanvas =
    114      new OffscreenCanvas(global, aWidth, aHeight);
    115  return offscreenCanvas.forget();
    116 }
    117 
    118 void OffscreenCanvas::SetWidth(uint32_t aWidth, ErrorResult& aRv) {
    119  if (mNeutered) {
    120    aRv.ThrowInvalidStateError("Cannot set width of detached OffscreenCanvas.");
    121    return;
    122  }
    123 
    124  // CanvasRenderingContextHelper::GetWidthHeight wants us to return
    125  // an nsIntSize, so make sure that that will work.
    126  if (!CheckedInt<int32_t>(aWidth).isValid()) {
    127    aRv.ThrowRangeError(
    128        nsPrintfCString("OffscreenCanvas width %u is out of range: must be no "
    129                        "greater than 2147483647.",
    130                        aWidth));
    131    return;
    132  }
    133 
    134  mWidth = aWidth;
    135  CanvasAttrChanged();
    136 }
    137 
    138 void OffscreenCanvas::SetHeight(uint32_t aHeight, ErrorResult& aRv) {
    139  if (mNeutered) {
    140    aRv.ThrowInvalidStateError(
    141        "Cannot set height of detached OffscreenCanvas.");
    142    return;
    143  }
    144 
    145  // CanvasRenderingContextHelper::GetWidthHeight wants us to return
    146  // an nsIntSize, so make sure that that will work.
    147  if (!CheckedInt<int32_t>(aHeight).isValid()) {
    148    aRv.ThrowRangeError(
    149        nsPrintfCString("OffscreenCanvas height %u is out of range: must be no "
    150                        "greater than 2147483647.",
    151                        aHeight));
    152    return;
    153  }
    154 
    155  mHeight = aHeight;
    156  CanvasAttrChanged();
    157 }
    158 
    159 void OffscreenCanvas::SetSize(const nsIntSize& aSize, ErrorResult& aRv) {
    160  if (mNeutered) {
    161    aRv.ThrowInvalidStateError(
    162        "Cannot set dimensions of detached OffscreenCanvas.");
    163    return;
    164  }
    165 
    166  if (NS_WARN_IF(aSize.IsEmpty())) {
    167    aRv.ThrowRangeError("OffscreenCanvas size is empty, must be non-empty.");
    168    return;
    169  }
    170 
    171  mWidth = aSize.width;
    172  mHeight = aSize.height;
    173  CanvasAttrChanged();
    174 }
    175 
    176 void OffscreenCanvas::GetContext(
    177    JSContext* aCx, const OffscreenRenderingContextId& aContextId,
    178    JS::Handle<JS::Value> aContextOptions,
    179    Nullable<OwningOffscreenRenderingContext>& aResult, ErrorResult& aRv) {
    180  if (mNeutered) {
    181    aResult.SetNull();
    182    aRv.ThrowInvalidStateError(
    183        "Cannot create context for detached OffscreenCanvas.");
    184    return;
    185  }
    186 
    187  CanvasContextType contextType;
    188  switch (aContextId) {
    189    case OffscreenRenderingContextId::_2d:
    190      contextType = CanvasContextType::OffscreenCanvas2D;
    191      break;
    192    case OffscreenRenderingContextId::Bitmaprenderer:
    193      contextType = CanvasContextType::ImageBitmap;
    194      break;
    195    case OffscreenRenderingContextId::Webgl:
    196      contextType = CanvasContextType::WebGL1;
    197      break;
    198    case OffscreenRenderingContextId::Webgl2:
    199      contextType = CanvasContextType::WebGL2;
    200      break;
    201    case OffscreenRenderingContextId::Webgpu:
    202      contextType = CanvasContextType::WebGPU;
    203      break;
    204    default:
    205      MOZ_ASSERT_UNREACHABLE("Unhandled canvas type!");
    206      aResult.SetNull();
    207      aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    208      return;
    209  }
    210 
    211  // If we are on a worker, we need to give our OffscreenCanvasDisplayHelper
    212  // object access to a worker ref so we can dispatch properly during painting
    213  // if we need to flush our contents to its ImageContainer for display.
    214  RefPtr<ThreadSafeWorkerRef> workerRef;
    215  if (mDisplay) {
    216    if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
    217      RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create(
    218          workerPrivate, "OffscreenCanvas::GetContext",
    219          [display = mDisplay]() { display->DestroyCanvas(); });
    220      if (NS_WARN_IF(!strongRef)) {
    221        aResult.SetNull();
    222        aRv.ThrowUnknownError("Worker shutting down");
    223        return;
    224      }
    225 
    226      workerRef = new ThreadSafeWorkerRef(strongRef);
    227    } else {
    228      MOZ_ASSERT(NS_IsMainThread());
    229    }
    230  }
    231 
    232  RefPtr<nsISupports> result = CanvasRenderingContextHelper::GetOrCreateContext(
    233      aCx, contextType, aContextOptions, aRv);
    234  if (!result) {
    235    aResult.SetNull();
    236    return;
    237  }
    238 
    239  Maybe<mozilla::ipc::ActorId> childId;
    240 
    241  MOZ_ASSERT(mCurrentContext);
    242  switch (mCurrentContextType) {
    243    case CanvasContextType::OffscreenCanvas2D:
    244      aResult.SetValue().SetAsOffscreenCanvasRenderingContext2D() =
    245          *static_cast<OffscreenCanvasRenderingContext2D*>(
    246              mCurrentContext.get());
    247      break;
    248    case CanvasContextType::ImageBitmap:
    249      aResult.SetValue().SetAsImageBitmapRenderingContext() =
    250          *static_cast<ImageBitmapRenderingContext*>(mCurrentContext.get());
    251      break;
    252    case CanvasContextType::WebGL1:
    253    case CanvasContextType::WebGL2: {
    254      auto* webgl = static_cast<ClientWebGLContext*>(mCurrentContext.get());
    255      WebGLChild* webglChild = webgl->GetChild();
    256      if (webglChild) {
    257        childId.emplace(webglChild->Id());
    258      }
    259      aResult.SetValue().SetAsWebGLRenderingContext() = *webgl;
    260      break;
    261    }
    262    case CanvasContextType::WebGPU:
    263      aResult.SetValue().SetAsGPUCanvasContext() =
    264          *static_cast<webgpu::CanvasContext*>(mCurrentContext.get());
    265      break;
    266    default:
    267      MOZ_ASSERT_UNREACHABLE("Unhandled canvas type!");
    268      aResult.SetNull();
    269      break;
    270  }
    271 
    272  if (mDisplay) {
    273    mDisplay->UpdateContext(this, std::move(workerRef), mCurrentContextType,
    274                            childId);
    275  }
    276 }
    277 
    278 already_AddRefed<nsICanvasRenderingContextInternal>
    279 OffscreenCanvas::CreateContext(CanvasContextType aContextType) {
    280  RefPtr<nsICanvasRenderingContextInternal> ret =
    281      CanvasRenderingContextHelper::CreateContext(aContextType);
    282  if (NS_WARN_IF(!ret)) {
    283    return nullptr;
    284  }
    285 
    286  ret->SetOffscreenCanvas(this);
    287  return ret.forget();
    288 }
    289 
    290 Maybe<uint64_t> OffscreenCanvas::GetWindowID() const {
    291  if (NS_IsMainThread()) {
    292    if (nsIGlobalObject* global = GetOwnerGlobal()) {
    293      if (auto* window = global->GetAsInnerWindow()) {
    294        return Some(window->WindowID());
    295      }
    296    }
    297  } else if (auto* workerPrivate = GetCurrentThreadWorkerPrivate()) {
    298    return Some(workerPrivate->WindowID());
    299  }
    300  return Nothing();
    301 }
    302 
    303 void OffscreenCanvas::UpdateDisplayData(
    304    const OffscreenCanvasDisplayData& aData) {
    305  if (!mDisplay) {
    306    return;
    307  }
    308 
    309  mPendingUpdate = Some(aData);
    310  QueueCommitToCompositor();
    311 }
    312 
    313 void OffscreenCanvas::QueueCommitToCompositor() {
    314  if (!mDisplay || !mCurrentContext || mPendingCommit) {
    315    // If we already have a commit pending, or we have no bound display/context,
    316    // just bail out.
    317    return;
    318  }
    319 
    320  mPendingCommit = NS_NewCancelableRunnableFunction(
    321      "OffscreenCanvas::QueueCommitToCompositor",
    322      [self = RefPtr{this}] { self->DequeueCommitToCompositor(); });
    323  NS_DispatchToCurrentThread(mPendingCommit);
    324 }
    325 
    326 void OffscreenCanvas::DequeueCommitToCompositor() {
    327  MOZ_ASSERT(mPendingCommit);
    328  mPendingCommit = nullptr;
    329  Maybe<OffscreenCanvasDisplayData> update = std::move(mPendingUpdate);
    330  mDisplay->CommitFrameToCompositor(mCurrentContext, update);
    331 }
    332 
    333 void OffscreenCanvas::CommitFrameToCompositor() {
    334  if (!mDisplay || !mCurrentContext) {
    335    // This offscreen canvas doesn't associate to any HTML canvas element.
    336    // So, just bail out.
    337    return;
    338  }
    339 
    340  if (mPendingCommit) {
    341    // We got an explicit commit while waiting for an implicit.
    342    mPendingCommit->Cancel();
    343    mPendingCommit = nullptr;
    344  }
    345 
    346  Maybe<OffscreenCanvasDisplayData> update = std::move(mPendingUpdate);
    347  mDisplay->CommitFrameToCompositor(mCurrentContext, update);
    348 }
    349 
    350 UniquePtr<OffscreenCanvasCloneData> OffscreenCanvas::ToCloneData(
    351    JSContext* aCx) {
    352  if (NS_WARN_IF(mNeutered)) {
    353    ErrorResult rv;
    354    rv.ThrowDataCloneError(
    355        "Cannot clone OffscreenCanvas that is already transferred.");
    356    MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx));
    357    return nullptr;
    358  }
    359 
    360  if (NS_WARN_IF(mCurrentContext)) {
    361    ErrorResult rv;
    362    rv.ThrowInvalidStateError("Cannot clone canvas with context.");
    363    MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx));
    364    return nullptr;
    365  }
    366 
    367  // Check if we are using HTMLCanvasElement::captureStream. This is not
    368  // defined by the spec yet, so it is better to fail now than implement
    369  // something not compliant:
    370  // https://github.com/w3c/mediacapture-fromelement/issues/65
    371  // https://github.com/w3c/mediacapture-extensions/pull/26
    372  // https://github.com/web-platform-tests/wpt/issues/21102
    373  if (mDisplay && NS_WARN_IF(mDisplay->UsingElementCaptureStream())) {
    374    ErrorResult rv;
    375    rv.ThrowNotSupportedError(
    376        "Cannot transfer OffscreenCanvas bound to element using "
    377        "captureStream.");
    378    MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx));
    379    return nullptr;
    380  }
    381 
    382  auto cloneData = MakeUnique<OffscreenCanvasCloneData>(
    383      mDisplay, mWidth, mHeight, mCompositorBackendType, mNeutered,
    384      mIsWriteOnly, mExpandedReader);
    385  SetNeutered();
    386  return cloneData;
    387 }
    388 
    389 already_AddRefed<ImageBitmap> OffscreenCanvas::TransferToImageBitmap(
    390    ErrorResult& aRv) {
    391  if (mNeutered) {
    392    aRv.ThrowInvalidStateError(
    393        "Cannot get bitmap from detached OffscreenCanvas.");
    394    return nullptr;
    395  }
    396 
    397  if (!mCurrentContext) {
    398    aRv.ThrowInvalidStateError(
    399        "Cannot get bitmap from canvas without a context.");
    400    return nullptr;
    401  }
    402 
    403  RefPtr<ImageBitmap> result =
    404      ImageBitmap::CreateFromOffscreenCanvas(GetOwnerGlobal(), *this, aRv);
    405  if (!result) {
    406    return nullptr;
    407  }
    408 
    409  if (mCurrentContext) {
    410    mCurrentContext->ResetBitmap();
    411  }
    412  return result.forget();
    413 }
    414 
    415 already_AddRefed<EncodeCompleteCallback>
    416 OffscreenCanvas::CreateEncodeCompleteCallback(Promise* aPromise) {
    417  // Encoder callback when encoding is complete.
    418  class EncodeCallback : public EncodeCompleteCallback {
    419   public:
    420    explicit EncodeCallback(Promise* aPromise)
    421        : mPromise(aPromise), mCanceled(false) {}
    422 
    423    void MaybeInitWorkerRef() {
    424      WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
    425      if (wp) {
    426        mWorkerRef = WeakWorkerRef::Create(
    427            wp, [self = RefPtr{this}]() { self->Cancel(); });
    428        if (!mWorkerRef) {
    429          Cancel();
    430        }
    431      }
    432    }
    433 
    434    nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
    435      RefPtr<BlobImpl> blobImpl = aBlobImpl;
    436      mWorkerRef = nullptr;
    437 
    438      if (mPromise) {
    439        RefPtr<nsIGlobalObject> global = mPromise->GetGlobalObject();
    440        if (NS_WARN_IF(!global) || NS_WARN_IF(!blobImpl)) {
    441          mPromise->MaybeReject(NS_ERROR_FAILURE);
    442        } else {
    443          RefPtr<Blob> blob = Blob::Create(global, blobImpl);
    444          if (NS_WARN_IF(!blob)) {
    445            mPromise->MaybeReject(NS_ERROR_FAILURE);
    446          } else {
    447            mPromise->MaybeResolve(blob);
    448          }
    449        }
    450      }
    451 
    452      mPromise = nullptr;
    453 
    454      return NS_OK;
    455    }
    456 
    457    bool CanBeDeletedOnAnyThread() override { return mCanceled; }
    458 
    459    void Cancel() {
    460      mPromise = nullptr;
    461      mWorkerRef = nullptr;
    462      mCanceled = true;
    463    }
    464 
    465    RefPtr<Promise> mPromise;
    466    RefPtr<WeakWorkerRef> mWorkerRef;
    467    Atomic<bool> mCanceled;
    468  };
    469 
    470  RefPtr<EncodeCallback> p = MakeAndAddRef<EncodeCallback>(aPromise);
    471  p->MaybeInitWorkerRef();
    472  return p.forget();
    473 }
    474 
    475 already_AddRefed<Promise> OffscreenCanvas::ConvertToBlob(
    476    const ImageEncodeOptions& aOptions, ErrorResult& aRv) {
    477  // do a trust check if this is a write-only canvas
    478  if (mIsWriteOnly) {
    479    aRv.ThrowSecurityError("Cannot get blob from write-only canvas.");
    480    return nullptr;
    481  }
    482 
    483  if (mNeutered) {
    484    aRv.ThrowInvalidStateError(
    485        "Cannot get blob from detached OffscreenCanvas.");
    486    return nullptr;
    487  }
    488 
    489  if (mWidth == 0 || mHeight == 0) {
    490    aRv.ThrowIndexSizeError("Cannot get blob from empty canvas.");
    491    return nullptr;
    492  }
    493 
    494  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
    495 
    496  RefPtr<Promise> promise = Promise::Create(global, aRv);
    497  if (aRv.Failed()) {
    498    return nullptr;
    499  }
    500 
    501  nsAutoString type;
    502  nsContentUtils::ASCIIToLower(aOptions.mType, type);
    503 
    504  nsAutoString encodeOptions;
    505 
    506  // Only image/jpeg and image/webp support the quality parameter.
    507  if (aOptions.mQuality.WasPassed() &&
    508      (type.EqualsLiteral("image/jpeg") || type.EqualsLiteral("image/webp"))) {
    509    encodeOptions.AppendLiteral("quality=");
    510    encodeOptions.AppendInt(NS_lround(aOptions.mQuality.Value() * 100.0));
    511  }
    512 
    513  RefPtr<EncodeCompleteCallback> callback =
    514      CreateEncodeCompleteCallback(promise);
    515 
    516  CanvasUtils::ImageExtraction extractionBehaviour =
    517      CanvasUtils::ImageExtractionResult(
    518          this, nsContentUtils::GetCurrentJSContext(),
    519          mCurrentContext ? mCurrentContext->PrincipalOrNull() : nullptr);
    520 
    521  if (extractionBehaviour != CanvasUtils::ImageExtraction::Placeholder &&
    522      GetContext()) {
    523    GetContext()->RecordCanvasUsage(CanvasExtractionAPI::ToBlob,
    524                                    GetWidthHeight());
    525  }
    526 
    527  CanvasRenderingContextHelper::ToBlob(callback, type, encodeOptions,
    528                                       /* aUsingCustomOptions */ false,
    529                                       extractionBehaviour, aRv);
    530 
    531  if (aRv.Failed()) {
    532    promise->MaybeReject(std::move(aRv));
    533  }
    534 
    535  return promise.forget();
    536 }
    537 
    538 already_AddRefed<Promise> OffscreenCanvas::ToBlob(JSContext* aCx,
    539                                                  const nsAString& aType,
    540                                                  JS::Handle<JS::Value> aParams,
    541                                                  ErrorResult& aRv) {
    542  // do a trust check if this is a write-only canvas
    543  if (mIsWriteOnly) {
    544    aRv.ThrowSecurityError("Cannot get blob from write-only canvas.");
    545    return nullptr;
    546  }
    547 
    548  if (mNeutered) {
    549    aRv.ThrowInvalidStateError(
    550        "Cannot get blob from detached OffscreenCanvas.");
    551    return nullptr;
    552  }
    553 
    554  if (mWidth == 0 || mHeight == 0) {
    555    aRv.ThrowIndexSizeError("Cannot get blob from empty canvas.");
    556    return nullptr;
    557  }
    558 
    559  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
    560 
    561  RefPtr<Promise> promise = Promise::Create(global, aRv);
    562  if (aRv.Failed()) {
    563    return nullptr;
    564  }
    565 
    566  RefPtr<EncodeCompleteCallback> callback =
    567      CreateEncodeCompleteCallback(promise);
    568  CanvasUtils::ImageExtraction extractionBehaviour =
    569      CanvasUtils::ImageExtractionResult(
    570          this, aCx,
    571          mCurrentContext ? mCurrentContext->PrincipalOrNull() : nullptr);
    572 
    573  if (extractionBehaviour != CanvasUtils::ImageExtraction::Placeholder &&
    574      GetContext()) {
    575    GetContext()->RecordCanvasUsage(CanvasExtractionAPI::ToBlob,
    576                                    GetWidthHeight());
    577  }
    578 
    579  CanvasRenderingContextHelper::ToBlob(aCx, callback, aType, aParams,
    580                                       extractionBehaviour, aRv);
    581 
    582  return promise.forget();
    583 }
    584 
    585 already_AddRefed<gfx::SourceSurface> OffscreenCanvas::GetSurfaceSnapshot(
    586    gfxAlphaType* const aOutAlphaType) {
    587  if (!mCurrentContext) {
    588    return nullptr;
    589  }
    590 
    591  return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
    592 }
    593 
    594 void OffscreenCanvas::SetWriteOnly(RefPtr<nsIPrincipal>&& aExpandedReader) {
    595  NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
    596                         mExpandedReader.forget());
    597  mExpandedReader = std::move(aExpandedReader);
    598  mIsWriteOnly = true;
    599 
    600  if (mDisplay) {
    601    mDisplay->SetWriteOnly(mExpandedReader);
    602  }
    603 }
    604 
    605 bool OffscreenCanvas::CallerCanRead(nsIPrincipal& aPrincipal) const {
    606  if (!mIsWriteOnly) {
    607    return true;
    608  }
    609 
    610  // If mExpandedReader is set, this canvas was tainted only by
    611  // mExpandedReader's resources. So allow reading if the subject
    612  // principal subsumes mExpandedReader.
    613  if (mExpandedReader && aPrincipal.Subsumes(mExpandedReader)) {
    614    return true;
    615  }
    616 
    617  return nsContentUtils::PrincipalHasPermission(aPrincipal,
    618                                                nsGkAtoms::all_urlsPermission);
    619 }
    620 
    621 bool OffscreenCanvas::ShouldResistFingerprinting(RFPTarget aTarget) const {
    622  return nsContentUtils::ShouldResistFingerprinting(GetOwnerGlobal(), aTarget);
    623 }
    624 
    625 /* static */
    626 already_AddRefed<OffscreenCanvas> OffscreenCanvas::CreateFromCloneData(
    627    nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData) {
    628  MOZ_ASSERT(aData);
    629  RefPtr<OffscreenCanvas> wc = new OffscreenCanvas(
    630      aGlobal, aData->mWidth, aData->mHeight, aData->mCompositorBackendType,
    631      aData->mDisplay.forget());
    632  if (aData->mNeutered) {
    633    wc->SetNeutered();
    634  }
    635  if (aData->mIsWriteOnly) {
    636    wc->SetWriteOnly(std::move(aData->mExpandedReader));
    637  }
    638  return wc.forget();
    639 }
    640 
    641 // FontVisibilityProvider implementation
    642 FontVisibility OffscreenCanvas::GetFontVisibility() const {
    643  return mFontVisibility;
    644 }
    645 
    646 void OffscreenCanvas::ReportBlockedFontFamily(const nsCString& aMsg) const {
    647  MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Info, ("%s", aMsg.get()));
    648  if (Maybe<uint64_t> windowID = GetWindowID()) {
    649    nsContentUtils::ReportToConsoleByWindowID(NS_ConvertUTF8toUTF16(aMsg),
    650                                              nsIScriptError::warningFlag,
    651                                              "Security"_ns, *windowID);
    652    return;
    653  }
    654 }
    655 
    656 bool OffscreenCanvas::IsChrome() const {
    657  if (NS_IsMainThread()) {
    658    nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetOwnerGlobal());
    659    NS_ENSURE_TRUE(win, false);
    660 
    661    nsCOMPtr<Document> doc = win->GetExtantDoc();
    662    NS_ENSURE_TRUE(doc, false);
    663 
    664    return doc->ChromeRulesEnabled();
    665  }
    666 
    667  dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate();
    668  NS_ENSURE_TRUE(worker, false);
    669 
    670  return worker->IsChromeWorker();
    671 }
    672 
    673 bool OffscreenCanvas::IsPrivateBrowsing() const {
    674  if (NS_IsMainThread()) {
    675    nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetOwnerGlobal());
    676    NS_ENSURE_TRUE(win, false);
    677 
    678    nsCOMPtr<Document> doc = win->GetExtantDoc();
    679    NS_ENSURE_TRUE(doc, false);
    680 
    681    return doc->IsInPrivateBrowsing();
    682  }
    683 
    684  dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate();
    685  NS_ENSURE_TRUE(worker, false);
    686 
    687  return worker->IsPrivateBrowsing();
    688 }
    689 
    690 nsICookieJarSettings* OffscreenCanvas::GetCookieJarSettings() const {
    691  if (nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetOwnerGlobal())) {
    692    if (nsCOMPtr<Document> doc = win->GetExtantDoc()) {
    693      return doc->CookieJarSettings();
    694    }
    695  }
    696 
    697  if (dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate()) {
    698    return worker->CookieJarSettings();
    699  }
    700 
    701  return nullptr;
    702 }
    703 
    704 Maybe<FontVisibility> OffscreenCanvas::MaybeInheritFontVisibility() const {
    705  if (NS_IsMainThread()) {
    706    nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(GetOwnerGlobal());
    707    NS_ENSURE_TRUE(win, Nothing());
    708 
    709    nsCOMPtr<Document> doc = win->GetExtantDoc();
    710    NS_ENSURE_TRUE(doc, Nothing());
    711 
    712    nsPresContext* presContext = doc->GetPresContext();
    713    NS_ENSURE_TRUE(presContext, Nothing());
    714 
    715    return Some(presContext->GetFontVisibility());
    716  }
    717 
    718  dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate();
    719  NS_ENSURE_TRUE(worker, Nothing());
    720 
    721  return Some(worker->GetFontVisibility());
    722 }
    723 
    724 void OffscreenCanvas::UserFontSetUpdated(gfxUserFontEntry*) {}
    725 
    726 NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper,
    727                                   mCurrentContext)
    728 
    729 NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
    730 NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
    731 
    732 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas)
    733  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
    734 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    735 
    736 }  // namespace mozilla::dom