Bug 1219985 - The canvas rendering context 2d should be opaque if either the moz-opaque attribute is set or if it has been initialized with alpha:false. r?jrmuizel
If the canvas is cleared by setting the width or height attributes, its
opaqueness should not be affected.
This patch keeps support for moz-opaque, and also keeps the behavior that
changing the moz-opaque attribute clears the canvas, even if this does not
affect the actual opaqueness of the canvas.
MozReview-Commit-ID: LOlsJxiP9kc
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1080,17 +1080,20 @@ static int32_t sMaxContexts = 0;
CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
: mRenderingMode(RenderingMode::OpenGLBackendMode)
, mCompositorBackend(aCompositorBackend)
// these are the default values from the Canvas spec
, mWidth(0), mHeight(0)
- , mZero(false), mOpaque(false)
+ , mZero(false)
+ , mOpaqueAttrValue(false)
+ , mContextAttributesHasAlpha(true)
+ , mOpaque(false)
, mResetLayer(true)
, mIPC(false)
, mIsSkiaGL(false)
, mHasPendingStableStateCallback(false)
, mDrawObserver(nullptr)
, mIsEntireFrameInvalid(false)
, mPredictManyRedrawCalls(false)
, mIsCapturedFrameInvalid(false)
@@ -1990,22 +1993,29 @@ CanvasRenderingContext2D::InitializeWith
// Cf comment in EnsureTarget
mTarget->PushClipRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
}
return NS_OK;
}
void
-CanvasRenderingContext2D::SetIsOpaque(bool aIsOpaque)
-{
- if (aIsOpaque != mOpaque) {
- mOpaque = aIsOpaque;
- ClearTarget();
- }
+CanvasRenderingContext2D::SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue)
+{
+ if (aOpaqueAttrValue != mOpaqueAttrValue) {
+ mOpaqueAttrValue = aOpaqueAttrValue;
+ UpdateIsOpaque();
+ }
+}
+
+void
+CanvasRenderingContext2D::UpdateIsOpaque()
+{
+ mOpaque = !mContextAttributesHasAlpha || mOpaqueAttrValue;
+ ClearTarget();
}
NS_IMETHODIMP
CanvasRenderingContext2D::SetIsIPC(bool aIsIPC)
{
if (aIsIPC != mIPC) {
mIPC = aIsIPC;
ClearTarget();
@@ -2038,19 +2048,18 @@ CanvasRenderingContext2D::SetContextOpti
// We want to lock into software, so remove the observer that
// may potentially change that...
RemoveDrawObserver();
mRenderingMode = RenderingMode::SoftwareBackendMode;
}
}
- if (!attributes.mAlpha) {
- SetIsOpaque(true);
- }
+ mContextAttributesHasAlpha = attributes.mAlpha;
+ UpdateIsOpaque();
return NS_OK;
}
UniquePtr<uint8_t[]>
CanvasRenderingContext2D::GetImageBuffer(int32_t* aFormat)
{
UniquePtr<uint8_t[]> ret;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -455,17 +455,17 @@ public:
{
EnsureTarget();
if (aOutAlphaType) {
*aOutAlphaType = (mOpaque ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
}
return mTarget->Snapshot();
}
- virtual void SetIsOpaque(bool aIsOpaque) override;
+ virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override;
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager) override;
bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
WebRenderCanvasData* aCanvasData) override;
@@ -614,16 +614,19 @@ protected:
// Returns whether a filter was successfully parsed.
bool ParseFilter(const nsAString& aString,
nsTArray<nsStyleFilter>& aFilterChain,
ErrorResult& aError);
// Returns whether the font was successfully updated.
bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
+ // Clears the target and updates mOpaque based on mOpaqueAttrValue and
+ // mContextAttributesHasAlpha.
+ void UpdateIsOpaque();
/**
* Creates the error target, if it doesn't exist
*/
static void EnsureErrorTarget();
/* This function ensures there is a writable pathbuilder available, this
* pathbuilder may be working in user space or in device space or
@@ -759,16 +762,27 @@ protected:
// Member vars
int32_t mWidth, mHeight;
// This is true when the canvas is valid, but of zero size, this requires
// specific behavior on some operations.
bool mZero;
+ // The two ways to set the opaqueness of the canvas.
+ // mOpaqueAttrValue: Whether the <canvas> element has the moz-opaque attribute
+ // set. Can change during the lifetime of the context. Non-standard, should
+ // hopefully go away soon.
+ // mContextAttributesHasAlpha: The standard way of setting canvas opaqueness.
+ // Set at context initialization time and never changes.
+ bool mOpaqueAttrValue;
+ bool mContextAttributesHasAlpha;
+
+ // Determines the context's opaqueness. Is computed from mOpaqueAttrValue and
+ // mContextAttributesHasAlpha in UpdateIsOpaque().
bool mOpaque;
// This is true when the next time our layer is retrieved we need to
// recreate it (i.e. our backing surface changed)
bool mResetLayer;
// This is needed for drawing in drawAsyncXULElement
bool mIPC;
// True if the current DrawTarget is using skia-gl, used so we can avoid
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -226,17 +226,17 @@ CanvasRenderingContextHelper::UpdateCont
{
if (!mCurrentContext)
return NS_OK;
nsIntSize sz = GetWidthHeight();
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
- currentContext->SetIsOpaque(GetOpaqueAttr());
+ currentContext->SetOpaqueValueFromOpaqueAttr(GetOpaqueAttr());
nsresult rv = currentContext->SetContextOptions(aCx, aNewContextOptions,
aRvForDictionaryInit);
if (NS_FAILED(rv)) {
mCurrentContext = nullptr;
return rv;
}
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -184,18 +184,19 @@ ImageBitmapRenderingContext::GetSurfaceS
if (surface->GetSize() != IntSize(mWidth, mHeight)) {
return MatchWithIntrinsicSize();
}
return surface.forget();
}
void
-ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
+ImageBitmapRenderingContext::SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue)
{
+ // ignored
}
bool
ImageBitmapRenderingContext::GetIsOpaque()
{
return false;
}
--- a/dom/canvas/ImageBitmapRenderingContext.h
+++ b/dom/canvas/ImageBitmapRenderingContext.h
@@ -61,17 +61,17 @@ public:
virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType) override;
- virtual void SetIsOpaque(bool aIsOpaque) override;
+ virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override;
virtual bool GetIsOpaque() override;
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager) override;
virtual void MarkContextClean() override;
NS_IMETHOD Redraw(const gfxRect& aDirty) override;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -375,17 +375,17 @@ public:
virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* out_format) override;
NS_IMETHOD GetInputStream(const char* mimeType,
const char16_t* encoderOptions,
nsIInputStream** out_stream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(gfxAlphaType* out_alphaType) override;
- virtual void SetIsOpaque(bool) override {};
+ virtual void SetOpaqueValueFromOpaqueAttr(bool) override {};
bool GetIsOpaque() override { return !mOptions.alpha; }
NS_IMETHOD SetContextOptions(JSContext* cx,
JS::Handle<JS::Value> options,
ErrorResult& aRvForDictionaryInit) override;
NS_IMETHOD SetIsIPC(bool) override {
return NS_ERROR_NOT_IMPLEMENTED;
}
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -121,21 +121,26 @@ public:
// This gets an Azure SourceSurface for the canvas, this will be a snapshot
// of the canvas at the time it was called.
// If premultAlpha is provided, then it assumed the callee can handle
// un-premultiplied surfaces, and *premultAlpha will be set to false
// if one is returned.
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(gfxAlphaType* out_alphaType = nullptr) = 0;
- // If this context is opaque, the backing store of the canvas should
+ // If this is called with true, the backing store of the canvas should
// be created as opaque; all compositing operators should assume the
- // dst alpha is always 1.0. If this is never called, the context
- // defaults to false (not opaque).
- virtual void SetIsOpaque(bool isOpaque) = 0;
+ // dst alpha is always 1.0. If this is never called, the context's
+ // opaqueness is determined by the context attributes that it's initialized
+ // with.
+ virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) = 0;
+
+ // Returns whether the context is opaque. This value can be based both on
+ // the value of the moz-opaque attribute and on the context's initialization
+ // attributes.
virtual bool GetIsOpaque() = 0;
// Invalidate this context and release any held resources, in preperation
// for possibly reinitializing with SetDimensions/InitializeWithSurface.
NS_IMETHOD Reset() = 0;
// Return the CanvasLayer for this context, creating
// one for the given layer manager if not available.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Basic transparent rendering</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: true });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Setting the canvas size should clear the canvas</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: true });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+c.width = 200;
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-3.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Basic rendering into a non-alpha canvas</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: false });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-4.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Setting the canvas size on a non-alpha should clear the canvas to opaque black</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: false });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+c.width = 200;
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-5.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Only the context attributes from the first getContext call should be respected.</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: true });
+ctx = c.getContext('2d', { alpha: false });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-6.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: moz-opaque should have the same effect as alpha:false</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200" moz-opaque="true"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: true });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-7.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Unsetting moz-opaque should clear the canvas</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200" moz-opaque="true"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: true });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+c.removeAttribute("moz-opaque");
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-8.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Unsetting moz-opaque should clear the canvas even if the canvas has been created with alpha:false, and the canvas should stay opaque.</title>
+
+<div style="width: 200px; height: 200px; background-color: red;">
+ <canvas id="c" width="200" height="200" moz-opaque="true"></canvas>
+</div>
+
+<script>
+
+var c = document.getElementById('c');
+var ctx = c.getContext('2d', { alpha: false });
+ctx.fillStyle = 'green';
+ctx.fillRect(50, 50, 100, 100);
+c.removeAttribute("moz-opaque");
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-ref-opaque-clear.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Reference for an opaque canvas with nothing rendered in it</title>
+
+<div style="width: 200px; height: 200px; background: black"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-ref-opaque-with-rendering.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Reference for an opaque canvas with a green square rendered in it</title>
+
+<div style="width: 200px; height: 200px; background: black; display: flex;">
+ <div style="width: 100px; height: 100px; margin: auto; background: green;"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-ref-transparent-clear.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Reference for a regular (non-opaque) canvas with nothing rendered in it. The red background behind the canvas is visible.</title>
+
+<div style="width: 200px; height: 200px; background: red"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1219985-ref-transparent-with-rendering.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1219985: Reference for a regular (non-opaque) canvas with a green square rendered in it. The red background behind the canvas is visible.</title>
+
+<div style="width: 200px; height: 200px; background: red; display: flex;">
+ <div style="width: 100px; height: 100px; margin: auto; background: green;"></div>
+</div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1954,16 +1954,24 @@ fuzzy(1,74) fails-if(Android||gtkWidget)
== 1202512-1.html 1202512-1-ref.html
fuzzy-if(skiaContent,1,1) == 1202512-2.html 1202512-2-ref.html
!= 1207326-1.html about:blank
== 1209603-1.html 1209603-1-ref.html
== 1209994-1.html 1209994-1-ref.html
== 1209994-2.html 1209994-2-ref.html
== 1209994-3.html 1209994-3-ref.html
== 1209994-4.html 1209994-4-ref.html
+== 1219985-1.html 1219985-ref-transparent-with-rendering.html
+== 1219985-2.html 1219985-ref-transparent-clear.html
+== 1219985-3.html 1219985-ref-opaque-with-rendering.html
+== 1219985-4.html 1219985-ref-opaque-clear.html
+== 1219985-5.html 1219985-ref-transparent-with-rendering.html
+== 1219985-6.html 1219985-ref-opaque-with-rendering.html
+== 1219985-7.html 1219985-ref-transparent-clear.html
+== 1219985-8.html 1219985-ref-opaque-clear.html
== 1222226-1.html 1222226-1-ref.html
pref(layout.css.overflow-clip-box.enabled,true) == 1226278.html 1226278-ref.html
== 1230466.html about:blank
random-if(gtkWidget) != 1238243-1.html 1238243-1-notref.html # may fail on Linux, depending on Korean fonts available
== 1238243-2.html 1238243-2-ref.html
fuzzy(100,2000) == 1239564.html 1239564-ref.html
== 1242172-1.html 1242172-1-ref.html
fuzzy-if(webrender,0-2,0-2601) == 1242172-2.html 1242172-2-ref.html