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-02 16:41:26 +00:00
Родитель 94c44e84c9
Коммит 48f822060f
14 изменённых файлов: 282 добавлений и 19 удалений

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,117 @@
/* -*- 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/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_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();
}
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();
}
} // namespace mozilla::dom

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

@ -0,0 +1,60 @@
/* -*- 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_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;
private:
void AddShutdownObserver() override;
void RemoveShutdownObserver() override;
bool AlreadyShutDown() const override {
return !mOffscreenShutdownObserver &&
CanvasRenderingContext2D::AlreadyShutDown();
}
~OffscreenCanvasRenderingContext2D() override;
RefPtr<OffscreenCanvasShutdownObserver> mOffscreenShutdownObserver;
RefPtr<WeakWorkerRef> mWorkerRef;
};
} // namespace mozilla::dom
#endif // MOZILLA_DOM_OFFSCREENCANVASRENDERINGCONTEXT2D_H_

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

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

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

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

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

@ -7,14 +7,14 @@
* https://html.spec.whatwg.org/#the-offscreencanvas-interface * 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 { dictionary ImageEncodeOptions {
DOMString type = "image/png"; DOMString type = "image/png";
unrestricted double quality; unrestricted double quality;
}; };
enum OffscreenRenderingContextId { /* "2d", */ "bitmaprenderer", "webgl", "webgl2", "webgpu" }; enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2", "webgpu" };
[Exposed=(Window,Worker), [Exposed=(Window,Worker),
Func="CanvasUtils::IsOffscreenCanvasEnabled"] 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"): with Files("OffscreenCanvas.webidl"):
BUG_COMPONENT = ("Core", "Canvas: 2D") BUG_COMPONENT = ("Core", "Canvas: 2D")
with Files("OffscreenCanvasRenderingContext2D.webidl"):
BUG_COMPONENT = ("Core", "Canvas: 2D")
with Files("OscillatorNode.webidl"): with Files("OscillatorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio") BUG_COMPONENT = ("Core", "Web Audio")
@ -752,6 +755,7 @@ WEBIDL_FILES = [
"OfflineAudioContext.webidl", "OfflineAudioContext.webidl",
"OfflineResourceList.webidl", "OfflineResourceList.webidl",
"OffscreenCanvas.webidl", "OffscreenCanvas.webidl",
"OffscreenCanvasRenderingContext2D.webidl",
"OscillatorNode.webidl", "OscillatorNode.webidl",
"PaintRequest.webidl", "PaintRequest.webidl",
"PaintRequestList.webidl", "PaintRequestList.webidl",

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

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