зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1172796 - Part 4: Implement OffscreenCanvas::ToBlob. r=roc r=smaug
--HG-- extra : commitid : 6RcUmz8ar4L
This commit is contained in:
Родитель
abfddd01cc
Коммит
4493d55b04
|
@ -1073,7 +1073,8 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
|||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
|
||||
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
|
||||
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
|
||||
delete data;
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
|
|
@ -26,36 +26,6 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
|
|||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsAutoString type;
|
||||
nsContentUtils::ASCIIToLower(aType, type);
|
||||
|
||||
nsAutoString params;
|
||||
bool usingCustomParseOptions;
|
||||
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
// We disallow canvases of width or height zero, and set them to 1, so
|
||||
// we will have a discrepancy with the sizes of the canvas and the context.
|
||||
// That discrepancy is OK, the rest are not.
|
||||
nsIntSize elementSize = GetWidthHeight();
|
||||
if ((elementSize.width != mCurrentContext->GetWidth() &&
|
||||
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
|
||||
(elementSize.height != mCurrentContext->GetHeight() &&
|
||||
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[]> imageBuffer;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
imageBuffer = mCurrentContext->GetImageBuffer(&format);
|
||||
}
|
||||
|
||||
// Encoder callback when encoding is complete.
|
||||
class EncodeCallback : public EncodeCompleteCallback
|
||||
{
|
||||
|
@ -97,6 +67,49 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
|
|||
RefPtr<EncodeCompleteCallback> callback =
|
||||
new EncodeCallback(aGlobal, &aCallback);
|
||||
|
||||
ToBlob(aCx, aGlobal, callback, aType, aParams, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
|
||||
nsIGlobalObject* aGlobal,
|
||||
EncodeCompleteCallback* aCallback,
|
||||
const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsAutoString type;
|
||||
nsContentUtils::ASCIIToLower(aType, type);
|
||||
|
||||
nsAutoString params;
|
||||
bool usingCustomParseOptions;
|
||||
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
// We disallow canvases of width or height zero, and set them to 1, so
|
||||
// we will have a discrepancy with the sizes of the canvas and the context.
|
||||
// That discrepancy is OK, the rest are not.
|
||||
nsIntSize elementSize = GetWidthHeight();
|
||||
if ((elementSize.width != mCurrentContext->GetWidth() &&
|
||||
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
|
||||
(elementSize.height != mCurrentContext->GetHeight() &&
|
||||
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[]> imageBuffer;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
imageBuffer = mCurrentContext->GetImageBuffer(&format);
|
||||
}
|
||||
|
||||
RefPtr<EncodeCompleteCallback> callback = aCallback;
|
||||
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
|
|
|
@ -18,6 +18,7 @@ class ErrorResult;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class EncodeCompleteCallback;
|
||||
class FileCallback;
|
||||
|
||||
enum class CanvasContextType : uint8_t {
|
||||
|
@ -57,6 +58,10 @@ protected:
|
|||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback,
|
||||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType);
|
||||
|
||||
|
|
|
@ -38,11 +38,13 @@ OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
|
|||
{
|
||||
}
|
||||
|
||||
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
|
||||
OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal,
|
||||
uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer)
|
||||
: mAttrDirty(false)
|
||||
: DOMEventTargetHelper(aGlobal)
|
||||
, mAttrDirty(false)
|
||||
, mNeutered(false)
|
||||
, mIsWriteOnly(false)
|
||||
, mWidth(aWidth)
|
||||
|
@ -57,12 +59,6 @@ OffscreenCanvas::~OffscreenCanvas()
|
|||
ClearResources();
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
OffscreenCanvas::GetParentObject() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
OffscreenCanvas::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
|
@ -76,8 +72,9 @@ OffscreenCanvas::Constructor(const GlobalObject& aGlobal,
|
|||
uint32_t aHeight,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<OffscreenCanvas> offscreenCanvas =
|
||||
new OffscreenCanvas(aWidth, aHeight,
|
||||
new OffscreenCanvas(global, aWidth, aHeight,
|
||||
layers::LayersBackend::LAYERS_NONE, nullptr);
|
||||
return offscreenCanvas.forget();
|
||||
}
|
||||
|
@ -93,7 +90,9 @@ OffscreenCanvas::ClearResources()
|
|||
if (mCanvasRenderer) {
|
||||
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
|
||||
MOZ_RELEASE_ASSERT(activeThread);
|
||||
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
|
||||
bool current;
|
||||
activeThread->IsOnCurrentThread(¤t);
|
||||
MOZ_RELEASE_ASSERT(current);
|
||||
mCanvasRenderer->SetCanvasClient(nullptr);
|
||||
mCanvasRenderer->mContext = nullptr;
|
||||
mCanvasRenderer->mGLContext = nullptr;
|
||||
|
@ -214,12 +213,92 @@ OffscreenCanvas::ToCloneData()
|
|||
mCompositorBackendType, mNeutered, mIsWriteOnly);
|
||||
}
|
||||
|
||||
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 = GetGlobalObject();
|
||||
|
||||
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 ReceiveBlob(already_AddRefed<Blob> aBlob)
|
||||
{
|
||||
RefPtr<Blob> blob = aBlob;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t size = blob->GetSize(rv);
|
||||
if (rv.Failed()) {
|
||||
rv.SuppressException();
|
||||
} else {
|
||||
AutoJSAPI jsapi;
|
||||
if (jsapi.Init(mGlobal)) {
|
||||
JS_updateMallocCounter(jsapi.cx(), size);
|
||||
}
|
||||
}
|
||||
|
||||
if (mPromise) {
|
||||
RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
|
||||
mPromise->MaybeResolve(newBlob);
|
||||
}
|
||||
|
||||
mGlobal = nullptr;
|
||||
mPromise = nullptr;
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
RefPtr<EncodeCompleteCallback> callback =
|
||||
new EncodeCallback(global, promise);
|
||||
|
||||
CanvasRenderingContextHelper::ToBlob(aCx, global,
|
||||
callback, aType, aParams, aRv);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
||||
nsCOMPtr<nsIGlobalObject>
|
||||
OffscreenCanvas::GetGlobalObject()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return GetParentObject();
|
||||
}
|
||||
|
||||
dom::workers::WorkerPrivate* workerPrivate =
|
||||
dom::workers::GetCurrentThreadWorkerPrivate();
|
||||
return workerPrivate->GlobalScope();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<OffscreenCanvas>
|
||||
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
|
||||
OffscreenCanvas::CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
RefPtr<OffscreenCanvas> wc =
|
||||
new OffscreenCanvas(aData->mWidth, aData->mHeight,
|
||||
new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
|
||||
aData->mCompositorBackendType, aData->mRenderer);
|
||||
if (aData->mNeutered) {
|
||||
wc->SetNeutered();
|
||||
|
|
|
@ -25,6 +25,7 @@ class CanvasClient;
|
|||
} // namespace layers
|
||||
|
||||
namespace dom {
|
||||
class Blob;
|
||||
|
||||
// This is helper class for transferring OffscreenCanvas to worker thread.
|
||||
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
|
||||
|
@ -53,12 +54,13 @@ public:
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
|
||||
OffscreenCanvas(uint32_t aWidth,
|
||||
OffscreenCanvas(nsIGlobalObject* aGlobal,
|
||||
uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer);
|
||||
|
||||
OffscreenCanvas* GetParentObject() const;
|
||||
nsCOMPtr<nsIGlobalObject> GetParentObject() const { return GetOwnerGlobal(); }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -107,13 +109,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ToBlob(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsICanvasRenderingContextInternal* GetContext() const
|
||||
{
|
||||
return mCurrentContext;
|
||||
}
|
||||
|
||||
static already_AddRefed<OffscreenCanvas>
|
||||
CreateFromCloneData(OffscreenCanvasCloneData* aData);
|
||||
CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
|
||||
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
|
@ -172,6 +180,8 @@ public:
|
|||
private:
|
||||
~OffscreenCanvas();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> GetGlobalObject();
|
||||
|
||||
void CanvasAttrChanged()
|
||||
{
|
||||
mAttrDirty = true;
|
||||
|
|
|
@ -268,6 +268,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
|
|||
[test_createPattern_broken.html]
|
||||
[test_setlinedash.html]
|
||||
[test_filter.html]
|
||||
[test_offscreencanvas_toblob.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_basic_webgl.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_dynamic_fallback.html]
|
||||
|
|
|
@ -1,28 +1,42 @@
|
|||
/* WebWorker for test_offscreencanvas_*.html */
|
||||
(function(){
|
||||
|
||||
var port = null;
|
||||
|
||||
function ok(expect, msg) {
|
||||
if (port) {
|
||||
port.postMessage({type: "test", result: !!expect, name: msg});
|
||||
} else {
|
||||
postMessage({type: "test", result: !!expect, name: msg});
|
||||
function isInWorker() {
|
||||
try {
|
||||
return !(self instanceof Window);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function postMessageGeneral(data) {
|
||||
if (isInWorker()) {
|
||||
if (port) {
|
||||
port.postMessage(data);
|
||||
} else {
|
||||
postMessage(data);
|
||||
}
|
||||
} else {
|
||||
postMessage(data, "*");
|
||||
}
|
||||
}
|
||||
|
||||
function ok(expect, msg) {
|
||||
postMessageGeneral({type: "test", result: !!expect, name: msg});
|
||||
}
|
||||
|
||||
function finish() {
|
||||
if (port) {
|
||||
port.postMessage({type: "finish"});
|
||||
} else {
|
||||
postMessage({type: "finish"});
|
||||
}
|
||||
postMessageGeneral({type: "finish"});
|
||||
}
|
||||
|
||||
function drawCount(count) {
|
||||
if (port) {
|
||||
port.postMessage({type: "draw", count: count});
|
||||
} else {
|
||||
postMessage({type: "draw", count: count});
|
||||
}
|
||||
postMessageGeneral({type: "draw", count: count});
|
||||
}
|
||||
|
||||
function sendBlob(blob) {
|
||||
postMessageGeneral({type: "blob", blob: blob});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -140,7 +154,7 @@ function createDrawFunc(canvas) {
|
|||
// Start drawing
|
||||
checkGLError('after setup');
|
||||
|
||||
return function(prefix) {
|
||||
return function(prefix, needCommitFrame) {
|
||||
if (prefix) {
|
||||
prefix = "[" + prefix + "] ";
|
||||
} else {
|
||||
|
@ -152,7 +166,9 @@ function createDrawFunc(canvas) {
|
|||
preDraw(prefix);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
postDraw(prefix);
|
||||
gl.commit();
|
||||
if (needCommitFrame) {
|
||||
gl.commit();
|
||||
}
|
||||
checkGLError(prefix);
|
||||
};
|
||||
}
|
||||
|
@ -161,6 +177,9 @@ function createDrawFunc(canvas) {
|
|||
function entryFunction(testStr, subtests, offscreenCanvas) {
|
||||
var test = testStr;
|
||||
var canvas = offscreenCanvas;
|
||||
if (test == "webgl_imagebitmap") {
|
||||
canvas = new OffscreenCanvas(64, 64);
|
||||
}
|
||||
|
||||
if (test != "subworker") {
|
||||
ok(canvas, "Canvas successfully transfered to worker");
|
||||
|
@ -190,7 +209,7 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
|
|||
finish();
|
||||
return;
|
||||
}
|
||||
draw("loop " +count);
|
||||
draw("loop " +count, true);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
|
@ -205,11 +224,25 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
|
|||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
++count;
|
||||
draw("loop " + count);
|
||||
draw("loop " + count, true);
|
||||
drawCount(count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Test toBlob
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_toblob") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
draw("", false);
|
||||
canvas.toBlob().then(function(blob) {
|
||||
sendBlob(blob);
|
||||
});
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Canvas Size Change from Worker
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_changesize") {
|
||||
|
@ -219,22 +252,22 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
|
|||
return;
|
||||
}
|
||||
|
||||
draw("64x64");
|
||||
draw("64x64", true);
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 128;
|
||||
canvas.height = 128;
|
||||
draw("Increased to 128x128");
|
||||
draw("Increased to 128x128", true);
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 32;
|
||||
canvas.width = 32;
|
||||
draw("Decreased to 32x32");
|
||||
draw("Decreased to 32x32", true);
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
draw("Increased to 64x64");
|
||||
draw("Increased to 64x64", true);
|
||||
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
|
@ -297,3 +330,9 @@ onconnect = function(evt) {
|
|||
|
||||
port.start();
|
||||
};
|
||||
|
||||
if (!isInWorker()) {
|
||||
window.entryFunction = entryFunction;
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="offscreencanvas.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<canvas id="c-mt" width="64" height="64"></canvas>
|
||||
<canvas id="c-ref" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testBlob(blob, callback) {
|
||||
// testing toBlob
|
||||
// Fill c-ref with green color.
|
||||
var c = document.getElementById("c-ref");
|
||||
var ctx = c.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
ok(c.toDataURL() == e.target.result, "toBlob should return a 64x64 green square");
|
||||
callback();
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
|
||||
function runTestOnMainThread() {
|
||||
var htmlCanvas = document.getElementById("c-mt");
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
|
||||
window.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "blob") {
|
||||
testBlob(msg.blob, SimpleTest.finish);
|
||||
}
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
entryFunction('webgl_toblob', '', offscreenCanvas);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "blob") {
|
||||
testBlob(msg.blob, function() {
|
||||
worker.terminate();
|
||||
runTestOnMainThread();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
worker.postMessage({test: 'webgl_toblob', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -374,7 +374,8 @@ HTMLCanvasElement::~HTMLCanvasElement()
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
|
||||
mCurrentContext, mPrintCallback,
|
||||
mPrintState, mOriginalCanvas)
|
||||
mPrintState, mOriginalCanvas,
|
||||
mOffscreenCanvas)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
|
||||
|
@ -779,7 +780,10 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
|
|||
renderer->SetWidth(sz.width);
|
||||
renderer->SetHeight(sz.height);
|
||||
|
||||
mOffscreenCanvas = new OffscreenCanvas(sz.width,
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
mOffscreenCanvas = new OffscreenCanvas(global,
|
||||
sz.width,
|
||||
sz.height,
|
||||
GetCompositorBackendType(),
|
||||
renderer);
|
||||
|
|
|
@ -19,6 +19,9 @@ interface OffscreenCanvas : EventTarget {
|
|||
[Throws]
|
||||
nsISupports? getContext(DOMString contextId,
|
||||
optional any contextOptions = null);
|
||||
[Throws]
|
||||
Promise<Blob> toBlob(optional DOMString type = "",
|
||||
optional any encoderOptions);
|
||||
};
|
||||
|
||||
// OffscreenCanvas implements Transferable;
|
||||
|
|
Загрузка…
Ссылка в новой задаче