Bug 709490 - Part 2: Introduce OffscreenCanvas and let WebGL context work on workers. r=nical, r=jgilbert, r=jrmuizel, sr=ehsan

Thanks Jon Morton [:jmorton] (jonanin@gmail.com) for polishing patches.

--HG--
extra : rebase_source : ce16acdc340cbe67102da651552f574eee897ca9
This commit is contained in:
Morris Tseng 2015-10-12 11:21:03 +08:00
parent acf60f0d3e
commit d5147a9b2a
32 changed files with 1614 additions and 526 deletions

View File

@ -0,0 +1,265 @@
/* -*- Mode: C++; 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/. */
#include "CanvasRenderingContextHelper.h"
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/Telemetry.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "nsIScriptContext.h"
#include "nsJSUtils.h"
#include "WebGL1Context.h"
#include "WebGL2Context.h"
namespace mozilla {
namespace dom {
void
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
nsIGlobalObject* aGlobal,
FileCallback& 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;
}
}
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
if (mCurrentContext) {
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
public:
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
: mGlobal(aGlobal)
, mFileCallback(aCallback) {}
// This is called on main thread.
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
{
nsRefPtr<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);
}
}
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
mFileCallback->Call(*newBlob, rv);
mGlobal = nullptr;
mFileCallback = nullptr;
return rv.StealNSResult();
}
nsCOMPtr<nsIGlobalObject> mGlobal;
nsRefPtr<FileCallback> mFileCallback;
};
nsRefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(aGlobal, &aCallback);
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
imageBuffer,
format,
GetWidthHeight(),
callback);
}
already_AddRefed<nsICanvasRenderingContextInternal>
CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
{
MOZ_ASSERT(aContextType != CanvasContextType::NoContext);
nsRefPtr<nsICanvasRenderingContextInternal> ret;
switch (aContextType) {
case CanvasContextType::NoContext:
break;
case CanvasContextType::Canvas2D:
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
ret = new CanvasRenderingContext2D();
break;
case CanvasContextType::WebGL1:
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
ret = WebGL1Context::Create();
if (!ret)
return nullptr;
break;
case CanvasContextType::WebGL2:
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
ret = WebGL2Context::Create();
if (!ret)
return nullptr;
break;
}
MOZ_ASSERT(ret);
return ret.forget();
}
already_AddRefed<nsISupports>
CanvasRenderingContextHelper::GetContext(JSContext* aCx,
const nsAString& aContextId,
JS::Handle<JS::Value> aContextOptions,
ErrorResult& aRv)
{
CanvasContextType contextType;
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType))
return nullptr;
if (!mCurrentContext) {
// This canvas doesn't have a context yet.
nsRefPtr<nsICanvasRenderingContextInternal> context;
context = CreateContext(contextType);
if (!context) {
return nullptr;
}
// Ensure that the context participates in CC. Note that returning a
// CC participant from QI doesn't addref.
nsXPCOMCycleCollectionParticipant* cp = nullptr;
CallQueryInterface(context, &cp);
if (!cp) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mCurrentContext = context.forget();
mCurrentContextType = contextType;
aRv = UpdateContext(aCx, aContextOptions);
if (aRv.Failed()) {
aRv = NS_OK; // See bug 645792
return nullptr;
}
} else {
// We already have a context of some type.
if (contextType != mCurrentContextType)
return nullptr;
}
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
return context.forget();
}
nsresult
CanvasRenderingContextHelper::UpdateContext(JSContext* aCx,
JS::Handle<JS::Value> aNewContextOptions)
{
if (!mCurrentContext)
return NS_OK;
nsIntSize sz = GetWidthHeight();
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
nsresult rv = currentContext->SetIsOpaque(GetOpaqueAttr());
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
rv = currentContext->SetDimensions(sz.width, sz.height);
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
}
return rv;
}
nsresult
CanvasRenderingContextHelper::ParseParams(JSContext* aCx,
const nsAString& aType,
const JS::Value& aEncoderOptions,
nsAString& outParams,
bool* const outUsingCustomParseOptions)
{
// Quality parameter is only valid for the image/jpeg MIME type
if (aType.EqualsLiteral("image/jpeg")) {
if (aEncoderOptions.isNumber()) {
double quality = aEncoderOptions.toNumber();
// Quality must be between 0.0 and 1.0, inclusive
if (quality >= 0.0 && quality <= 1.0) {
outParams.AppendLiteral("quality=");
outParams.AppendInt(NS_lround(quality * 100.0));
}
}
}
// If we haven't parsed the aParams check for proprietary options.
// The proprietary option -moz-parse-options will take a image lib encoder
// parse options string as is and pass it to the encoder.
*outUsingCustomParseOptions = false;
if (outParams.Length() == 0 && aEncoderOptions.isString()) {
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
nsAutoJSString paramString;
if (!paramString.init(aCx, aEncoderOptions.toString())) {
return NS_ERROR_FAILURE;
}
if (StringBeginsWith(paramString, mozParseOptions)) {
nsDependentSubstring parseOptions = Substring(paramString,
mozParseOptions.Length(),
paramString.Length() -
mozParseOptions.Length());
outParams.Append(parseOptions);
*outUsingCustomParseOptions = true;
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,71 @@
/* -*- Mode: C++; 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/. */
#ifndef MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
#define MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
#include "mozilla/dom/BindingDeclarations.h"
#include "nsSize.h"
class nsICanvasRenderingContextInternal;
class nsIGlobalObject;
namespace mozilla {
class ErrorResult;
namespace dom {
class FileCallback;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2
};
/**
* Povides common RenderingContext functionality used by both OffscreenCanvas
* and HTMLCanvasElement.
*/
class CanvasRenderingContextHelper
{
public:
virtual already_AddRefed<nsISupports>
GetContext(JSContext* aCx,
const nsAString& aContextId,
JS::Handle<JS::Value> aContextOptions,
ErrorResult& aRv);
virtual bool GetOpaqueAttr() = 0;
protected:
virtual nsresult UpdateContext(JSContext* aCx,
JS::Handle<JS::Value> aNewContextOptions);
virtual nsresult ParseParams(JSContext* aCx,
const nsAString& aType,
const JS::Value& aEncoderOptions,
nsAString& outParams,
bool* const outCustomParseOptions);
void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback,
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType);
virtual nsIntSize GetWidthHeight() = 0;
CanvasContextType mCurrentContextType;
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
};
} // namespace dom
} // namespace mozilla
#endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_

View File

@ -23,12 +23,47 @@
#include "CanvasUtils.h"
#include "mozilla/gfx/Matrix.h"
#include "WebGL2Context.h"
using namespace mozilla::gfx;
namespace mozilla {
namespace CanvasUtils {
bool
GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
{
if (str.EqualsLiteral("2d")) {
*out_type = dom::CanvasContextType::Canvas2D;
return true;
}
if (str.EqualsLiteral("experimental-webgl")) {
*out_type = dom::CanvasContextType::WebGL1;
return true;
}
#ifdef MOZ_WEBGL_CONFORMANT
if (str.EqualsLiteral("webgl")) {
/* WebGL 1.0, $2.1 "Context Creation":
* If the user agent supports both the webgl and experimental-webgl
* canvas context types, they shall be treated as aliases.
*/
*out_type = dom::CanvasContextType::WebGL1;
return true;
}
#endif
if (WebGL2Context::IsSupported()) {
if (str.EqualsLiteral("webgl2")) {
*out_type = dom::CanvasContextType::WebGL2;
return true;
}
}
return false;
}
/**
* This security check utility might be called from an source that never taints
* others. For example, while painting a CanvasPattern, which is created from an

View File

@ -21,6 +21,7 @@ class HTMLCanvasElement;
namespace CanvasUtils {
bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type);
// Check that the rectangle [x,y,w,h] is a subrectangle of [0,0,realWidth,realHeight]

View File

@ -0,0 +1,210 @@
/* -*- 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/OffscreenCanvasBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/layers/AsyncCanvasRenderer.h"
#include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/Telemetry.h"
#include "CanvasRenderingContext2D.h"
#include "CanvasUtils.h"
#include "GLScreenBuffer.h"
#include "WebGL1Context.h"
#include "WebGL2Context.h"
namespace mozilla {
namespace dom {
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
bool aNeutered)
: mRenderer(aRenderer)
, mWidth(aWidth)
, mHeight(aHeight)
, mNeutered(aNeutered)
{
}
OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
{
}
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
uint32_t aHeight,
layers::AsyncCanvasRenderer* aRenderer)
: mAttrDirty(false)
, mNeutered(false)
, mWidth(aWidth)
, mHeight(aHeight)
, mCanvasClient(nullptr)
, mCanvasRenderer(aRenderer)
{}
OffscreenCanvas::~OffscreenCanvas()
{
if (mCanvasRenderer) {
mCanvasRenderer->SetCanvasClient(nullptr);
mCanvasRenderer->mContext = nullptr;
mCanvasRenderer->mActiveThread = nullptr;
}
if (mCanvasClient) {
ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
}
}
OffscreenCanvas*
OffscreenCanvas::GetParentObject() const
{
return nullptr;
}
JSObject*
OffscreenCanvas::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
}
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))
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
}
already_AddRefed<nsISupports> result =
CanvasRenderingContextHelper::GetContext(aCx,
aContextId,
aContextOptions,
aRv);
if (mCanvasRenderer && mCurrentContext && ImageBridgeChild::IsCreated()) {
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
mCanvasClient = ImageBridgeChild::GetSingleton()->
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
mCanvasRenderer->SetCanvasClient(mCanvasClient);
gl::GLContext* gl = static_cast<WebGLContext*>(mCurrentContext.get())->GL();
mCanvasRenderer->mContext = mCurrentContext;
mCanvasRenderer->mActiveThread = NS_GetCurrentThread();
mCanvasRenderer->mGLContext = gl;
gl::GLScreenBuffer* screen = gl->Screen();
gl::SurfaceCaps caps = screen->mCaps;
auto forwarder = mCanvasClient->GetForwarder();
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
if (factory)
screen->Morph(Move(factory));
}
return result;
}
already_AddRefed<nsICanvasRenderingContextInternal>
OffscreenCanvas::CreateContext(CanvasContextType aContextType)
{
nsRefPtr<nsICanvasRenderingContextInternal> ret =
CanvasRenderingContextHelper::CreateContext(aContextType);
ret->SetOffscreenCanvas(this);
return ret.forget();
}
void
OffscreenCanvas::CommitFrameToCompositor()
{
// The attributes has changed, we have to notify main
// thread to change canvas size.
if (mAttrDirty) {
if (mCanvasRenderer) {
mCanvasRenderer->SetWidth(mWidth);
mCanvasRenderer->SetHeight(mHeight);
mCanvasRenderer->NotifyElementAboutAttributesChanged();
}
mAttrDirty = false;
}
if (mCurrentContext) {
static_cast<WebGLContext*>(mCurrentContext.get())->PresentScreenBuffer();
}
if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
ImageBridgeChild::GetSingleton()->
UpdateAsyncCanvasRenderer(mCanvasRenderer);
}
}
OffscreenCanvasCloneData*
OffscreenCanvas::ToCloneData()
{
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth,
mHeight, mNeutered);
}
/* static */ already_AddRefed<OffscreenCanvas>
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
{
MOZ_ASSERT(aData);
nsRefPtr<OffscreenCanvas> wc =
new OffscreenCanvas(aData->mWidth, aData->mHeight, aData->mRenderer);
if (aData->mNeutered) {
wc->SetNeutered();
}
return wc.forget();
}
/* static */ bool
OffscreenCanvas::PrefEnabled(JSContext* aCx, JSObject* aObj)
{
return gfxPrefs::OffscreenCanvasEnabled();
}
/* static */ bool
OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj)
{
if (NS_IsMainThread()) {
return true;
}
return PrefEnabled(aCx, aObj);
}
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_INHERITED(OffscreenCanvas)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,166 @@
/* -*- 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_OFFSCREENCANVAS_H_
#define MOZILLA_DOM_OFFSCREENCANVAS_H_
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/RefPtr.h"
#include "CanvasRenderingContextHelper.h"
#include "nsCycleCollectionParticipant.h"
struct JSContext;
namespace mozilla {
class ErrorResult;
namespace layers {
class AsyncCanvasRenderer;
class CanvasClient;
} // namespace layers
namespace dom {
// This is helper class for transferring OffscreenCanvas to worker thread.
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
// Canvas to worker thread directly. Thus, we create this helper class and
// store necessary data in it then pass it to worker thread.
struct OffscreenCanvasCloneData final
{
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
bool aNeutered);
~OffscreenCanvasCloneData();
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
uint32_t mWidth;
uint32_t mHeight;
bool mNeutered;
};
class OffscreenCanvas final : public DOMEventTargetHelper
, public CanvasRenderingContextHelper
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
OffscreenCanvas(uint32_t aWidth,
uint32_t aHeight,
layers::AsyncCanvasRenderer* aRenderer);
OffscreenCanvas* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
uint32_t Width() const
{
return mWidth;
}
uint32_t Height() const
{
return mHeight;
}
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
{
if (mNeutered) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (mWidth != aWidth) {
mWidth = aWidth;
CanvasAttrChanged();
}
}
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
{
if (mNeutered) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (mHeight != aHeight) {
mHeight = aHeight;
CanvasAttrChanged();
}
}
nsICanvasRenderingContextInternal* GetContext() const
{
return mCurrentContext;
}
static already_AddRefed<OffscreenCanvas>
CreateFromCloneData(OffscreenCanvasCloneData* aData);
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
// Return true on main-thread, and return gfx.offscreencanvas.enabled
// on worker thread.
static bool PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj);
OffscreenCanvasCloneData* ToCloneData();
void CommitFrameToCompositor();
virtual bool GetOpaqueAttr() override
{
return false;
}
virtual nsIntSize GetWidthHeight() override
{
return nsIntSize(mWidth, mHeight);
}
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType) override;
virtual already_AddRefed<nsISupports>
GetContext(JSContext* aCx,
const nsAString& aContextId,
JS::Handle<JS::Value> aContextOptions,
ErrorResult& aRv) override;
void SetNeutered()
{
mNeutered = true;
}
bool IsNeutered() const
{
return mNeutered;
}
private:
~OffscreenCanvas();
void CanvasAttrChanged()
{
mAttrDirty = true;
UpdateContext(nullptr, JS::NullHandleValue);
}
bool mAttrDirty;
bool mNeutered;
uint32_t mWidth;
uint32_t mHeight;
layers::CanvasClient* mCanvasClient;
RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
};
} // namespace dom
} // namespace mozilla
#endif // MOZILLA_DOM_OFFSCREENCANVAS_H_

View File

@ -22,6 +22,7 @@
#include "ImageEncoder.h"
#include "Layers.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/EnumeratedArrayCycleCollection.h"
@ -79,125 +80,6 @@ using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla::layers;
WebGLObserver::WebGLObserver(WebGLContext* webgl)
: mWebGL(webgl)
{
}
WebGLObserver::~WebGLObserver()
{
}
void
WebGLObserver::Destroy()
{
UnregisterMemoryPressureEvent();
UnregisterVisibilityChangeEvent();
mWebGL = nullptr;
}
void
WebGLObserver::RegisterVisibilityChangeEvent()
{
if (!mWebGL)
return;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
MOZ_ASSERT(canvas);
if (canvas) {
nsIDocument* document = canvas->OwnerDoc();
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true, false);
}
}
void
WebGLObserver::UnregisterVisibilityChangeEvent()
{
if (!mWebGL)
return;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
if (canvas) {
nsIDocument* document = canvas->OwnerDoc();
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true);
}
}
void
WebGLObserver::RegisterMemoryPressureEvent()
{
if (!mWebGL)
return;
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService)
observerService->AddObserver(this, "memory-pressure", false);
}
void
WebGLObserver::UnregisterMemoryPressureEvent()
{
if (!mWebGL)
return;
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
// Do not assert on observerService here. This might be triggered by
// the cycle collector at a late enough time, that XPCOM services are
// no longer available. See bug 1029504.
if (observerService)
observerService->RemoveObserver(this, "memory-pressure");
}
NS_IMETHODIMP
WebGLObserver::Observe(nsISupports*, const char* topic, const char16_t*)
{
if (!mWebGL || strcmp(topic, "memory-pressure")) {
return NS_OK;
}
bool wantToLoseContext = mWebGL->mLoseContextOnMemoryPressure;
if (!mWebGL->mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
wantToLoseContext = false;
}
if (wantToLoseContext)
mWebGL->ForceLoseContext();
return NS_OK;
}
NS_IMETHODIMP
WebGLObserver::HandleEvent(nsIDOMEvent* event)
{
nsAutoString type;
event->GetType(type);
if (!mWebGL || !type.EqualsLiteral("visibilitychange"))
return NS_OK;
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
MOZ_ASSERT(canvas);
if (canvas && !canvas->OwnerDoc()->Hidden())
mWebGL->ForceRestoreContext();
return NS_OK;
}
WebGLContextOptions::WebGLContextOptions()
: alpha(true)
, depth(true)
@ -208,7 +90,7 @@ WebGLContextOptions::WebGLContextOptions()
, failIfMajorPerformanceCaveat(false)
{
// Set default alpha state based on preference.
if (Preferences::GetBool("webgl.default-no-alpha", false))
if (gfxPrefs::WebGLDefaultNoAlpha())
alpha = false;
}
@ -282,7 +164,10 @@ WebGLContext::WebGLContext()
mPixelStorePackAlignment = 4;
mPixelStoreUnpackAlignment = 4;
WebGLMemoryTracker::AddWebGLContext(this);
if (NS_IsMainThread()) {
// XXX mtseng: bug 709490, not thread safe
WebGLMemoryTracker::AddWebGLContext(this);
}
mAllowContextRestore = true;
mLastLossWasSimulated = false;
@ -296,15 +181,12 @@ WebGLContext::WebGLContext()
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
mAlreadyWarnedAboutViewportLargerThanDest = false;
mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
if (mMaxWarnings < -1) {
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
mMaxWarnings = 0;
}
mContextObserver = new WebGLObserver(this);
MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
mLastUseIndex = 0;
InvalidateBufferFetching();
@ -319,10 +201,12 @@ WebGLContext::WebGLContext()
WebGLContext::~WebGLContext()
{
RemovePostRefreshObserver();
mContextObserver->Destroy();
DestroyResourcesAndContext();
WebGLMemoryTracker::RemoveWebGLContext(this);
if (NS_IsMainThread()) {
// XXX mtseng: bug 709490, not thread safe
WebGLMemoryTracker::RemoveWebGLContext(this);
}
mContextLossHandler->DisableTimer();
mContextLossHandler = nullptr;
@ -331,8 +215,6 @@ WebGLContext::~WebGLContext()
void
WebGLContext::DestroyResourcesAndContext()
{
mContextObserver->UnregisterMemoryPressureEvent();
if (!gl)
return;
@ -431,6 +313,35 @@ WebGLContext::Invalidate()
mCanvasElement->InvalidateCanvasContent(nullptr);
}
void
WebGLContext::OnVisibilityChange()
{
if (!IsContextLost()) {
return;
}
if (!mRestoreWhenVisible || mLastLossWasSimulated) {
return;
}
ForceRestoreContext();
}
void
WebGLContext::OnMemoryPressure()
{
bool shouldLoseContext = mLoseContextOnMemoryPressure;
if (!mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
shouldLoseContext = false;
}
if (shouldLoseContext)
ForceLoseContext();
}
//
// nsICanvasRenderingContextInternal
//
@ -513,7 +424,7 @@ static bool
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
{
int32_t status;
if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
return false;
return status != nsIGfxInfo::FEATURE_STATUS_OK;
@ -524,19 +435,29 @@ HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
{
int32_t status;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
&status);
if (status)
return true;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_OPENGL_LAYERS,
&status);
if (status)
return true;
@ -593,11 +514,14 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
// we should really have this behind a
// |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
// for now it's just behind a pref for testing/evaluation.
baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
#ifdef MOZ_WIDGET_GONK
do {
auto canvasElement = webgl->GetCanvas();
if (!canvasElement)
break;
auto ownerDoc = canvasElement->OwnerDoc();
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(ownerDoc);
if (!docWidget)
@ -618,7 +542,7 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
// Done with baseCaps construction.
bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (!forceAllowAA &&
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
@ -738,7 +662,7 @@ bool
WebGLContext::CreateAndInitGL(bool forceEnabled)
{
bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
disableANGLE = true;
@ -819,10 +743,6 @@ WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
NS_IMETHODIMP
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
{
// Early error return cases
if (!GetCanvas())
return NS_ERROR_FAILURE;
if (signedWidth < 0 || signedHeight < 0) {
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
return NS_ERROR_OUT_OF_MEMORY;
@ -832,7 +752,10 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
uint32_t height = signedHeight;
// Early success return cases
GetCanvas()->InvalidateCanvas();
// May have a OffscreenCanvas instead of an HTMLCanvasElement
if (GetCanvas())
GetCanvas()->InvalidateCanvas();
// Zero-sized surfaces can cause problems.
if (width == 0)
@ -905,10 +828,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
// pick up the old generation.
++mGeneration;
// Get some prefs for some preferred/overriden things
NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
bool disabled = Preferences::GetBool("webgl.disabled", false);
bool disabled = gfxPrefs::WebGLDisabled();
// TODO: When we have software webgl support we should use that instead.
disabled |= gfxPlatform::InSafeMode();
@ -931,7 +851,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
}
// Alright, now let's start trying.
bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
bool forceEnabled = gfxPrefs::WebGLForceEnabled();
ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
MOZ_ASSERT(!gl);
@ -1052,6 +972,11 @@ WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
#endif
MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
if (!NS_IsMainThread()) {
// XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
return;
}
// it's important to update the index on a new context before losing old contexts,
// otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
// when choosing which one to lose first.
@ -1268,25 +1193,26 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
}
WebGLContextUserData* userData = nullptr;
if (builder->IsPaintingToWindow()) {
// Make the layer tell us whenever a transaction finishes (including
// the current transaction), so we can clear our invalidation state and
// start invalidating again. We need to do this for the layer that is
// being painted to a window (there shouldn't be more than one at a time,
// and if there is, flushing the invalidation state more often than
// necessary is harmless).
if (builder->IsPaintingToWindow() && mCanvasElement) {
// Make the layer tell us whenever a transaction finishes (including
// the current transaction), so we can clear our invalidation state and
// start invalidating again. We need to do this for the layer that is
// being painted to a window (there shouldn't be more than one at a time,
// and if there is, flushing the invalidation state more often than
// necessary is harmless).
// The layer will be destroyed when we tear down the presentation
// (at the latest), at which time this userData will be destroyed,
// releasing the reference to the element.
// The userData will receive DidTransactionCallbacks, which flush the
// the invalidation state to indicate that the canvas is up to date.
userData = new WebGLContextUserData(mCanvasElement);
canvasLayer->SetDidTransactionCallback(
WebGLContextUserData::DidTransactionCallback, userData);
canvasLayer->SetPreTransactionCallback(
WebGLContextUserData::PreTransactionCallback, userData);
// The layer will be destroyed when we tear down the presentation
// (at the latest), at which time this userData will be destroyed,
// releasing the reference to the element.
// The userData will receive DidTransactionCallbacks, which flush the
// the invalidation state to indicate that the canvas is up to date.
userData = new WebGLContextUserData(mCanvasElement);
canvasLayer->SetDidTransactionCallback(
WebGLContextUserData::DidTransactionCallback, userData);
canvasLayer->SetPreTransactionCallback(
WebGLContextUserData::PreTransactionCallback, userData);
}
canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
CanvasLayer::Data data;
@ -1316,6 +1242,27 @@ WebGLContext::GetCompositorBackendType() const
return LayersBackend::LAYERS_NONE;
}
void
WebGLContext::Commit()
{
if (mOffscreenCanvas) {
mOffscreenCanvas->CommitFrameToCompositor();
}
}
void
WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
{
if (mCanvasElement) {
MOZ_RELEASE_ASSERT(!mOffscreenCanvas);
retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
} else if (mOffscreenCanvas) {
retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
} else {
retval.SetNull();
}
}
void
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
{
@ -1624,7 +1571,7 @@ WebGLContext::RunContextLossTimer()
mContextLossHandler->RunTimer();
}
class UpdateContextLossStatusTask : public nsRunnable
class UpdateContextLossStatusTask : public nsCancelableRunnable
{
nsRefPtr<WebGLContext> mWebGL;
@ -1635,10 +1582,16 @@ public:
}
NS_IMETHOD Run() {
mWebGL->UpdateContextLossStatus();
if (mWebGL)
mWebGL->UpdateContextLossStatus();
return NS_OK;
}
NS_IMETHOD Cancel() {
mWebGL = nullptr;
return NS_OK;
}
};
void
@ -1665,7 +1618,7 @@ WebGLContext::EnqueueUpdateContextLossStatus()
void
WebGLContext::UpdateContextLossStatus()
{
if (!mCanvasElement) {
if (!mCanvasElement && !mOffscreenCanvas) {
// the canvas is gone. That happens when the page was closed before we got
// this timer event. In this case, there's nothing to do here, just don't crash.
return;
@ -1693,12 +1646,23 @@ WebGLContext::UpdateContextLossStatus()
// callback, so do that now.
bool useDefaultHandler;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextlost"),
true,
true,
&useDefaultHandler);
if (mCanvasElement) {
nsContentUtils::DispatchTrustedEvent(
mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextlost"),
true,
true,
&useDefaultHandler);
} else {
// OffscreenCanvas case
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
event->SetTrusted(true);
mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
}
// We sent the callback, so we're just 'regular lost' now.
mContextStatus = ContextLost;
// If we're told to use the default handler, it means the script
@ -1750,11 +1714,22 @@ WebGLContext::UpdateContextLossStatus()
// Revival!
mContextStatus = ContextNotLost;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextrestored"),
true,
true);
if (mCanvasElement) {
nsContentUtils::DispatchTrustedEvent(
mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextrestored"),
true,
true);
} else {
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
event->SetTrusted(true);
bool unused;
mOffscreenCanvas->DispatchEvent(event, &unused);
}
mEmitContextLostErrorOnce = true;
return;
}
@ -1772,12 +1747,6 @@ WebGLContext::ForceLoseContext(bool simulateLosing)
DestroyResourcesAndContext();
mLastLossWasSimulated = simulateLosing;
// Register visibility change observer to defer the context restoring.
// Restore the context when the app is visible.
if (mRestoreWhenVisible && !mLastLossWasSimulated) {
mContextObserver->RegisterVisibilityChangeEvent();
}
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
@ -1789,8 +1758,6 @@ WebGLContext::ForceRestoreContext()
mContextStatus = ContextLostAwaitingRestore;
mAllowContextRestore = true; // Hey, you did say 'force'.
mContextObserver->UnregisterVisibilityChangeEvent();
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
@ -1925,6 +1892,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
mCanvasElement,
mOffscreenCanvas,
mExtensions,
mBound2DTextures,
mBoundCubeMapTextures,

View File

@ -40,7 +40,11 @@
// Generated
#include "nsIDOMEventListener.h"
#include "nsIDOMWebGLRenderingContext.h"
#include "nsICanvasRenderingContextInternal.h"
#include "nsIObserver.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "nsWrapperCache.h"
#include "nsLayoutUtils.h"
class nsIDocShell;
@ -80,7 +84,6 @@ class WebGLContextLossHandler;
class WebGLBuffer;
class WebGLExtensionBase;
class WebGLFramebuffer;
class WebGLObserver;
class WebGLProgram;
class WebGLQuery;
class WebGLRenderbuffer;
@ -95,6 +98,7 @@ class WebGLVertexArray;
namespace dom {
class Element;
class ImageData;
class OwningHTMLCanvasElementOrOffscreenCanvas;
struct WebGLContextAttributes;
template<typename> struct Nullable;
} // namespace dom
@ -184,7 +188,6 @@ class WebGLContext
friend class WebGLExtensionLoseContext;
friend class WebGLExtensionVertexArray;
friend class WebGLMemoryTracker;
friend class WebGLObserver;
enum {
UNPACK_FLIP_Y_WEBGL = 0x9240,
@ -214,6 +217,9 @@ public:
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
virtual void OnVisibilityChange() override;
virtual void OnMemoryPressure() override;
// nsICanvasRenderingContextInternal
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
@ -361,8 +367,11 @@ public:
void AssertCachedBindings();
void AssertCachedState();
// WebIDL WebGLRenderingContext API
dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
// WebIDL WebGLRenderingContext API
void Commit();
void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
GLsizei DrawingBufferHeight() const {
return IsContextLost() ? 0 : mHeight;
@ -1508,8 +1517,6 @@ protected:
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
#endif
nsRefPtr<WebGLObserver> mContextObserver;
public:
// console logging helpers
void GenerateWarning(const char* fmt, ...);
@ -1614,32 +1621,6 @@ WebGLContext::ValidateObject(const char* info, ObjectType* object)
return ValidateObjectAssumeNonNull(info, object);
}
// Listen visibilitychange and memory-pressure event for context lose/restore
class WebGLObserver final
: public nsIObserver
, public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIDOMEVENTLISTENER
explicit WebGLObserver(WebGLContext* webgl);
void Destroy();
void RegisterVisibilityChangeEvent();
void UnregisterVisibilityChangeEvent();
void RegisterMemoryPressureEvent();
void UnregisterMemoryPressureEvent();
private:
~WebGLObserver();
WebGLContext* mWebGL;
};
size_t RoundUpToMultipleOf(size_t value, size_t multiple);
bool

View File

@ -6,6 +6,7 @@
#include "WebGLContext.h"
#include "WebGLContextUtils.h"
#include "WebGLExtensions.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "nsString.h"
@ -74,12 +75,15 @@ bool WebGLContext::IsExtensionSupported(JSContext* cx,
// Chrome contexts need access to debug information even when
// webgl.disable-extensions is set. This is used in the graphics
// section of about:support.
if (xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)))
// section of about:support
if (NS_IsMainThread() &&
xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
allowPrivilegedExts = true;
}
if (Preferences::GetBool("webgl.enable-privileged-extensions", false))
if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
allowPrivilegedExts = true;
}
if (allowPrivilegedExts) {
switch (ext) {
@ -181,9 +185,7 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
break;
}
if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
IsWebGL2())
{
if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) {
switch (ext) {
case WebGLExtensionID::EXT_disjoint_timer_query:
return WebGLExtensionDisjointTimerQuery::IsSupported(this);

View File

@ -1390,7 +1390,10 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
if (IsContextLost())
return;
if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
if (mCanvasElement &&
mCanvasElement->IsWriteOnly() &&
!nsContentUtils::IsCallerChrome())
{
GenerateWarning("readPixels: Not allowed");
return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
}

View File

@ -8,15 +8,103 @@
#include "nsITimer.h"
#include "nsThreadUtils.h"
#include "WebGLContext.h"
#include "mozilla/dom/WorkerPrivate.h"
namespace mozilla {
// -------------------------------------------------------------------
// Begin worker specific code
// -------------------------------------------------------------------
// On workers we can only dispatch CancelableRunnables, so we have to wrap the
// timer's EventTarget to use our own cancelable runnable
class ContextLossWorkerEventTarget final : public nsIEventTarget
{
public:
explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
: mEventTarget(aEventTarget)
{
MOZ_ASSERT(aEventTarget);
}
NS_DECL_NSIEVENTTARGET
NS_DECL_THREADSAFE_ISUPPORTS
protected:
~ContextLossWorkerEventTarget() {}
private:
nsCOMPtr<nsIEventTarget> mEventTarget;
};
class ContextLossWorkerRunnable final : public nsICancelableRunnable
{
public:
explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
: mRunnable(aRunnable)
{
}
NS_DECL_NSICANCELABLERUNNABLE
NS_DECL_THREADSAFE_ISUPPORTS
NS_FORWARD_NSIRUNNABLE(mRunnable->)
protected:
~ContextLossWorkerRunnable() {}
private:
nsCOMPtr<nsIRunnable> mRunnable;
};
NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
nsISupports)
NS_IMETHODIMP
ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
{
nsCOMPtr<nsIRunnable> event(aEvent);
return Dispatch(event.forget(), aFlags);
}
NS_IMETHODIMP
ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aEvent,
uint32_t aFlags)
{
nsCOMPtr<nsIRunnable> eventRef(aEvent);
nsRefPtr<ContextLossWorkerRunnable> wrappedEvent =
new ContextLossWorkerRunnable(eventRef);
return mEventTarget->Dispatch(wrappedEvent, aFlags);
}
NS_IMETHODIMP
ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
{
return mEventTarget->IsOnCurrentThread(aResult);
}
NS_IMPL_ISUPPORTS(ContextLossWorkerRunnable, nsICancelableRunnable,
nsIRunnable)
NS_IMETHODIMP
ContextLossWorkerRunnable::Cancel()
{
mRunnable = nullptr;
return NS_OK;
}
// -------------------------------------------------------------------
// End worker-specific code
// -------------------------------------------------------------------
WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
: mWeakWebGL(webgl)
, mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
, mIsTimerRunning(false)
, mShouldRunTimerAgain(false)
, mIsDisabled(false)
, mFeatureAdded(false)
#ifdef DEBUG
, mThread(NS_GetCurrentThread())
#endif
@ -90,6 +178,17 @@ WebGLContextLossHandler::RunTimer()
return;
}
if (!NS_IsMainThread()) {
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
if (!mFeatureAdded) {
workerPrivate->AddFeature(workerPrivate->GetJSContext(), this);
mFeatureAdded = true;
}
}
StartTimer(1000);
mIsTimerRunning = true;
@ -104,6 +203,14 @@ WebGLContextLossHandler::DisableTimer()
mIsDisabled = true;
if (mFeatureAdded) {
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
MOZ_RELEASE_ASSERT(workerPrivate);
workerPrivate->RemoveFeature(workerPrivate->GetJSContext(), this);
mFeatureAdded = false;
}
// We can't just Cancel() the timer, as sometimes we end up
// receiving a callback after calling Cancel(). This could cause us
// to receive the callback after object destruction.
@ -116,4 +223,16 @@ WebGLContextLossHandler::DisableTimer()
mTimer->SetDelay(0);
}
bool
WebGLContextLossHandler::Notify(JSContext* aCx, dom::workers::Status aStatus)
{
bool isWorkerRunning = aStatus < dom::workers::Closing;
if (!isWorkerRunning && mIsTimerRunning) {
mIsTimerRunning = false;
this->Release();
}
return true;
}
} // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "mozilla/WeakPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "WorkerFeature.h"
class nsIThread;
class nsITimer;
@ -17,13 +18,14 @@ class nsITimer;
namespace mozilla {
class WebGLContext;
class WebGLContextLossHandler
class WebGLContextLossHandler : public dom::workers::WorkerFeature
{
WeakPtr<WebGLContext> mWeakWebGL;
nsCOMPtr<nsITimer> mTimer;
bool mIsTimerRunning;
bool mShouldRunTimerAgain;
bool mIsDisabled;
bool mFeatureAdded;
DebugOnly<nsIThread*> mThread;
public:
@ -33,6 +35,7 @@ public:
void RunTimer();
void DisableTimer();
bool Notify(JSContext* aCx, dom::workers::Status aStatus) override;
protected:
~WebGLContextLossHandler();

View File

@ -9,8 +9,6 @@
namespace mozilla {
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
NS_IMETHODIMP
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
nsISupports* data, bool)

View File

@ -8,6 +8,7 @@
#include <algorithm>
#include "angle/ShaderLang.h"
#include "CanvasUtils.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "jsfriendapi.h"
#include "mozilla/CheckedInt.h"
@ -1665,11 +1666,11 @@ WebGLContext::InitAndValidateGL()
return false;
}
mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-pressure", false);
mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);
mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
if (MinCapabilityMode())
mDisableFragHighP = true;
@ -1878,10 +1879,7 @@ WebGLContext::InitAndValidateGL()
#endif
// Check the shader validator pref
NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
mBypassShaderValidation);
mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
// initialize shader translator
if (!ShInitialize()) {
@ -1937,9 +1935,6 @@ WebGLContext::InitAndValidateGL()
mDefaultVertexArray->BindVertexArray();
}
if (mLoseContextOnMemoryPressure)
mContextObserver->RegisterMemoryPressureEvent();
return true;
}

View File

@ -16,8 +16,6 @@
namespace mozilla {
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
NS_IMETHODIMP
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
nsISupports* data, bool)

View File

@ -6,6 +6,7 @@
#include "WebGLShaderValidator.h"
#include "angle/ShaderLang.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "mozilla/Preferences.h"
#include "MurmurHash3.h"
@ -43,7 +44,7 @@ ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
}
if (Preferences::GetBool("webgl.all-angle-options", false)) {
if (gfxPrefs::WebGLAllANGLEOptions()) {
return options |
SH_VALIDATE_LOOP_INDEXING |
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |

View File

@ -28,10 +28,12 @@ EXPORTS.mozilla.dom += [
'CanvasPath.h',
'CanvasPattern.h',
'CanvasRenderingContext2D.h',
'CanvasRenderingContextHelper.h',
'CanvasUtils.h',
'ImageBitmap.h',
'ImageBitmapSource.h',
'ImageData.h',
'OffscreenCanvas.h',
'TextMetrics.h',
'WebGLVertexArrayObject.h',
]
@ -40,11 +42,13 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
'CanvasImageCache.cpp',
'CanvasRenderingContext2D.cpp',
'CanvasRenderingContextHelper.cpp',
'CanvasUtils.cpp',
'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp',
'ImageBitmap.cpp',
'ImageData.cpp',
'OffscreenCanvas.cpp',
]
# WebGL Sources
@ -150,6 +154,7 @@ LOCAL_INCLUDES += [
'/dom/base',
'/dom/html',
'/dom/svg',
'/dom/workers',
'/dom/xul',
'/gfx/gl',
'/image',

View File

@ -12,11 +12,12 @@
#include "nsIDocShell.h"
#include "nsRefreshDriver.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x3cc9e801, 0x1806, 0x4ff6, \
{ 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
{ 0xb84f2fed, 0x9d4b, 0x430b, \
{ 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
class gfxASurface;
class nsDisplayListBuilder;
@ -79,6 +80,11 @@ public:
return mCanvasElement;
}
void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
{
mOffscreenCanvas = aOffscreenCanvas;
}
// Dimensions of the canvas, in pixels.
virtual int32_t GetWidth() const = 0;
virtual int32_t GetHeight() const = 0;
@ -151,6 +157,10 @@ public:
// Given a point, return hit region ID if it exists or an empty string if it doesn't
virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
virtual void OnVisibilityChange() {}
virtual void OnMemoryPressure() {}
//
// shmem support
//
@ -163,6 +173,7 @@ public:
protected:
nsRefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement;
nsRefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;
nsRefPtr<nsRefreshDriver> mRefreshDriver;
};

View File

@ -19,6 +19,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/HTMLCanvasElementBinding.h"
#include "mozilla/dom/MouseEvent.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/layers/AsyncCanvasRenderer.h"
@ -240,6 +241,113 @@ HTMLCanvasPrintState::NotifyDone()
// ---------------------------------------------------------------------------
HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
: mElement(aElement)
{
RegisterVisibilityChangeEvent();
RegisterMemoryPressureEvent();
}
HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
{
Destroy();
}
void
HTMLCanvasElementObserver::Destroy()
{
UnregisterMemoryPressureEvent();
UnregisterVisibilityChangeEvent();
mElement = nullptr;
}
void
HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
{
if (!mElement) {
return;
}
nsIDocument* document = mElement->OwnerDoc();
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true, false);
}
void
HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
{
if (!mElement) {
return;
}
nsIDocument* document = mElement->OwnerDoc();
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this, true);
}
void
HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
{
if (!mElement) {
return;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService)
observerService->AddObserver(this, "memory-pressure", false);
}
void
HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
{
if (!mElement) {
return;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
// Do not assert on observerService here. This might be triggered by
// the cycle collector at a late enough time, that XPCOM services are
// no longer available. See bug 1029504.
if (observerService)
observerService->RemoveObserver(this, "memory-pressure");
}
NS_IMETHODIMP
HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
{
if (!mElement || strcmp(aTopic, "memory-pressure")) {
return NS_OK;
}
mElement->OnMemoryPressure();
return NS_OK;
}
NS_IMETHODIMP
HTMLCanvasElementObserver::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (!mElement || !type.EqualsLiteral("visibilitychange")) {
return NS_OK;
}
mElement->OnVisibilityChange();
return NS_OK;
}
NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
// ---------------------------------------------------------------------------
HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo),
mWriteOnly(false)
@ -248,6 +356,11 @@ HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& a
HTMLCanvasElement::~HTMLCanvasElement()
{
if (mContextObserver) {
mContextObserver->Destroy();
mContextObserver = nullptr;
}
ResetPrintCallback();
if (mRequestedFrameRefreshObserver) {
mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
@ -277,6 +390,22 @@ HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<nsICanvasRenderingContextInternal>
HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
{
nsRefPtr<nsICanvasRenderingContextInternal> ret =
CanvasRenderingContextHelper::CreateContext(aContextType);
// Add Observer for webgl canvas.
if (aContextType == CanvasContextType::WebGL1 ||
aContextType == CanvasContextType::WebGL2) {
mContextObserver = new HTMLCanvasElementObserver(this);
}
ret->SetCanvasElement(this);
return ret.forget();
}
nsIntSize
HTMLCanvasElement::GetWidthHeight()
{
@ -564,48 +693,6 @@ HTMLCanvasElement::ExtractData(nsAString& aType,
aStream);
}
nsresult
HTMLCanvasElement::ParseParams(JSContext* aCx,
const nsAString& aType,
const JS::Value& aEncoderOptions,
nsAString& aParams,
bool* usingCustomParseOptions)
{
// Quality parameter is only valid for the image/jpeg MIME type
if (aType.EqualsLiteral("image/jpeg")) {
if (aEncoderOptions.isNumber()) {
double quality = aEncoderOptions.toNumber();
// Quality must be between 0.0 and 1.0, inclusive
if (quality >= 0.0 && quality <= 1.0) {
aParams.AppendLiteral("quality=");
aParams.AppendInt(NS_lround(quality * 100.0));
}
}
}
// If we haven't parsed the aParams check for proprietary options.
// The proprietary option -moz-parse-options will take a image lib encoder
// parse options string as is and pass it to the encoder.
*usingCustomParseOptions = false;
if (aParams.Length() == 0 && aEncoderOptions.isString()) {
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
nsAutoJSString paramString;
if (!paramString.init(aCx, aEncoderOptions.toString())) {
return NS_ERROR_FAILURE;
}
if (StringBeginsWith(paramString, mozParseOptions)) {
nsDependentSubstring parseOptions = Substring(paramString,
mozParseOptions.Length(),
paramString.Length() -
mozParseOptions.Length());
aParams.Append(parseOptions);
*usingCustomParseOptions = true;
}
}
return NS_OK;
}
nsresult
HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
const nsAString& aMimeType,
@ -664,84 +751,35 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
return;
}
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;
}
}
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
if (mCurrentContext) {
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
public:
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
: mGlobal(aGlobal)
, mFileCallback(aCallback) {}
// This is called on main thread.
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
{
nsRefPtr<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);
}
}
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
mFileCallback->Call(*newBlob, rv);
mGlobal = nullptr;
mFileCallback = nullptr;
return rv.StealNSResult();
}
nsCOMPtr<nsIGlobalObject> mGlobal;
nsRefPtr<FileCallback> mFileCallback;
};
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
nsRefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, &aCallback);
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
imageBuffer,
format,
GetSize(),
callback);
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
aParams, aRv);
}
OffscreenCanvas*
HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
{
if (mCurrentContext) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
if (!mOffscreenCanvas) {
nsIntSize sz = GetWidthHeight();
nsRefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
renderer->SetWidth(sz.width);
renderer->SetHeight(sz.height);
mOffscreenCanvas = new OffscreenCanvas(sz.width, sz.height, renderer);
mContextObserver = new HTMLCanvasElementObserver(this);
} else {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
}
return mOffscreenCanvas;
}
already_AddRefed<File>
@ -812,76 +850,6 @@ HTMLCanvasElement::MozGetAsBlobImpl(const nsAString& aName,
return NS_OK;
}
static bool
GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
{
if (str.EqualsLiteral("2d")) {
*out_type = CanvasContextType::Canvas2D;
return true;
}
if (str.EqualsLiteral("experimental-webgl")) {
*out_type = CanvasContextType::WebGL1;
return true;
}
#ifdef MOZ_WEBGL_CONFORMANT
if (str.EqualsLiteral("webgl")) {
/* WebGL 1.0, $2.1 "Context Creation":
* If the user agent supports both the webgl and experimental-webgl
* canvas context types, they shall be treated as aliases.
*/
*out_type = CanvasContextType::WebGL1;
return true;
}
#endif
if (WebGL2Context::IsSupported()) {
if (str.EqualsLiteral("webgl2")) {
*out_type = CanvasContextType::WebGL2;
return true;
}
}
return false;
}
static already_AddRefed<nsICanvasRenderingContextInternal>
CreateContextForCanvas(CanvasContextType contextType, HTMLCanvasElement* canvas)
{
MOZ_ASSERT(contextType != CanvasContextType::NoContext);
nsRefPtr<nsICanvasRenderingContextInternal> ret;
switch (contextType) {
case CanvasContextType::NoContext:
break;
case CanvasContextType::Canvas2D:
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
ret = new CanvasRenderingContext2D();
break;
case CanvasContextType::WebGL1:
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
ret = WebGL1Context::Create();
if (!ret)
return nullptr;
break;
case CanvasContextType::WebGL2:
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
ret = WebGL2Context::Create();
if (!ret)
return nullptr;
break;
}
MOZ_ASSERT(ret);
ret->SetCanvasElement(canvas);
return ret.forget();
}
nsresult
HTMLCanvasElement::GetContext(const nsAString& aContextId,
nsISupports** aContext)
@ -895,45 +863,14 @@ already_AddRefed<nsISupports>
HTMLCanvasElement::GetContext(JSContext* aCx,
const nsAString& aContextId,
JS::Handle<JS::Value> aContextOptions,
ErrorResult& rv)
ErrorResult& aRv)
{
CanvasContextType contextType;
if (!GetCanvasContextType(aContextId, &contextType))
if (mOffscreenCanvas) {
return nullptr;
if (!mCurrentContext) {
// This canvas doesn't have a context yet.
nsRefPtr<nsICanvasRenderingContextInternal> context;
context = CreateContextForCanvas(contextType, this);
if (!context)
return nullptr;
// Ensure that the context participates in CC. Note that returning a
// CC participant from QI doesn't addref.
nsXPCOMCycleCollectionParticipant* cp = nullptr;
CallQueryInterface(context, &cp);
if (!cp) {
rv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mCurrentContext = context.forget();
mCurrentContextType = contextType;
rv = UpdateContext(aCx, aContextOptions);
if (rv.Failed()) {
rv = NS_OK; // See bug 645792
return nullptr;
}
} else {
// We already have a context of some type.
if (contextType != mCurrentContextType)
return nullptr;
}
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
return context.forget();
return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
aContextOptions, aRv);
}
NS_IMETHODIMP
@ -955,7 +892,7 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
// This canvas doesn't have a context yet.
nsRefPtr<nsICanvasRenderingContextInternal> context;
context = CreateContextForCanvas(contextType, this);
context = CreateContext(contextType);
if (!context) {
*aContext = nullptr;
return NS_OK;
@ -977,36 +914,6 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
return NS_OK;
}
nsresult
HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
{
if (!mCurrentContext)
return NS_OK;
nsIntSize sz = GetWidthHeight();
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
nsresult rv = currentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
rv = currentContext->SetDimensions(sz.width, sz.height);
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
return rv;
}
nsIntSize
HTMLCanvasElement::GetSize()
@ -1110,6 +1017,12 @@ HTMLCanvasElement::GetIsOpaque()
return mCurrentContext->GetIsOpaque();
}
return GetOpaqueAttr();
}
bool
HTMLCanvasElement::GetOpaqueAttr()
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
}
@ -1118,16 +1031,51 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (!mCurrentContext)
return nullptr;
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
// data key for retained LayerManagers managed by FrameLayerBuilder.
// We don't much care about what value in it, so just assign a dummy
// value for it.
static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
if (mCurrentContext) {
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
}
if (mOffscreenCanvas) {
if (aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
nsRefPtr<CanvasLayer> ret = aOldLayer;
return ret.forget();
}
nsRefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
if (!layer) {
NS_WARNING("CreateCanvasLayer failed!");
return nullptr;
}
LayerUserData* userData = nullptr;
layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
layer->SetAsyncRenderer(GetAsyncCanvasRenderer());
layer->Updated();
return layer.forget();
}
return nullptr;
}
bool
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager *aManager)
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
{
return !mCurrentContext || mCurrentContext->ShouldForceInactiveLayer(aManager);
if (mCurrentContext) {
return mCurrentContext->ShouldForceInactiveLayer(aManager);
}
if (mOffscreenCanvas) {
// TODO: We should handle offscreen canvas case.
return false;
}
return true;
}
void
@ -1253,6 +1201,92 @@ HTMLCanvasElement::GetAsyncCanvasRenderer()
return mAsyncCanvasRenderer;
}
void
HTMLCanvasElement::OnVisibilityChange()
{
if (OwnerDoc()->Hidden()) {
return;
}
if (mOffscreenCanvas) {
class Runnable final : public nsCancelableRunnable
{
public:
explicit Runnable(AsyncCanvasRenderer* aRenderer)
: mRenderer(aRenderer)
{}
NS_IMETHOD Run()
{
if (mRenderer && mRenderer->mContext) {
mRenderer->mContext->OnVisibilityChange();
}
return NS_OK;
}
void Revoke()
{
mRenderer = nullptr;
}
private:
nsRefPtr<AsyncCanvasRenderer> mRenderer;
};
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
if (mAsyncCanvasRenderer->mActiveThread) {
mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
}
return;
}
if (mCurrentContext) {
mCurrentContext->OnVisibilityChange();
}
}
void
HTMLCanvasElement::OnMemoryPressure()
{
if (mOffscreenCanvas) {
class Runnable final : public nsCancelableRunnable
{
public:
explicit Runnable(AsyncCanvasRenderer* aRenderer)
: mRenderer(aRenderer)
{}
NS_IMETHOD Run()
{
if (mRenderer && mRenderer->mContext) {
mRenderer->mContext->OnMemoryPressure();
}
return NS_OK;
}
void Revoke()
{
mRenderer = nullptr;
}
private:
nsRefPtr<AsyncCanvasRenderer> mRenderer;
};
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
if (mAsyncCanvasRenderer->mActiveThread) {
mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
}
return;
}
if (mCurrentContext) {
mCurrentContext->OnMemoryPressure();
}
}
/* static */ void
HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
{

View File

@ -8,12 +8,15 @@
#include "mozilla/Attributes.h"
#include "mozilla/WeakPtr.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMHTMLCanvasElement.h"
#include "nsIObserver.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsSize.h"
#include "nsError.h"
#include "mozilla/dom/CanvasRenderingContextHelper.h"
#include "mozilla/gfx/Rect.h"
class nsICanvasRenderingContextInternal;
@ -21,6 +24,8 @@ class nsITimerCallback;
namespace mozilla {
class WebGLContext;
namespace layers {
class AsyncCanvasRenderer;
class CanvasLayer;
@ -36,14 +41,33 @@ class CanvasCaptureMediaStream;
class File;
class FileCallback;
class HTMLCanvasPrintState;
class OffscreenCanvas;
class PrintCallback;
class RequestedFrameRefreshObserver;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2
// Listen visibilitychange and memory-pressure event and inform
// context when event is fired.
class HTMLCanvasElementObserver final : public nsIObserver
, public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIDOMEVENTLISTENER
explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement);
void Destroy();
void RegisterVisibilityChangeEvent();
void UnregisterVisibilityChangeEvent();
void RegisterMemoryPressureEvent();
void UnregisterMemoryPressureEvent();
private:
~HTMLCanvasElementObserver();
HTMLCanvasElement* mElement;
};
/*
@ -85,7 +109,8 @@ protected:
};
class HTMLCanvasElement final : public nsGenericHTMLElement,
public nsIDOMHTMLCanvasElement
public nsIDOMHTMLCanvasElement,
public CanvasRenderingContextHelper
{
enum {
DEFAULT_CANVAS_WIDTH = 300,
@ -118,6 +143,11 @@ public:
}
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
{
if (mOffscreenCanvas) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
SetUnsignedIntAttr(nsGkAtoms::height, aHeight, aRv);
}
uint32_t Width()
@ -126,30 +156,45 @@ public:
}
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
{
if (mOffscreenCanvas) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
SetUnsignedIntAttr(nsGkAtoms::width, aWidth, aRv);
}
already_AddRefed<nsISupports>
virtual already_AddRefed<nsISupports>
GetContext(JSContext* aCx, const nsAString& aContextId,
JS::Handle<JS::Value> aContextOptions,
ErrorResult& aRv);
ErrorResult& aRv) override;
void ToDataURL(JSContext* aCx, const nsAString& aType,
JS::Handle<JS::Value> aParams,
nsAString& aDataURL, ErrorResult& aRv)
{
aRv = ToDataURL(aType, aParams, aCx, aDataURL);
}
void ToBlob(JSContext* aCx,
FileCallback& aCallback,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
bool MozOpaque() const
{
return GetBoolAttr(nsGkAtoms::moz_opaque);
}
void SetMozOpaque(bool aValue, ErrorResult& aRv)
{
if (mOffscreenCanvas) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
}
already_AddRefed<File> MozGetAsFile(const nsAString& aName,
@ -206,6 +251,7 @@ public:
* across its entire area.
*/
bool GetIsOpaque();
virtual bool GetOpaqueAttr() override;
virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
@ -284,6 +330,10 @@ public:
nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
void OnVisibilityChange();
void OnMemoryPressure();
static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
protected:
@ -291,14 +341,11 @@ protected:
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsIntSize GetWidthHeight();
virtual nsIntSize GetWidthHeight() override;
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType) override;
nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
nsresult ParseParams(JSContext* aCx,
const nsAString& aType,
const JS::Value& aEncoderOptions,
nsAString& aParams,
bool* usingCustomParseOptions);
nsresult ExtractData(nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream);
@ -313,14 +360,14 @@ protected:
AsyncCanvasRenderer* GetAsyncCanvasRenderer();
CanvasContextType mCurrentContextType;
nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
nsRefPtr<PrintCallback> mPrintCallback;
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
nsRefPtr<HTMLCanvasPrintState> mPrintState;
nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners;
nsRefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver;
nsRefPtr<AsyncCanvasRenderer> mAsyncCanvasRenderer;
nsRefPtr<OffscreenCanvas> mOffscreenCanvas;
nsRefPtr<HTMLCanvasElementObserver> mContextObserver;
public:
// Record whether this canvas should be write-only or not.

View File

@ -46,6 +46,13 @@ partial interface HTMLCanvasElement {
CanvasCaptureMediaStream captureStream(optional double frameRate);
};
// For OffscreenCanvas
// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
partial interface HTMLCanvasElement {
[Pref="gfx.offscreencanvas.enabled", Throws]
OffscreenCanvas transferControlToOffscreen();
};
[ChromeOnly]
interface MozCanvasPrintState
{

View File

@ -0,0 +1,28 @@
/* -*- 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://wiki.whatwg.org/wiki/OffscreenCanvas
*
* Current implementation focus on transfer canvas from main thread to worker.
* So there are some spec doesn't implement, such as [Constructor], toBlob() and
* transferToImageBitmap in OffscreenCanvas. Bug 1172796 will implement
* remaining spec.
*/
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
interface OffscreenCanvas : EventTarget {
[Pure, SetterThrows]
attribute unsigned long width;
[Pure, SetterThrows]
attribute unsigned long height;
[Throws]
nsISupports? getContext(DOMString contextId,
optional any contextOptions = null);
};
// OffscreenCanvas implements Transferable;

View File

@ -45,24 +45,38 @@ dictionary WebGLContextAttributes {
boolean failIfMajorPerformanceCaveat = false;
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLBuffer {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLFramebuffer {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLProgram {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLRenderbuffer {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLShader {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLTexture {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLUniformLocation {
};
@ -70,18 +84,24 @@ interface WebGLUniformLocation {
interface WebGLVertexArrayObjectOES {
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLActiveInfo {
readonly attribute GLint size;
readonly attribute GLenum type;
readonly attribute DOMString name;
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLShaderPrecisionFormat {
readonly attribute GLint rangeMin;
readonly attribute GLint rangeMax;
readonly attribute GLint precision;
};
[Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
interface WebGLRenderingContext {
/* ClearBufferMask */
@ -504,7 +524,7 @@ interface WebGLRenderingContext {
const GLenum BROWSER_DEFAULT_WEBGL = 0x9244;
// The canvas might actually be null in some cases, apparently.
readonly attribute HTMLCanvasElement? canvas;
readonly attribute (HTMLCanvasElement or OffscreenCanvas)? canvas;
readonly attribute GLsizei drawingBufferWidth;
readonly attribute GLsizei drawingBufferHeight;
@ -766,6 +786,14 @@ interface WebGLRenderingContext {
void viewport(GLint x, GLint y, GLsizei width, GLsizei height);
};
// For OffscreenCanvas
// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
[Exposed=(Window,Worker)]
partial interface WebGLRenderingContext {
[Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
void commit();
};
/*[Constructor(DOMString type, optional WebGLContextEventInit eventInit)]
interface WebGLContextEvent : Event {
readonly attribute DOMString statusMessage;

View File

@ -344,6 +344,7 @@ WEBIDL_FILES = [
'OfflineAudioCompletionEvent.webidl',
'OfflineAudioContext.webidl',
'OfflineResourceList.webidl',
'OffscreenCanvas.webidl',
'OscillatorNode.webidl',
'PaintRequest.webidl',
'PaintRequestList.webidl',

View File

@ -5,6 +5,7 @@
#include "GLLibraryEGL.h"
#include "gfxCrashReporterUtils.h"
#include "gfxUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Assertions.h"
#include "nsDirectoryServiceDefs.h"
@ -128,7 +129,9 @@ static bool
IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
{
int32_t angleSupport;
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &angleSupport);
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
nsIGfxInfo::FEATURE_WEBGL_ANGLE,
&angleSupport);
return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
}

View File

@ -9,8 +9,10 @@
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/RefPtr.h" // for nsAutoPtr, nsRefPtr, etc
#include "nsCOMPtr.h" // for nsCOMPtr
class nsICanvasRenderingContextInternal;
class nsIThread;
namespace mozilla {
@ -82,6 +84,7 @@ public:
// canvas' surface texture destructor will deref and destroy it too early
RefPtr<gl::GLContext> mGLContext;
nsCOMPtr<nsIThread> mActiveThread;
private:
virtual ~AsyncCanvasRenderer();

View File

@ -84,6 +84,22 @@ public:
}
};
class AppendAppNotesRunnable : public nsCancelableRunnable {
public:
explicit AppendAppNotesRunnable(nsAutoCString aFeatureStr)
: mFeatureString(aFeatureStr)
{
}
NS_IMETHOD Run() override {
CrashReporter::AppendAppNotesToCrashReport(mFeatureString);
return NS_OK;
}
private:
nsCString mFeatureString;
};
void
ScopedGfxFeatureReporter::WriteAppNote(char statusChar)
{
@ -102,7 +118,8 @@ ScopedGfxFeatureReporter::WriteAppNote(char statusChar)
if (!gFeaturesAlreadyReported->Contains(featureString)) {
gFeaturesAlreadyReported->AppendElement(featureString);
CrashReporter::AppendAppNotesToCrashReport(featureString);
nsCOMPtr<nsIRunnable> r = new AppendAppNotesRunnable(featureString);
NS_DispatchToMainThread(r);
}
}

View File

@ -248,6 +248,7 @@ private:
DECL_GFX_PREF(Live, "gfx.layerscope.port", LayerScopePort, int32_t, 23456);
// Note that "gfx.logging.level" is defined in Logging.h
DECL_GFX_PREF(Once, "gfx.logging.crash.length", GfxLoggingCrashLength, uint32_t, 6);
DECL_GFX_PREF(Live, "gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, bool, false);
DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false);
DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled", SurfaceTextureDetachEnabled, bool, true);
DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
@ -391,12 +392,32 @@ private:
DECL_GFX_PREF(Live, "test.mousescroll", MouseScrollTestingEnabled, bool, false);
DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay", UiClickHoldContextMenusDelay, int32_t, 500);
DECL_GFX_PREF(Once, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
DECL_GFX_PREF(Once, "webgl.angle.try-d3d11", WebGLANGLETryD3D11, bool, false);
// WebGL (for pref access from Worker threads)
DECL_GFX_PREF(Live, "webgl.all-angle-options", WebGLAllANGLEOptions, bool, false);
DECL_GFX_PREF(Live, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
DECL_GFX_PREF(Live, "webgl.angle.try-d3d11", WebGLANGLETryD3D11, bool, false);
DECL_GFX_PREF(Once, "webgl.angle.force-warp", WebGLANGLEForceWARP, bool, false);
DECL_GFX_PREF(Live, "webgl.bypass-shader-validation", WebGLBypassShaderValidator, bool, true);
DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground", WebGLCanLoseContextInForeground, bool, true);
DECL_GFX_PREF(Live, "webgl.default-no-alpha", WebGLDefaultNoAlpha, bool, false);
DECL_GFX_PREF(Live, "webgl.disable-angle", WebGLDisableANGLE, bool, false);
DECL_GFX_PREF(Live, "webgl.disable-extensions", WebGLDisableExtensions, bool, false);
DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
DECL_GFX_PREF(Live, "webgl.disabled", WebGLDisabled, bool, false);
DECL_GFX_PREF(Live, "webgl.enable-draft-extensions", WebGLDraftExtensionsEnabled, bool, false);
DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions", WebGLPrivilegedExtensionsEnabled, bool, false);
DECL_GFX_PREF(Live, "webgl.force-enabled", WebGLForceEnabled, bool, false);
DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
DECL_GFX_PREF(Live, "webgl.max-warnings-per-context", WebGLMaxWarningsPerContext, uint32_t, 32);
DECL_GFX_PREF(Live, "webgl.min_capability_mode", WebGLMinCapabilityMode, bool, false);
DECL_GFX_PREF(Live, "webgl.msaa-force", WebGLForceMSAA, bool, false);
DECL_GFX_PREF(Live, "webgl.prefer-16bpp", WebGLPrefer16bpp, bool, false);
DECL_GFX_PREF(Live, "webgl.restore-context-when-visible", WebGLRestoreWhenVisible, bool, true);
// WARNING:
// Please make sure that you've added your new preference to the list above in alphabetical order.

View File

@ -12,6 +12,8 @@
#include "gfxDrawable.h"
#include "imgIEncoder.h"
#include "mozilla/Base64.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/gfx/Logging.h"
@ -21,6 +23,7 @@
#include "nsComponentManagerUtils.h"
#include "nsIClipboardHelper.h"
#include "nsIFile.h"
#include "nsIGfxInfo.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsRegion.h"
@ -1543,6 +1546,62 @@ gfxUtils::CopyAsDataURI(DrawTarget* aDT)
}
}
class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
{
public:
GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
const nsCOMPtr<nsIGfxInfo>& gfxInfo,
int32_t feature,
int32_t* status)
: WorkerMainThreadRunnable(workerPrivate)
, mGfxInfo(gfxInfo)
, mFeature(feature)
, mStatus(status)
, mNSResult(NS_OK)
{
}
bool MainThreadRun() override
{
if (mGfxInfo) {
mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mStatus);
}
return true;
}
nsresult GetNSResult() const
{
return mNSResult;
}
protected:
~GetFeatureStatusRunnable() {}
private:
nsCOMPtr<nsIGfxInfo> mGfxInfo;
int32_t mFeature;
int32_t* mStatus;
nsresult mNSResult;
};
/* static */ nsresult
gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
int32_t feature, int32_t* status)
{
if (!NS_IsMainThread()) {
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
nsRefPtr<GetFeatureStatusRunnable> runnable =
new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, status);
runnable->Dispatch(workerPrivate->GetJSContext());
return runnable->GetNSResult();
}
return gfxInfo->GetFeatureStatus(feature, status);
}
/* static */ bool
gfxUtils::DumpDisplayList() {
return gfxPrefs::LayoutDumpDisplayList();

View File

@ -16,6 +16,7 @@
class gfxASurface;
class gfxDrawable;
class nsIGfxInfo;
class nsIntRegion;
class nsIPresShell;
@ -280,6 +281,10 @@ public:
static nsCString GetAsDataURI(DrawTarget* aDT);
static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
int32_t feature,
int32_t* status);
/**
* Copy to the clipboard as a PNG encoded Data URL.
*/

View File

@ -271,6 +271,7 @@ GENERATED_FILES = [
]
LOCAL_INCLUDES += [
'/dom/workers',
'/dom/xml',
]

View File

@ -4196,6 +4196,8 @@ pref("webgl.angle.force-d3d11", false);
pref("webgl.angle.force-warp", false);
#endif
pref("gfx.offscreencanvas.enabled", false);
#ifdef MOZ_WIDGET_GONK
pref("gfx.gralloc.fence-with-readpixels", false);
#endif