Bug 1746750 - Part 3. Implement partial OffscreenCanvasRenderingContext2D. r=lsalzman,webidl,smaug

This patch adds a partial OffscreenCanvasRenderingContext2D
implementation. It is missing anything text and UI related, including
CanvasFilters, CanvasUserInterface, CanvasText, CanvasTextDrawingStyles,
and CanvasHitRegions.

Differential Revision: https://phabricator.services.mozilla.com/D135354
This commit is contained in:
Andrew Osmond 2022-02-03 20:00:25 +00:00
Родитель 4d8e11ca77
Коммит 193a13db35
14 изменённых файлов: 349 добавлений и 19 удалений

Просмотреть файл

@ -609,6 +609,12 @@ DOMInterfaces = {
'nativeType': 'nsDOMOfflineResourceList',
},
'OffscreenCanvasRenderingContext2D': {
'implicitJSContext': [
'createImageData', 'getImageData', 'isPointInPath', 'isPointInStroke'
],
},
'PaintRequestList': {
'headerFile': 'mozilla/dom/PaintRequest.h',
},

Просмотреть файл

@ -1140,14 +1140,14 @@ nsresult CanvasRenderingContext2D::Redraw() {
mIsEntireFrameInvalid = true;
if (!mCanvasElement) {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
return NS_OK;
}
if (mCanvasElement) {
SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
mCanvasElement->InvalidateCanvasContent(nullptr);
} else if (mOffscreenCanvas) {
mOffscreenCanvas->QueueCommitToCompositor();
} else {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
}
return NS_OK;
}
@ -1166,14 +1166,14 @@ void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) {
return;
}
if (!mCanvasElement) {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
return;
}
if (mCanvasElement) {
SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
mCanvasElement->InvalidateCanvasContent(&aR);
} else if (mOffscreenCanvas) {
mOffscreenCanvas->QueueCommitToCompositor();
} else {
NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
}
}
void CanvasRenderingContext2D::DidRefresh() {}
@ -1573,6 +1573,14 @@ void CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) {
AddAssociatedMemory();
}
if (mOffscreenCanvas) {
OffscreenCanvasDisplayData data;
data.mSize = {mWidth, mHeight};
data.mIsOpaque = mOpaque;
data.mIsAlphaPremult = true;
mOffscreenCanvas->UpdateDisplayData(data);
}
if (!mCanvasElement || !mCanvasElement->IsInComposedDoc()) {
return;
}

Просмотреть файл

@ -9,6 +9,7 @@
#include "ImageEncoder.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/OffscreenCanvasRenderingContext2D.h"
#include "mozilla/GfxMessageUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
@ -137,6 +138,11 @@ CanvasRenderingContextHelper::CreateContextHelper(
ret = new CanvasRenderingContext2D(aCompositorBackend);
break;
case CanvasContextType::OffscreenCanvas2D:
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
ret = new OffscreenCanvasRenderingContext2D(aCompositorBackend);
break;
case CanvasContextType::WebGL1:
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);

Просмотреть файл

@ -25,6 +25,7 @@ class EncodeCompleteCallback;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
OffscreenCanvas2D,
WebGL1,
WebGL2,
WebGPU,

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/OffscreenCanvasBinding.h"
#include "mozilla/dom/OffscreenCanvasDisplayHelper.h"
#include "mozilla/dom/OffscreenCanvasRenderingContext2D.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
@ -118,6 +119,9 @@ void OffscreenCanvas::GetContext(
CanvasContextType contextType;
switch (aContextId) {
case OffscreenRenderingContextId::_2d:
contextType = CanvasContextType::OffscreenCanvas2D;
break;
case OffscreenRenderingContextId::Bitmaprenderer:
contextType = CanvasContextType::ImageBitmap;
break;
@ -148,6 +152,11 @@ void OffscreenCanvas::GetContext(
MOZ_ASSERT(mCurrentContext);
switch (mCurrentContextType) {
case CanvasContextType::OffscreenCanvas2D:
aResult.SetValue().SetAsOffscreenCanvasRenderingContext2D() =
*static_cast<OffscreenCanvasRenderingContext2D*>(
mCurrentContext.get());
break;
case CanvasContextType::ImageBitmap:
aResult.SetValue().SetAsImageBitmapRenderingContext() =
*static_cast<ImageBitmapRenderingContext*>(mCurrentContext.get());
@ -305,6 +314,12 @@ already_AddRefed<Promise> OffscreenCanvas::ConvertToBlob(
return nullptr;
}
if (mNeutered) {
aRv.ThrowInvalidStateError(
"Cannot get blob from placeholder canvas transferred to worker.");
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
RefPtr<Promise> promise = Promise::Create(global, aRv);
@ -327,6 +342,9 @@ already_AddRefed<Promise> OffscreenCanvas::ConvertToBlob(
CanvasRenderingContextHelper::ToBlob(callback, type, encodeOptions,
/* aUsingCustomOptions */ false,
usePlaceholder, aRv);
if (aRv.Failed()) {
promise->MaybeReject(std::move(aRv));
}
return promise.forget();
}
@ -341,6 +359,12 @@ already_AddRefed<Promise> OffscreenCanvas::ToBlob(JSContext* aCx,
return nullptr;
}
if (mNeutered) {
aRv.ThrowInvalidStateError(
"Cannot get blob from placeholder canvas transferred to worker.");
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
RefPtr<Promise> promise = Promise::Create(global, aRv);

Просмотреть файл

@ -36,7 +36,7 @@ class ImageBitmap;
struct ImageEncodeOptions;
using OwningOffscreenRenderingContext = class
OwningImageBitmapRenderingContextOrWebGLRenderingContextOrWebGL2RenderingContextOrGPUCanvasContext;
OwningOffscreenCanvasRenderingContext2DOrImageBitmapRenderingContextOrWebGLRenderingContextOrWebGL2RenderingContextOrGPUCanvasContext;
// This is helper class for transferring OffscreenCanvas to worker thread.
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
@ -151,6 +151,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
bool ShouldResistFingerprinting() const;
bool IsTransferredFromElement() const { return !!mDisplay; }
private:
~OffscreenCanvas();

Просмотреть файл

@ -0,0 +1,175 @@
/* -*- 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 "OffscreenCanvasRenderingContext2D.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/OffscreenCanvasRenderingContext2DBinding.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerRef.h"
namespace mozilla::dom {
class OffscreenCanvasShutdownObserver final {
NS_INLINE_DECL_REFCOUNTING(OffscreenCanvasShutdownObserver)
public:
explicit OffscreenCanvasShutdownObserver(
OffscreenCanvasRenderingContext2D* aOwner)
: mOwner(aOwner) {}
void OnShutdown() {
if (mOwner) {
mOwner->OnShutdown();
mOwner = nullptr;
}
}
void ClearOwner() { mOwner = nullptr; }
private:
~OffscreenCanvasShutdownObserver() = default;
OffscreenCanvasRenderingContext2D* mOwner;
};
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(
OffscreenCanvasRenderingContext2D, CanvasRenderingContext2D)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OffscreenCanvasRenderingContext2D)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(OffscreenCanvasRenderingContext2D)
return tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(OffscreenCanvasRenderingContext2D)
return tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(OffscreenCanvasRenderingContext2D)
return tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMPL_ADDREF_INHERITED(OffscreenCanvasRenderingContext2D,
CanvasRenderingContext2D)
NS_IMPL_RELEASE_INHERITED(OffscreenCanvasRenderingContext2D,
CanvasRenderingContext2D)
OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D(
layers::LayersBackend aCompositorBackend)
: CanvasRenderingContext2D(aCompositorBackend) {}
OffscreenCanvasRenderingContext2D::~OffscreenCanvasRenderingContext2D() =
default;
JSObject* OffscreenCanvasRenderingContext2D::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return OffscreenCanvasRenderingContext2D_Binding::Wrap(aCx, this,
aGivenProto);
}
nsIGlobalObject* OffscreenCanvasRenderingContext2D::GetParentObject() const {
return mOffscreenCanvas->GetOwnerGlobal();
}
NS_IMETHODIMP OffscreenCanvasRenderingContext2D::InitializeWithDrawTarget(
nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) {
return NS_ERROR_NOT_IMPLEMENTED;
}
void OffscreenCanvasRenderingContext2D::AddShutdownObserver() {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
if (!workerPrivate) {
// We may be using OffscreenCanvas on the main thread.
CanvasRenderingContext2D::AddShutdownObserver();
return;
}
mOffscreenShutdownObserver =
MakeAndAddRef<OffscreenCanvasShutdownObserver>(this);
mWorkerRef = WeakWorkerRef::Create(
workerPrivate,
[observer = mOffscreenShutdownObserver] { observer->OnShutdown(); });
}
void OffscreenCanvasRenderingContext2D::RemoveShutdownObserver() {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
if (!workerPrivate) {
// We may be using OffscreenCanvas on the main thread.
CanvasRenderingContext2D::RemoveShutdownObserver();
return;
}
if (mOffscreenShutdownObserver) {
mOffscreenShutdownObserver->ClearOwner();
}
mOffscreenShutdownObserver = nullptr;
mWorkerRef = nullptr;
}
void OffscreenCanvasRenderingContext2D::OnShutdown() {
if (mOffscreenShutdownObserver) {
mOffscreenShutdownObserver->ClearOwner();
mOffscreenShutdownObserver = nullptr;
}
CanvasRenderingContext2D::OnShutdown();
}
void OffscreenCanvasRenderingContext2D::Commit(ErrorResult& aRv) {
if (!mOffscreenCanvas->IsTransferredFromElement()) {
aRv.ThrowInvalidStateError(
"Cannot commit on an OffscreenCanvas that is not transferred from an "
"HTMLCanvasElement.");
return;
}
mOffscreenCanvas->CommitFrameToCompositor();
}
void OffscreenCanvasRenderingContext2D::AddZoneWaitingForGC() {
JSObject* wrapper = GetWrapperPreserveColor();
if (wrapper) {
CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(
JS::GetObjectZone(wrapper));
}
}
void OffscreenCanvasRenderingContext2D::AddAssociatedMemory() {
JSObject* wrapper = GetWrapperMaybeDead();
if (wrapper) {
JS::AddAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
JS::MemoryUse::DOMBinding);
}
}
void OffscreenCanvasRenderingContext2D::RemoveAssociatedMemory() {
JSObject* wrapper = GetWrapperMaybeDead();
if (wrapper) {
JS::RemoveAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
JS::MemoryUse::DOMBinding);
}
}
size_t BindingJSObjectMallocBytes(OffscreenCanvasRenderingContext2D* aContext) {
gfx::IntSize size = aContext->GetSize();
// TODO: Bug 1552137: No memory will be allocated if either dimension is
// greater than gfxPrefs::gfx_canvas_max_size(). We should check this here
// too.
CheckedInt<uint32_t> bytes =
CheckedInt<uint32_t>(size.width) * size.height * 4;
if (!bytes.isValid()) {
return 0;
}
return bytes.value();
}
} // namespace mozilla::dom

Просмотреть файл

@ -0,0 +1,69 @@
/* -*- 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/. */
#ifndef MOZILLA_DOM_OFFSCREENCANVASRENDERINGCONTEXT2D_H_
#define MOZILLA_DOM_OFFSCREENCANVASRENDERINGCONTEXT2D_H_
#include "mozilla/RefPtr.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
struct JSContext;
class nsIGlobalObject;
namespace mozilla::dom {
class OffscreenCanvas;
class OffscreenCanvasShutdownObserver;
class WeakWorkerRef;
class OffscreenCanvasRenderingContext2D final
: public CanvasRenderingContext2D {
public:
// nsISupports interface + CC
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(
OffscreenCanvasRenderingContext2D, CanvasRenderingContext2D)
explicit OffscreenCanvasRenderingContext2D(
layers::LayersBackend aCompositorBackend);
nsIGlobalObject* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
OffscreenCanvas* Canvas() { return mOffscreenCanvas; }
const OffscreenCanvas* Canvas() const { return mOffscreenCanvas; }
void Commit(ErrorResult& aRv);
void OnShutdown() override;
NS_IMETHOD InitializeWithDrawTarget(
nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) override;
private:
void AddShutdownObserver() override;
void RemoveShutdownObserver() override;
bool AlreadyShutDown() const override {
return !mOffscreenShutdownObserver &&
CanvasRenderingContext2D::AlreadyShutDown();
}
void AddZoneWaitingForGC() override;
void AddAssociatedMemory() override;
void RemoveAssociatedMemory() override;
~OffscreenCanvasRenderingContext2D() override;
RefPtr<OffscreenCanvasShutdownObserver> mOffscreenShutdownObserver;
RefPtr<WeakWorkerRef> mWorkerRef;
};
size_t BindingJSObjectMallocBytes(OffscreenCanvasRenderingContext2D* aContext);
} // namespace mozilla::dom
#endif // MOZILLA_DOM_OFFSCREENCANVASRENDERINGCONTEXT2D_H_

Просмотреть файл

@ -61,6 +61,7 @@ EXPORTS.mozilla.dom += [
"ImageUtils.h",
"OffscreenCanvas.h",
"OffscreenCanvasDisplayHelper.h",
"OffscreenCanvasRenderingContext2D.h",
"QueueParamTraits.h",
"TextMetrics.h",
"WebGLChild.h",
@ -101,6 +102,7 @@ SOURCES += [
"DrawTargetWebgl.cpp", # Isolate Skia
"ImageUtils.cpp",
"OffscreenCanvasDisplayHelper.cpp", # See bug 1745384
"OffscreenCanvasRenderingContext2D.cpp", # See bug 1745384
]
# WebGL Sources

Просмотреть файл

@ -338,7 +338,8 @@ interface mixin CanvasHitRegions {
[Pref="canvas.hitregions.enabled"] void clearHitRegions();
};
[Exposed=Window]
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface CanvasGradient {
// opaque object
[Throws]
@ -346,7 +347,8 @@ interface CanvasGradient {
void addColorStop(float offset, UTF8String color);
};
[Exposed=Window]
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface CanvasPattern {
// opaque object
// [Throws, LenientFloat] - could not do this overload because of bug 1020975
@ -397,7 +399,8 @@ interface TextMetrics {
};
[Pref="canvas.path.enabled",
Exposed=Window]
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread",
Exposed=(Window,Worker)]
interface Path2D
{
constructor();

Просмотреть файл

@ -7,14 +7,14 @@
* https://html.spec.whatwg.org/#the-offscreencanvas-interface
*/
typedef (ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) OffscreenRenderingContext;
typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) OffscreenRenderingContext;
dictionary ImageEncodeOptions {
DOMString type = "image/png";
unrestricted double quality;
};
enum OffscreenRenderingContextId { /* "2d", */ "bitmaprenderer", "webgl", "webgl2", "webgpu" };
enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2", "webgpu" };
[Exposed=(Window,Worker),
Func="CanvasUtils::IsOffscreenCanvasEnabled"]

Просмотреть файл

@ -0,0 +1,30 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* For more information on this interface, please see
* https://html.spec.whatwg.org/#the-offscreen-2d-rendering-context
*/
[Exposed=(Window,Worker),
Func="CanvasUtils::IsOffscreenCanvasEnabled"]
interface OffscreenCanvasRenderingContext2D {
[Throws]
void commit();
readonly attribute OffscreenCanvas canvas;
};
OffscreenCanvasRenderingContext2D includes CanvasState;
OffscreenCanvasRenderingContext2D includes CanvasTransform;
OffscreenCanvasRenderingContext2D includes CanvasCompositing;
OffscreenCanvasRenderingContext2D includes CanvasImageSmoothing;
OffscreenCanvasRenderingContext2D includes CanvasFillStrokeStyles;
OffscreenCanvasRenderingContext2D includes CanvasShadowStyles;
OffscreenCanvasRenderingContext2D includes CanvasRect;
OffscreenCanvasRenderingContext2D includes CanvasDrawPath;
OffscreenCanvasRenderingContext2D includes CanvasDrawImage;
OffscreenCanvasRenderingContext2D includes CanvasImageData;
OffscreenCanvasRenderingContext2D includes CanvasPathDrawingStyles;
OffscreenCanvasRenderingContext2D includes CanvasPathMethods;

Просмотреть файл

@ -241,6 +241,9 @@ with Files("OfflineAudio*"):
with Files("OffscreenCanvas.webidl"):
BUG_COMPONENT = ("Core", "Canvas: 2D")
with Files("OffscreenCanvasRenderingContext2D.webidl"):
BUG_COMPONENT = ("Core", "Canvas: 2D")
with Files("OscillatorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
@ -752,6 +755,7 @@ WEBIDL_FILES = [
"OfflineAudioContext.webidl",
"OfflineResourceList.webidl",
"OffscreenCanvas.webidl",
"OffscreenCanvasRenderingContext2D.webidl",
"OscillatorNode.webidl",
"PaintRequest.webidl",
"PaintRequestList.webidl",

Просмотреть файл

@ -1525,7 +1525,7 @@
# Add support for canvas path objects
- name: canvas.path.enabled
type: bool
type: RelaxedAtomicBool
value: true
mirror: always