Backed out changeset 828633114de2 (bug1736177) Backed out changeset 5be8557c4721 (bug1736177) Backed out changeset 49f8b4205a46 (bug1736177) Backed out changeset 2610d4464ad5 (bug1736177) Backed out changeset 6d6c78c31c28 (bug1736177) Backed out changeset d55f1ee88bb9 (bug1736177) Backed out changeset bf588f8ffcf1 (bug1736177) Backed out changeset 86f6f6d86c6c (bug1736177) Backed out changeset f400c75c5829 (bug1736177) Backed out changeset 4a34124d5f4e (bug1736177) Backed out changeset 70aff7fcd001 (bug1736177) Backed out changeset db2347ee8147 (bug1736177) Backed out changeset 3dde5ddb65e5 (bug 1738971) Backed out changeset 894ba6b7b68f (bug 1738971) Backed out changeset dc4503052cf1 (bug 1738971) Backed out changeset d9aef3e9797e (bug 1738971) Backed out changeset 562a1e8e5ac3 (bug 1738971)
312 lines
9.9 KiB
C++
312 lines
9.9 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "OffscreenCanvas.h"
|
|
|
|
#include "mozilla/dom/BlobImpl.h"
|
|
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/WorkerPrivate.h"
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
#include "mozilla/layers/CanvasRenderer.h"
|
|
#include "mozilla/layers/CanvasClient.h"
|
|
#include "mozilla/layers/ImageBridgeChild.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "CanvasRenderingContext2D.h"
|
|
#include "CanvasUtils.h"
|
|
#include "GLContext.h"
|
|
#include "GLScreenBuffer.h"
|
|
#include "ImageBitmap.h"
|
|
#include "nsContentUtils.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
OffscreenCanvasCloneData::OffscreenCanvasCloneData(
|
|
layers::CanvasRenderer* aRenderer, uint32_t aWidth, uint32_t aHeight,
|
|
layers::LayersBackend aCompositorBackend, bool aNeutered, bool aIsWriteOnly)
|
|
: mRenderer(aRenderer),
|
|
mWidth(aWidth),
|
|
mHeight(aHeight),
|
|
mCompositorBackendType(aCompositorBackend),
|
|
mNeutered(aNeutered),
|
|
mIsWriteOnly(aIsWriteOnly) {}
|
|
|
|
OffscreenCanvasCloneData::~OffscreenCanvasCloneData() = default;
|
|
|
|
OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth,
|
|
uint32_t aHeight,
|
|
layers::LayersBackend aCompositorBackend,
|
|
layers::CanvasRenderer* aRenderer)
|
|
: DOMEventTargetHelper(aGlobal),
|
|
mAttrDirty(false),
|
|
mNeutered(false),
|
|
mIsWriteOnly(false),
|
|
mWidth(aWidth),
|
|
mHeight(aHeight),
|
|
mCompositorBackendType(aCompositorBackend),
|
|
mCanvasRenderer(aRenderer) {}
|
|
|
|
OffscreenCanvas::~OffscreenCanvas() { ClearResources(); }
|
|
|
|
JSObject* OffscreenCanvas::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return OffscreenCanvas_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<OffscreenCanvas> OffscreenCanvas::Constructor(
|
|
const GlobalObject& aGlobal, uint32_t aWidth, uint32_t aHeight) {
|
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
|
RefPtr<OffscreenCanvas> offscreenCanvas = new OffscreenCanvas(
|
|
global, aWidth, aHeight, layers::LayersBackend::LAYERS_NONE, nullptr);
|
|
return offscreenCanvas.forget();
|
|
}
|
|
|
|
void OffscreenCanvas::ClearResources() {
|
|
if (mCanvasClient) {
|
|
mCanvasClient->Clear();
|
|
|
|
MOZ_CRASH("todo");
|
|
// if (mCanvasRenderer) {
|
|
// nsCOMPtr<nsISerialEventTarget> activeTarget =
|
|
// mCanvasRenderer->GetActiveEventTarget();
|
|
// MOZ_RELEASE_ASSERT(activeTarget,
|
|
// "GFX: failed to get active event target.");
|
|
// bool current;
|
|
// activeTarget->IsOnCurrentThread(¤t);
|
|
// MOZ_RELEASE_ASSERT(current, "GFX: active thread is not current
|
|
// thread."); mCanvasRenderer->SetCanvasClient(nullptr);
|
|
// mCanvasRenderer->mContext = nullptr;
|
|
// mCanvasRenderer->mGLContext = nullptr;
|
|
// mCanvasRenderer->ResetActiveEventTarget();
|
|
//}
|
|
|
|
mCanvasClient = nullptr;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<nsISupports> OffscreenCanvas::GetContext(
|
|
JSContext* aCx, const nsAString& aContextId,
|
|
JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv) {
|
|
if (mNeutered) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
|
|
// We only support WebGL in workers for now
|
|
CanvasContextType contextType;
|
|
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!(contextType == CanvasContextType::WebGL1 ||
|
|
contextType == CanvasContextType::WebGL2 ||
|
|
contextType == CanvasContextType::WebGPU ||
|
|
contextType == CanvasContextType::ImageBitmap)) {
|
|
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<nsISupports> result = CanvasRenderingContextHelper::GetContext(
|
|
aCx, aContextId, aContextOptions, aRv);
|
|
|
|
if (!mCurrentContext) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mCanvasRenderer) {
|
|
// mCanvasRenderer->SetContextType(contextType);
|
|
if (contextType == CanvasContextType::WebGL1 ||
|
|
contextType == CanvasContextType::WebGL2) {
|
|
MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
|
|
return nullptr;
|
|
}
|
|
if (contextType == CanvasContextType::WebGPU) {
|
|
MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return result.forget();
|
|
}
|
|
|
|
layers::ImageContainer* OffscreenCanvas::GetImageContainer() {
|
|
if (!mCanvasRenderer) {
|
|
return nullptr;
|
|
}
|
|
// return mCanvasRenderer->GetImageContainer();
|
|
MOZ_CRASH("todo");
|
|
}
|
|
|
|
already_AddRefed<nsICanvasRenderingContextInternal>
|
|
OffscreenCanvas::CreateContext(CanvasContextType aContextType) {
|
|
RefPtr<nsICanvasRenderingContextInternal> ret =
|
|
CanvasRenderingContextHelper::CreateContext(aContextType);
|
|
|
|
ret->SetOffscreenCanvas(this);
|
|
return ret.forget();
|
|
}
|
|
|
|
void OffscreenCanvas::CommitFrameToCompositor() {
|
|
if (!mCanvasRenderer) {
|
|
// This offscreen canvas doesn't associate to any HTML canvas element.
|
|
// So, just bail out.
|
|
return;
|
|
}
|
|
MOZ_CRASH("todo");
|
|
|
|
// The attributes has changed, we have to notify main
|
|
// thread to change canvas size.
|
|
if (mAttrDirty) {
|
|
MOZ_CRASH("todo");
|
|
// if (mCanvasRenderer) {
|
|
// mCanvasRenderer->SetWidth(mWidth);
|
|
// mCanvasRenderer->SetHeight(mHeight);
|
|
// mCanvasRenderer->NotifyElementAboutAttributesChanged();
|
|
//}
|
|
mAttrDirty = false;
|
|
}
|
|
|
|
// CanvasContextType contentType = mCanvasRenderer->GetContextType();
|
|
// if (mCurrentContext && (contentType == CanvasContextType::WebGL1 ||
|
|
// contentType == CanvasContextType::WebGL2)) {
|
|
// MOZ_ASSERT_UNREACHABLE("WebGL OffscreenCanvas not yet supported.");
|
|
// return;
|
|
//}
|
|
// if (mCurrentContext && (contentType == CanvasContextType::WebGPU)) {
|
|
// MOZ_ASSERT_UNREACHABLE("WebGPU OffscreenCanvas not yet supported.");
|
|
// return;
|
|
//}
|
|
|
|
// if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
|
|
// mCanvasRenderer->NotifyElementAboutInvalidation();
|
|
// ImageBridgeChild::GetSingleton()->UpdateAsyncCanvasRenderer(
|
|
// mCanvasRenderer);
|
|
//}
|
|
}
|
|
|
|
OffscreenCanvasCloneData* OffscreenCanvas::ToCloneData() {
|
|
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
|
|
mCompositorBackendType, mNeutered,
|
|
mIsWriteOnly);
|
|
}
|
|
|
|
already_AddRefed<ImageBitmap> OffscreenCanvas::TransferToImageBitmap(
|
|
ErrorResult& aRv) {
|
|
RefPtr<ImageBitmap> result =
|
|
ImageBitmap::CreateFromOffscreenCanvas(GetOwnerGlobal(), *this, aRv);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// TODO: Clear the content?
|
|
return result.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> OffscreenCanvas::ToBlob(JSContext* aCx,
|
|
const nsAString& aType,
|
|
JS::Handle<JS::Value> aParams,
|
|
ErrorResult& aRv) {
|
|
// do a trust check if this is a write-only canvas
|
|
if (mIsWriteOnly) {
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
|
|
|
|
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Encoder callback when encoding is complete.
|
|
class EncodeCallback : public EncodeCompleteCallback {
|
|
public:
|
|
EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
|
|
: mGlobal(aGlobal), mPromise(aPromise) {}
|
|
|
|
// This is called on main thread.
|
|
nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
|
|
RefPtr<BlobImpl> blobImpl = aBlobImpl;
|
|
|
|
if (mPromise) {
|
|
RefPtr<Blob> blob = Blob::Create(mGlobal, blobImpl);
|
|
if (NS_WARN_IF(!blob)) {
|
|
mPromise->MaybeReject(NS_ERROR_FAILURE);
|
|
} else {
|
|
mPromise->MaybeResolve(blob);
|
|
}
|
|
}
|
|
|
|
mGlobal = nullptr;
|
|
mPromise = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIGlobalObject> mGlobal;
|
|
RefPtr<Promise> mPromise;
|
|
};
|
|
|
|
RefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, promise);
|
|
|
|
bool usePlaceholder = ShouldResistFingerprinting();
|
|
CanvasRenderingContextHelper::ToBlob(aCx, global, callback, aType, aParams,
|
|
usePlaceholder, aRv);
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<gfx::SourceSurface> OffscreenCanvas::GetSurfaceSnapshot(
|
|
gfxAlphaType* const aOutAlphaType) {
|
|
if (!mCurrentContext) {
|
|
return nullptr;
|
|
}
|
|
|
|
return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
|
|
}
|
|
|
|
bool OffscreenCanvas::ShouldResistFingerprinting() const {
|
|
return nsContentUtils::ShouldResistFingerprinting(GetOwnerGlobal());
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<OffscreenCanvas> OffscreenCanvas::CreateFromCloneData(
|
|
nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData) {
|
|
MOZ_ASSERT(aData);
|
|
RefPtr<OffscreenCanvas> wc =
|
|
new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
|
|
aData->mCompositorBackendType, aData->mRenderer);
|
|
if (aData->mNeutered) {
|
|
wc->SetNeutered();
|
|
}
|
|
return wc.forget();
|
|
}
|
|
|
|
/* static */
|
|
bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx,
|
|
JSObject* aObj) {
|
|
if (NS_IsMainThread()) {
|
|
return true;
|
|
}
|
|
|
|
return StaticPrefs::gfx_offscreencanvas_enabled();
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper,
|
|
mCurrentContext)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvas)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
} // namespace mozilla::dom
|