b=539771; Add support for context attribs to canvas; r=jmuizelaar
authorVladimir Vukicevic <vladimir@pobox.com>
Tue, 16 Nov 2010 20:33:03 -0800
changeset 57631 941c3d14e14d11a7b2f4160d5190471a11f8b820
parent 57630 0f43632752fe99e5f04aeb6ad31c06e8b609bbe6
child 57632 e5a34d3081b7da80a6769d0dd00505f5efa670de
push id17016
push uservladimir@mozilla.com
push dateWed, 17 Nov 2010 04:34:14 +0000
treeherdermozilla-central@69844c0f4fd1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmuizelaar
bugs539771
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=539771; Add support for context attribs to canvas; r=jmuizelaar
content/canvas/public/nsICanvasRenderingContextInternal.h
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html
content/html/content/public/nsHTMLCanvasElement.h
content/html/content/src/nsHTMLCanvasElement.cpp
dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
gfx/thebes/GLContext.cpp
gfx/thebes/GLContextProviderWGL.cpp
js/src/xpconnect/src/dom_quickstubs.qsconf
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -45,16 +45,17 @@
 
 // {EC90F32E-7848-4819-A1E3-02E64C682A72}
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
 { 0xec90f32e, 0x7848, 0x4819, { 0xa1, 0xe3, 0x2, 0xe6, 0x4c, 0x68, 0x2a, 0x72 } }
 
 class nsHTMLCanvasElement;
 class gfxContext;
 class gfxASurface;
+class nsIPropertyBag;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
 class LayerManager;
 }
 namespace ipc {
 class Shmem;
@@ -110,16 +111,24 @@ public:
   virtual already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                        LayerManager *aManager) = 0;
 
   virtual void MarkContextClean() = 0;
 
   // Redraw the dirty rectangle of this canvas.
   NS_IMETHOD Redraw(const gfxRect &dirty) = 0;
 
+  // Passes a generic nsIPropertyBag options argument, along with the
+  // previous one, if any.  Optional.
+  NS_IMETHOD SetContextOptions(nsIPropertyBag *aNewOptions) { return NS_OK; }
+
+  //
+  // shmem support
+  //
+
   // If this context can be set to use Mozilla's Shmem segments as its backing
   // store, this will set it to that state. Note that if you have drawn
   // anything into this canvas before changing the shmem state, it will be
   // lost.
   NS_IMETHOD SetIsIPC(PRBool isIPC) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasRenderingContextInternal,
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -43,16 +43,19 @@
 #include "nsIPrefService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsDOMError.h"
 #include "nsIGfxInfo.h"
 
+#include "nsIPropertyBag.h"
+#include "nsIVariant.h"
+
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 
 #include "GLContextProvider.h"
 
@@ -83,16 +86,17 @@ WebGLContext::WebGLContext()
     : mCanvasElement(nsnull),
       gl(nsnull)
 {
     mWidth = mHeight = 0;
     mGeneration = 0;
     mInvalidated = PR_FALSE;
     mResetLayer = PR_TRUE;
     mVerbose = PR_FALSE;
+    mOptionsFrozen = PR_FALSE;
 
     mActiveTexture = 0;
     mSynthesizedGLError = LOCAL_GL_NO_ERROR;
     mPixelStoreFlipY = PR_FALSE;
     mPixelStorePremultiplyAlpha = PR_FALSE;
 
     mShaderValidation = PR_TRUE;
 
@@ -261,16 +265,77 @@ WebGLContext::SetCanvasElement(nsHTMLCan
     if (aParentCanvas && !SafeToCreateCanvas3DContext(aParentCanvas))
         return NS_ERROR_FAILURE;
 
     mCanvasElement = aParentCanvas;
 
     return NS_OK;
 }
 
+static bool
+GetBoolFromPropertyBag(nsIPropertyBag *bag, const char *propName, bool *boolResult)
+{
+    nsCOMPtr<nsIVariant> vv;
+    PRBool bv;
+
+    nsresult rv = bag->GetProperty(NS_ConvertASCIItoUTF16(propName), getter_AddRefs(vv));
+    if (NS_FAILED(rv) || !vv)
+        return false;
+
+    rv = vv->GetAsBool(&bv);
+    if (NS_FAILED(rv))
+        return false;
+
+    *boolResult = bv ? true : false;
+    return true;
+}
+
+NS_IMETHODIMP
+WebGLContext::SetContextOptions(nsIPropertyBag *aOptions)
+{
+    if (!aOptions)
+        return NS_OK;
+
+    WebGLContextOptions newOpts;
+
+    // defaults are: yes: depth, alpha, premultipliedAlpha; no: stencil
+    if (!GetBoolFromPropertyBag(aOptions, "stencil", &newOpts.stencil))
+        newOpts.stencil = false;
+
+    if (!GetBoolFromPropertyBag(aOptions, "depth", &newOpts.depth))
+        newOpts.depth = true;
+
+    if (!GetBoolFromPropertyBag(aOptions, "alpha", &newOpts.alpha))
+        newOpts.alpha = true;
+
+    if (!GetBoolFromPropertyBag(aOptions, "premultipliedAlpha", &newOpts.premultipliedAlpha))
+        newOpts.premultipliedAlpha = true;
+
+    GetBoolFromPropertyBag(aOptions, "antialiasHint", &newOpts.antialiasHint);
+
+    // enforce that if stencil is specified, we also give back depth
+    newOpts.depth |= newOpts.stencil;
+
+    LogMessage("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d\n",
+               newOpts.antialiasHint ? 1 : 0,
+               newOpts.stencil ? 1 : 0,
+               newOpts.depth ? 1 : 0,
+               newOpts.alpha ? 1 : 0,
+               newOpts.premultipliedAlpha ? 1 : 0);
+
+    if (mOptionsFrozen && newOpts != mOptions) {
+        // Error if the options are already frozen, and the ones that were asked for
+        // aren't the same as what they were originally.
+        return NS_ERROR_FAILURE;
+    }
+
+    mOptions = newOpts;
+    return NS_OK;
+}
+
 NS_IMETHODIMP
 WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
 {
     if (mWidth == width && mHeight == height)
         return NS_OK;
 
     // If we already have a gl context, then we just need to resize
     // FB0.
@@ -295,18 +360,36 @@ WebGLContext::SetDimensions(PRInt32 widt
     if (!(mGeneration+1).valid())
         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
 
     // We're going to recreate our context, so make sure we clean up
     // after ourselves.
     DestroyResourcesAndContext();
 
     gl::ContextFormat format(gl::ContextFormat::BasicRGBA32);
-    format.depth = 16;
-    format.minDepth = 1;
+    if (mOptions.depth) {
+        format.depth = 24;
+        format.minDepth = 16;
+    }
+
+    if (mOptions.stencil) {
+        format.stencil = 8;
+        format.minStencil = 8;
+    }
+
+    if (!mOptions.alpha) {
+        // Select 565; we won't/shouldn't hit this on the desktop,
+        // but let mobile know we're ok with it.
+        format.red = 5;
+        format.green = 6;
+        format.blue = 5;
+
+        format.alpha = 0;
+        format.minAlpha = 0;
+    }
 
     nsCOMPtr<nsIPrefBranch> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
     NS_ENSURE_TRUE(prefService != nsnull, NS_ERROR_FAILURE);
 
     PRBool verbose = PR_FALSE;
     prefService->GetBoolPref("webgl.verbose", &verbose);
     mVerbose = verbose;
 
@@ -416,16 +499,17 @@ WebGLContext::SetDimensions(PRInt32 widt
 
 #ifdef DEBUG
     printf_stderr ("--- WebGL context created: %p\n", gl.get());
 #endif
 
     mWidth = width;
     mHeight = height;
     mResetLayer = PR_TRUE;
+    mOptionsFrozen = PR_TRUE;
 
     // increment the generation number
     ++mGeneration;
 
 #if 0
     if (mGeneration > 0) {
         // XXX dispatch context lost event
     }
@@ -584,29 +668,63 @@ WebGLContext::GetCanvasLayer(CanvasLayer
 
     if (native_surface) {
         data.mSurface = static_cast<gfxASurface*>(native_surface);
     } else {
         data.mGLContext = gl.get();
     }
 
     data.mSize = nsIntSize(mWidth, mHeight);
-    data.mGLBufferIsPremultiplied = PR_FALSE;
+    data.mGLBufferIsPremultiplied = mOptions.premultipliedAlpha ? PR_TRUE : PR_FALSE;
 
     canvasLayer->Initialize(data);
     PRUint32 flags = gl->CreationFormat().alpha == 0 ? Layer::CONTENT_OPAQUE : 0;
     canvasLayer->SetContentFlags(flags);
     canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
 
     mInvalidated = PR_FALSE;
     mResetLayer = PR_FALSE;
 
     return canvasLayer.forget().get();
 }
 
+NS_IMETHODIMP
+WebGLContext::GetContextAttributes(jsval *aResult)
+{
+    JSContext *cx = nsContentUtils::GetCurrentJSContext();
+    if (!cx)
+        return NS_ERROR_FAILURE;
+
+    JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
+    if (!obj)
+        return NS_ERROR_FAILURE;
+
+    *aResult = OBJECT_TO_JSVAL(obj);
+
+    gl::ContextFormat cf = gl->ActualFormat();
+
+    if (!JS_DefineProperty(cx, obj, "alpha", cf.alpha > 0 ? JSVAL_TRUE : JSVAL_FALSE,
+                           NULL, NULL, JSPROP_ENUMERATE) ||
+        !JS_DefineProperty(cx, obj, "depth", cf.depth > 0 ? JSVAL_TRUE : JSVAL_FALSE,
+                           NULL, NULL, JSPROP_ENUMERATE) ||
+        !JS_DefineProperty(cx, obj, "stencil", cf.stencil > 0 ? JSVAL_TRUE : JSVAL_FALSE,
+                           NULL, NULL, JSPROP_ENUMERATE) ||
+        !JS_DefineProperty(cx, obj, "antialias", JSVAL_FALSE,
+                           NULL, NULL, JSPROP_ENUMERATE) ||
+        !JS_DefineProperty(cx, obj, "premultipliedAlpha",
+                           mOptions.premultipliedAlpha ? JSVAL_TRUE : JSVAL_FALSE,
+                           NULL, NULL, JSPROP_ENUMERATE))
+    {
+        *aResult = JSVAL_VOID;
+        return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+}
+
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextWebGL)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextWebGL)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebGLContext)
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -62,16 +62,17 @@
 
 #include "CheckedInt.h"
 
 #define UNPACK_FLIP_Y_WEBGL            0x9240
 #define UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241
 #define CONTEXT_LOST_WEBGL             0x9242
 
 class nsIDocShell;
+class nsIPropertyBag;
 
 namespace mozilla {
 
 class WebGLTexture;
 class WebGLBuffer;
 class WebGLProgram;
 class WebGLShader;
 class WebGLFramebuffer;
@@ -272,16 +273,49 @@ struct WebGLVertexAttribData {
     }
 
     GLuint actualStride() const {
         if (stride) return stride;
         return size * componentSize();
     }
 };
 
+struct WebGLContextOptions {
+    // these are defaults
+    WebGLContextOptions()
+        : alpha(true), depth(true), stencil(false),
+          premultipliedAlpha(true), antialiasHint(false)
+    { }
+
+    bool operator==(const WebGLContextOptions& other) const {
+        return
+            alpha == other.alpha &&
+            depth == other.depth &&
+            stencil == other.stencil &&
+            premultipliedAlpha == other.premultipliedAlpha &&
+            antialiasHint == other.antialiasHint;
+    }
+
+    bool operator!=(const WebGLContextOptions& other) const {
+        return
+            alpha != other.alpha ||
+            depth != other.depth ||
+            stencil != other.stencil ||
+            premultipliedAlpha != other.premultipliedAlpha ||
+            antialiasHint != other.antialiasHint;
+    }
+
+    bool alpha;
+    bool depth;
+    bool stencil;
+
+    bool premultipliedAlpha;
+    bool antialiasHint;
+};
+
 class WebGLContext :
     public nsICanvasRenderingContextWebGL,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference
 {
 public:
     WebGLContext();
     virtual ~WebGLContext();
@@ -300,16 +334,18 @@ public:
     NS_IMETHOD Reset()
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter f);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; };
+    NS_IMETHOD SetContextOptions(nsIPropertyBag *aOptions);
+
     NS_IMETHOD SetIsIPC(PRBool b) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack,
                     PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(PRUint32 nativeID,
                     PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
@@ -355,19 +391,22 @@ protected:
         return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get());
     }
 
     nsRefPtr<gl::GLContext> gl;
 
     PRInt32 mWidth, mHeight;
     CheckedUint32 mGeneration;
 
+    WebGLContextOptions mOptions;
+
     PRPackedBool mInvalidated;
     PRPackedBool mResetLayer;
     PRPackedBool mVerbose;
+    PRPackedBool mOptionsFrozen;
 
     WebGLuint mActiveTexture;
     WebGLenum mSynthesizedGLError;
 
     // whether shader validation is supported
     PRBool mShaderValidation;
 
     // some GL constants
@@ -724,23 +763,24 @@ public:
 
     PRBool Deleted() { return mDeleted; }
     WebGLuint GLName() { return mName; }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLTEXTURE
 
 protected:
+    friend class WebGLContext;
+    friend class WebGLFramebuffer;
+
     PRBool mDeleted;
     WebGLuint mName;
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-/////// everything below that point is only used for the texture completeness/npot business
-/////// (sections 3.7.10 and 3.8.2 in GL ES 2.0.24 spec)
-////////////////////////////////////////////////////////////////////////////////////////////////////
+    // we store information about the various images that are part of
+    // this texture (cubemap faces, mipmap levels)
 
     struct ImageInfo {
         ImageInfo() : mWidth(0), mHeight(0), mFormat(0), mType(0), mIsDefined(PR_FALSE) {}
         PRBool operator==(const ImageInfo& a) const {
             return mWidth == a.mWidth && mHeight == a.mHeight &&
                    mFormat == a.mFormat && mType == a.mType;
         }
         PRBool operator!=(const ImageInfo& a) const {
@@ -1323,30 +1363,33 @@ class WebGLFramebuffer :
     public WebGLContextBoundObject
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLFRAMEBUFFER_PRIVATE_IID)
 
     WebGLFramebuffer(WebGLContext *context, WebGLuint name) :
         WebGLContextBoundObject(context),
         mName(name), mDeleted(PR_FALSE),
+        mColorAttachment0HasAlpha(PR_FALSE),
         mHasDepthAttachment(PR_FALSE),
         mHasStencilAttachment(PR_FALSE),
         mHasDepthStencilAttachment(PR_FALSE)
     { }
 
     void Delete() {
         if (mDeleted)
             return;
         ZeroOwners();
         mDeleted = PR_TRUE;
     }
     PRBool Deleted() { return mDeleted; }
     WebGLuint GLName() { return mName; }
 
+    PRBool ColorAttachment0HasAlpha() { return mColorAttachment0HasAlpha; }
+
     nsresult FramebufferRenderbuffer(WebGLenum target,
                                      WebGLenum attachment,
                                      WebGLenum rbtarget,
                                      nsIWebGLRenderbuffer *rbobj)
     {
         WebGLuint renderbuffername;
         PRBool isNull;
         WebGLRenderbuffer *wrb;
@@ -1400,25 +1443,30 @@ public:
             }
             if (!isNull) {
                 if (wrb->mInternalFormat != LOCAL_GL_RGBA4 &&
                     wrb->mInternalFormat != LOCAL_GL_RGB565 &&
                     wrb->mInternalFormat != LOCAL_GL_RGB5_A1)
                 {
                     return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
                 }
+
+                // ReadPixels needs alpha and size information, but only
+                // for COLOR_ATTACHMENT0
+                if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) {
+                    setDimensions(wrb);
+                    mColorAttachment0HasAlpha = InternalFormatHasAlpha(wrb->mInternalFormat);
+                } else {
+                    mColorAttachment0HasAlpha = PR_FALSE;
+                }
             }
             mColorRenderbufferAttachment = wrb;
             break;
         }
 
-        // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
-        if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
-            setDimensions(wrb);
-
         mContext->MakeContextCurrent();
         mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
 
         return NS_OK;
     }
 
     nsresult FramebufferTexture2D(WebGLenum target,
                                   WebGLenum attachment,
@@ -1455,24 +1503,36 @@ public:
                           "only be renderbuffers, not textures, as there is no suitable texture format.");
             break;
         default:
             if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
                  attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mMaxFramebufferColorAttachments))
             {
                 return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
             }
-            // nothing to do for color buffers. all textures have a color-renderable format.
+
+            // keep data for readPixels, function only uses COLOR_ATTACHMENT0
+            if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) {
+                setDimensions(wtex);
+
+                if (wtex) {
+                    const WebGLTexture::ImageInfo& ia = wtex->ImageInfoAt
+                        (level, textarget == LOCAL_GL_TEXTURE_2D
+                         ? 0
+                         : textarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X);
+                    mColorAttachment0HasAlpha = InternalFormatHasAlpha(ia.mFormat);
+                } else {
+                    mColorAttachment0HasAlpha = PR_FALSE;
+                }
+            }
+
+            // nothing else to do for color buffers. all textures have a color-renderable format.
             break;
         }
 
-        // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
-        if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
-            setDimensions(wtex);
-
         mContext->MakeContextCurrent();
         mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
 
         return NS_OK;
     }
 
     // implement inline, as it's performance critical (called by draw-functions).
     // the generic case for which we're optimizing is the case where there's nothing to initialize.
@@ -1496,16 +1556,24 @@ public:
     NS_DECL_NSIWEBGLFRAMEBUFFER
 
     PRBool HasConflictingAttachments() const {
         return int(mHasDepthAttachment) +
                int(mHasStencilAttachment) +
                int(mHasDepthStencilAttachment) > 1;
     }
 
+    static PRBool InternalFormatHasAlpha(WebGLenum aInternalFormat) {
+        return
+            aInternalFormat == LOCAL_GL_RGBA ||
+            aInternalFormat == LOCAL_GL_ALPHA ||
+            aInternalFormat == LOCAL_GL_RGBA4 ||
+            aInternalFormat == LOCAL_GL_RGB5_A1;
+    }
+
 protected:
 
     // protected because WebGLContext should only call InitializeRenderbuffers
     void InitializeRenderbuffers()
     {
         mContext->MakeContextCurrent();
 
         if (mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE)
@@ -1600,17 +1668,18 @@ protected:
         return mHasDepthAttachment || mHasDepthStencilAttachment;
     }
 
     PRBool HasStencilBuffer() const {
         return mHasStencilAttachment || mHasDepthStencilAttachment;
     }
 
     WebGLuint mName;
-    PRBool mDeleted;
+    PRPackedBool mDeleted;
+    PRPackedBool mColorAttachment0HasAlpha;
 
     // we only store pointers to attached renderbuffers, not to attached textures, because
     // we will only need to initialize renderbuffers. Textures are already initialized.
     nsRefPtr<WebGLRenderbuffer> mColorRenderbufferAttachment;
     nsRefPtr<WebGLRenderbuffer> mDepthOrStencilRenderbufferAttachment;
 
     // these boolean values keep track of all attachments: renderbuffers and textures.
     // thus they are not at all redundant with the above member pointers.
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -118,28 +118,27 @@ WebGLProgram::GetUniformLocationObject(G
         return nsnull;
     }
 
     nsRefPtr<WebGLUniformLocation> loc = new WebGLUniformLocation(mContext, this, glLocation);
     mMapUniformLocations.Put(glLocation, loc);
     return loc.forget();
 }
 
+static PRBool
+InternalFormatHasAlpha(WebGLenum aInternalFormat) {
+    return aInternalFormat == LOCAL_GL_RGBA4 ||
+        aInternalFormat == LOCAL_GL_RGB5_A1;
+}
+
 //
 //  WebGL API
 //
 
 
-/* void present (); */
-NS_IMETHODIMP
-WebGLContext::Present()
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 /* void GlActiveTexture (in GLenum texture); */
 NS_IMETHODIMP
 WebGLContext::ActiveTexture(WebGLenum texture)
 {
     if (texture < LOCAL_GL_TEXTURE0 || texture >= LOCAL_GL_TEXTURE0+mBound2DTextures.Length())
         return ErrorInvalidEnum("ActiveTexture: texture unit %d out of range (0..%d)",
                                 texture, mBound2DTextures.Length()-1);
 
@@ -2480,19 +2479,19 @@ WebGLContext::ReadPixels_base(WebGLint x
       case LOCAL_GL_RGBA:
         size = 4;
         break;
       default:
         return ErrorInvalidEnumInfo("readPixels: format", format);
     }
 
     switch (type) {
-//         case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-//         case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-//         case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+      // XXX we need to support 565 with GL_RGB, but the code
+      // below needs to be taught about unsigned short
+      //case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
       case LOCAL_GL_UNSIGNED_BYTE:
         break;
       default:
         return ErrorInvalidEnumInfo("ReadPixels: type", type);
     }
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
 
@@ -2574,16 +2573,61 @@ WebGLContext::ReadPixels_base(WebGLint x
             memcpy(static_cast<GLubyte*>(data)
                      + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
                      + size * subrect_x_in_dest_buffer, // destination
                    subrect_data + subrect_alignedRowSize * y_inside_subrect, // source
                    subrect_plainRowSize); // size
         }
         delete [] subrect_data;
     }
+
+    // if we're reading alpha, we may need to do fixup
+    if (format == LOCAL_GL_ALPHA ||
+        format == LOCAL_GL_RGBA)
+    {
+        PRBool needAlphaFixup;
+        if (mBoundFramebuffer) {
+            needAlphaFixup = !mBoundFramebuffer->ColorAttachment0HasAlpha();
+        } else {
+            needAlphaFixup = gl->ActualFormat().alpha == 0;
+        }
+
+        if (needAlphaFixup) {
+            if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
+                // this is easy; it's an 0xff memset per row
+                PRUint8 *row = (PRUint8*)data;
+                for (GLint j = 0; j < height; ++j) {
+                    memset(row, 0xff, checked_plainRowSize.value());
+                    row += checked_alignedRowSize.value();
+                }
+            } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
+                // this is harder, we need to just set the alpha byte here
+                PRUint8 *row = (PRUint8*)data;
+                for (GLint j = 0; j < height; ++j) {
+                    PRUint8 *rowp = row;
+#ifdef IS_LITTLE_ENDIAN
+                    // offset to get the alpha byte; we're always going to
+                    // move by 4 bytes
+                    rowp += 3;
+#endif
+                    PRUint8 *endrowp = rowp + 4 * width;
+                    while (rowp != endrowp) {
+                        *rowp = 0xff;
+                        rowp += 4;
+                    }
+
+                    row += checked_alignedRowSize.value();
+                }
+            } else {
+                NS_WARNING("Unhandled case, how'd we get here?");
+                return NS_ERROR_FAILURE;
+            }
+        }            
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::ReadPixels_array(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height,
                                WebGLenum format, WebGLenum type, js::TypedArray *pixels)
 {
     return ReadPixels_base(x, y, width, height, format, type,
@@ -3875,139 +3919,16 @@ WebGLContext::TexSubImage2D_dom(WebGLenu
     return TexSubImage2D_base(target, level,
                               xoffset, yoffset,
                               isurf->Width(), isurf->Height(), isurf->Stride(),
                               format, type,
                               isurf->Data(), byteLength,
                               srcFormat, PR_TRUE);
 }
 
-#if 0
-// ImageData getImageData (in float x, in float y, in float width, in float height);
-NS_IMETHODIMP
-WebGLContext::GetImageData(PRUint32 x, PRUint32 y, PRUint32 w, PRUint32 h)
-{
-    // disabled due to win32 linkage issues with thebes symbols and NS_RELEASE
-    return NS_ERROR_FAILURE;
-
-#if 0
-    NativeJSContext js;
-    if (NS_FAILED(js.error))
-        return js.error;
-
-    if (js.argc != 4) return NS_ERROR_INVALID_ARG;
-    
-    if (!mGLPbuffer ||
-        !mGLPbuffer->ThebesSurface())
-        return NS_ERROR_FAILURE;
-
-    if (!mCanvasElement)
-        return NS_ERROR_FAILURE;
-
-    if (HTMLCanvasElement()->IsWriteOnly() && !IsCallerTrustedForRead()) {
-        // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-        return NS_ERROR_DOM_SECURITY_ERR;
-    }
-
-    JSContext *ctx = js.ctx;
-
-    if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    nsAutoArrayPtr<PRUint8> surfaceData (new (std::nothrow) PRUint8[w * h * 4]);
-    int surfaceDataStride = w*4;
-    int surfaceDataOffset = 0;
-
-    if (!surfaceData)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    nsRefPtr<gfxImageSurface> tmpsurf = new gfxImageSurface(surfaceData,
-                                                            gfxIntSize(w, h),
-                                                            w * 4,
-                                                            gfxASurface::ImageFormatARGB32);
-    if (!tmpsurf || tmpsurf->CairoStatus())
-        return NS_ERROR_FAILURE;
-
-    nsRefPtr<gfxContext> tmpctx = new gfxContext(tmpsurf);
-
-    if (!tmpctx || tmpctx->HasError())
-        return NS_ERROR_FAILURE;
-
-    nsRefPtr<gfxASurface> surf = mGLPbuffer->ThebesSurface();
-    nsRefPtr<gfxPattern> pat = CanvasGLThebes::CreatePattern(surf);
-    gfxMatrix m;
-    m.Translate(gfxPoint(x, mGLPbuffer->Height()-y));
-    m.Scale(1.0, -1.0);
-    pat->SetMatrix(m);
-
-    // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
-    // pixel alignment for this stuff!
-    tmpctx->NewPath();
-    tmpctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, w, h), pat);
-    tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-    tmpctx->Fill();
-
-    tmpctx = nsnull;
-    tmpsurf = nsnull;
-
-    PRUint32 len = w * h * 4;
-    if (len > (((PRUint32)0xfff00000)/sizeof(jsval)))
-        return NS_ERROR_INVALID_ARG;
-
-    nsAutoArrayPtr<jsval> jsvector(new (std::nothrow) jsval[w * h * 4]);
-    if (!jsvector)
-        return NS_ERROR_OUT_OF_MEMORY;
-    jsval *dest = jsvector.get();
-    PRUint8 *row;
-    for (PRUint32 j = 0; j < h; j++) {
-        row = surfaceData + surfaceDataOffset + (surfaceDataStride * j);
-        for (PRUint32 i = 0; i < w; i++) {
-            // XXX Is there some useful swizzle MMX we can use here?
-            // I guess we have to INT_TO_JSVAL still
-#ifdef IS_LITTLE_ENDIAN
-            PRUint8 b = *row++;
-            PRUint8 g = *row++;
-            PRUint8 r = *row++;
-            PRUint8 a = *row++;
-#else
-            PRUint8 a = *row++;
-            PRUint8 r = *row++;
-            PRUint8 g = *row++;
-            PRUint8 b = *row++;
-#endif
-            // Convert to non-premultiplied color
-            if (a != 0) {
-                r = (r * 255) / a;
-                g = (g * 255) / a;
-                b = (b * 255) / a;
-            }
-
-            *dest++ = INT_TO_JSVAL(r);
-            *dest++ = INT_TO_JSVAL(g);
-            *dest++ = INT_TO_JSVAL(b);
-            *dest++ = INT_TO_JSVAL(a);
-        }
-    }
-
-    JSObject *dataArray = JS_NewArrayObject(ctx, w*h*4, jsvector);
-    if (!dataArray)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    JSObjectHelper retobj(&js);
-    retobj.DefineProperty("width", w);
-    retobj.DefineProperty("height", h);
-    retobj.DefineProperty("data", dataArray);
-
-    js.SetRetVal(retobj);
-
-    return NS_OK;
-#endif
-}
-#endif
-
 PRBool
 BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize)
 {
     switch (uType) {
         case LOCAL_GL_INT:
         case LOCAL_GL_INT_VEC2:
         case LOCAL_GL_INT_VEC3:
         case LOCAL_GL_INT_VEC4:
--- a/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html
+++ b/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html
@@ -55,17 +55,16 @@ function init()
 function getWebGL(canvasName, contextAttribs, clearColor, clearDepth, clearStencil)
 {
     var canvas = document.getElementById(canvasName);
     var gl = canvas.getContext("experimental-webgl", contextAttribs);
     if (!gl) {
         alert("No WebGL context found");
         return null;
     }
-    var actualContextAttribs = gl.getContextAttributes();
 
     // Add a console
     gl.console = ("console" in window) ? window.console : { log: function() { } };
 
     // create our shaders
     var vertexShader = loadShader(gl, "vshader");
     var fragmentShader = loadShader(gl, "fshader");
 
@@ -141,16 +140,23 @@ function drawAndReadPixel(gl, vertices, 
 function testAlpha(alpha)
 {
     debug("Testing alpha = " + alpha);
     if (alpha)
         shouldBeNonNull("webGL = getWebGL('alphaOn', { alpha: true, depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 0 ], 1, 0)");
     else
         shouldBeNonNull("webGL = getWebGL('alphaOff', { alpha: false, depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 0 ], 1, 0)");
     shouldBeNonNull("contextAttribs = webGL.getContextAttributes()");
+
+    shouldBe("'depth' in contextAttribs", "true");
+    shouldBe("'stencil' in contextAttribs", "true");
+    shouldBe("'alpha' in contextAttribs", "true");
+    shouldBe("'antialias' in contextAttribs", "true");
+    shouldBe("'premultipliedAlpha' in contextAttribs", "true");
+
     shouldBe("contextAttribs.alpha", (alpha ? "true" : "false"));
     shouldBe("contextAttribs.depth", "false");
     shouldBe("contextAttribs.stencil", "false");
     shouldBe("contextAttribs.antialias", "false");
     shouldBe("contextAttribs.premultipliedAlpha", "true");
 
     var buf = new Uint8Array(1 * 1 * 4);
     webGL.readPixels(0, 0, 1, 1, webGL.RGBA, webGL.UNSIGNED_BYTE, buf);
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -164,17 +164,17 @@ public:
   // invalidated has been displayed to the screen, so that it should
   // start requesting invalidates again as needed.
   void MarkContextClean();
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   nsIntSize GetWidthHeight();
 
-  nsresult UpdateContext();
+  nsresult UpdateContext(nsIPropertyBag *aNewContextOptions = nsnull);
   nsresult ExtractData(const nsAString& aType,
                        const nsAString& aOptions,
                        char*& aData,
                        PRUint32& aSize,
                        bool& aFellBackToPNG);
   nsresult ToDataURLImpl(const nsAString& aMimeType,
                          const nsAString& aEncoderOptions,
                          nsAString& aDataURL);
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -46,16 +46,19 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 
 #include "nsFrameManager.h"
 #include "nsDisplayList.h"
 #include "ImageLayers.h"
 #include "BasicLayers.h"
+#include "imgIEncoder.h"
+
+#include "nsIWritablePropertyBag2.h"
 
 #define DEFAULT_CANVAS_WIDTH 300
 #define DEFAULT_CANVAS_HEIGHT 150
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
@@ -146,17 +149,17 @@ nsHTMLCanvasElement::SetAttr(PRInt32 aNa
 nsresult
 nsHTMLCanvasElement::CopyInnerTo(nsGenericElement* aDest) const
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDest->GetOwnerDoc()->IsStaticDocument()) {
     nsHTMLCanvasElement* dest = static_cast<nsHTMLCanvasElement*>(aDest);
     nsCOMPtr<nsISupports> cxt;
-    dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt));
+    dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt));
     nsCOMPtr<nsIDOMCanvasRenderingContext2D> context2d = do_QueryInterface(cxt);
     if (context2d) {
       context2d->DrawImage(const_cast<nsHTMLCanvasElement*>(this),
                            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0);
     }
   }
   return rv;
 }
@@ -225,43 +228,70 @@ nsHTMLCanvasElement::ToDataURLAs(const n
 
 nsresult
 nsHTMLCanvasElement::ExtractData(const nsAString& aType,
                                  const nsAString& aOptions,
                                  char*& aResult,
                                  PRUint32& aSize,
                                  bool& aFellBackToPNG)
 {
-  // We get an input stream from the context. If more than one context type
-  // is supported in the future, this will have to be changed to do the right
-  // thing. For now, just assume that the 2D context has all the goods.
-  nsCOMPtr<nsICanvasRenderingContextInternal> context;
-  nsresult rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!context) {
-    // XXX bug 578349
-    return NS_ERROR_NOT_IMPLEMENTED;
+  // note that if we don't have a current context, the spec says we're
+  // supposed to just return transparent black pixels of the canvas
+  // dimensions.
+  nsRefPtr<gfxImageSurface> emptyCanvas;
+  nsIntSize size = GetWidthHeight();
+  if (!mCurrentContext) {
+    emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
   }
 
+  nsresult rv;
+
   // get image bytes
   nsCOMPtr<nsIInputStream> imgStream;
-  NS_ConvertUTF16toUTF8 aMimeType8(aType);
-  rv = context->GetInputStream(nsPromiseFlatCString(aMimeType8).get(),
-                               nsPromiseFlatString(aOptions).get(),
-                               getter_AddRefs(imgStream));
-  if (NS_FAILED(rv)) {
-    // Use image/png instead.
+  nsCAutoString encoderType;
+  encoderType.Assign(NS_ConvertUTF16toUTF8(aType));
+
+ try_again:
+  if (mCurrentContext) {
+    rv = mCurrentContext->GetInputStream(nsPromiseFlatCString(encoderType).get(),
+                                         nsPromiseFlatString(aOptions).get(),
+                                         getter_AddRefs(imgStream));
+  } else {
+    // no context, so we have to encode the empty image we created above
+    nsCString enccid("@mozilla.org/image/encoder;2?type=");
+    enccid += encoderType;
+
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(nsPromiseFlatCString(enccid).get(), &rv);
+    if (NS_SUCCEEDED(rv) && encoder) {
+      rv = encoder->InitFromData(emptyCanvas->Data(),
+                                 size.width * size.height * 4,
+                                 size.width,
+                                 size.height,
+                                 size.width * 4,
+                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                                 aOptions);
+      if (NS_SUCCEEDED(rv)) {
+        imgStream = do_QueryInterface(encoder);
+      }
+    } else {
+      rv = NS_ERROR_FAILURE;
+    }
+  }
+
+  if (NS_FAILED(rv) && !aFellBackToPNG) {
+    // Try image/png instead.
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     aFellBackToPNG = true;
-    rv = context->GetInputStream("image/png",
-                                 nsPromiseFlatString(aOptions).get(),
-                                 getter_AddRefs(imgStream));
-    NS_ENSURE_SUCCESS(rv, rv);
+    encoderType.AssignLiteral("image/png");
+    goto try_again;
   }
 
+  // at this point, we either need to succeed or bail.
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Generally, there will be only one chunk of data, and it will be available
   // for us to read right away, so optimize this case.
   PRUint32 bufSize;
   rv = imgStream->Available(&bufSize);
   CheckedInt32 safeBufSize(bufSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ...leave a little extra room so we can call read again and make sure we
@@ -296,16 +326,17 @@ nsHTMLCanvasElement::ExtractData(const n
 }
 
 nsresult
 nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
                                    const nsAString& aEncoderOptions,
                                    nsAString& aDataURL)
 {
   bool fallbackToPNG = false;
+
   PRUint32 imgSize = 0;
   char* imgData;
 
   nsresult rv = ExtractData(aMimeType, aEncoderOptions, imgData,
                             imgSize, fallbackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // base 64, result will be NULL terminated
@@ -412,16 +443,17 @@ nsHTMLCanvasElement::GetContextHelper(co
 
   *aContext = ctx.forget().get();
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLCanvasElement::GetContext(const nsAString& aContextId,
+                                const jsval& aContextOptions,
                                 nsISupports **aContext)
 {
   nsresult rv;
 
   if (mCurrentContextId.IsEmpty()) {
     rv = GetContextHelper(aContextId, getter_AddRefs(mCurrentContext));
     NS_ENSURE_SUCCESS(rv, rv);
     if (!mCurrentContext) {
@@ -438,17 +470,62 @@ nsHTMLCanvasElement::GetContext(const ns
     }
 
     rv = mCurrentContext->SetCanvasElement(this);
     if (NS_FAILED(rv)) {
       mCurrentContext = nsnull;
       return rv;
     }
 
-    rv = UpdateContext();
+    nsCOMPtr<nsIPropertyBag> contextProps;
+    if (!JSVAL_IS_NULL(aContextOptions) &&
+        !JSVAL_IS_VOID(aContextOptions))
+    {
+      JSContext *cx = nsContentUtils::GetCurrentJSContext();
+
+      nsCOMPtr<nsIWritablePropertyBag2> newProps;
+
+      // note: if any contexts end up supporting something other
+      // than objects, e.g. plain strings, then we'll need to expand
+      // this to know how to create nsISupportsStrings etc.
+      if (JSVAL_IS_OBJECT(aContextOptions)) {
+        newProps = do_CreateInstance("@mozilla.org/hash-property-bag;1");
+
+        JSObject *opts = JSVAL_TO_OBJECT(aContextOptions);
+        JSIdArray *props = JS_Enumerate(cx, opts);
+        for (int i = 0; props && i < props->length; ++i) {
+          jsid propid = props->vector[i];
+          jsval propname, propval;
+          if (!JS_IdToValue(cx, propid, &propname) ||
+              !JS_GetPropertyById(cx, opts, propid, &propval))
+          {
+            continue;
+          }
+
+          JSString *propnameString = JS_ValueToString(cx, propname);
+
+          nsDependentString pstr(JS_GetStringChars(propnameString), JS_GetStringLength(propnameString));
+
+          if (JSVAL_IS_BOOLEAN(propval)) {
+            newProps->SetPropertyAsBool(pstr, propval == JSVAL_TRUE ? PR_TRUE : PR_FALSE);
+          } else if (JSVAL_IS_INT(propval)) {
+            newProps->SetPropertyAsInt32(pstr, JSVAL_TO_INT(propval));
+          } else if (JSVAL_IS_DOUBLE(propval)) {
+            newProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval));
+          } else if (JSVAL_IS_STRING(propval)) {
+            newProps->SetPropertyAsAString(pstr, nsDependentString(JS_GetStringChars(JS_ValueToString(cx, propval)),
+                                                                   JS_GetStringLength(JS_ValueToString(cx, propval))));
+          }
+        }
+      }
+
+      contextProps = newProps;
+    }
+
+    rv = UpdateContext(contextProps);
     if (NS_FAILED(rv)) {
       mCurrentContext = nsnull;
       return rv;
     }
 
     mCurrentContextId.Assign(aContextId);
   } else if (!mCurrentContextId.Equals(aContextId)) {
     //XXX eventually allow for more than one active context on a given canvas
@@ -499,24 +576,35 @@ nsHTMLCanvasElement::MozGetIPCContext(co
   NS_ADDREF (*aContext = mCurrentContext);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 nsresult
-nsHTMLCanvasElement::UpdateContext()
+nsHTMLCanvasElement::UpdateContext(nsIPropertyBag *aNewContextOptions)
 {
+  if (!mCurrentContext)
+    return NS_OK;
+
   nsresult rv = NS_OK;
-  if (mCurrentContext) {
-    nsIntSize sz = GetWidthHeight();
-    rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
-    rv = mCurrentContext->SetDimensions(sz.width, sz.height);
-  }
+
+  rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = mCurrentContext->SetContextOptions(aNewContextOptions);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsIntSize sz = GetWidthHeight();
+  rv = mCurrentContext->SetDimensions(sz.width, sz.height);
+  if (NS_FAILED(rv))
+    return rv;
 
   return rv;
 }
 
 nsIFrame *
 nsHTMLCanvasElement::GetPrimaryCanvasFrame()
 {
   return GetPrimaryFrame(Flush_Frames);
--- a/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
+++ b/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
@@ -36,30 +36,34 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIVariant.idl"
 
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
+interface nsIPropertyBag;
 
 // XXX should we comment out these typedefs in the C++ header?
 
 typedef unsigned long  WebGLenum;
 typedef boolean        WebGLboolean;
 typedef unsigned long  WebGLbitfield;
 typedef long           WebGLint;
 typedef long           WebGLsizei;
 typedef long           WebGLsizeiptr;
 typedef unsigned long  WebGLuint;
 typedef float          WebGLfloat;
 typedef float          WebGLclampf;
 
 %{C++
+// for jsval
+#include "jsapi.h"
+
 namespace js {
 struct ArrayBuffer;
 struct TypedArray;
 }
 
 /* Avoid conflict with WinAPI */
 #undef NO_ERROR
 %}
@@ -553,17 +557,17 @@ interface nsICanvasRenderingContextWebGL
   //
   //  ATTRIBUTES
   //
   readonly attribute nsIDOMHTMLCanvasElement canvas;
 
   //
   //  METHODS
   //
-  void present();
+  jsval getContextAttributes();
 
   void activeTexture(in WebGLenum texture);
   void attachShader([optional] in nsIWebGLProgram program, [optional] in nsIWebGLShader shader);
   void bindAttribLocation(in nsIWebGLProgram program, in WebGLuint index, in DOMString name);
   void bindBuffer(in WebGLenum target, in nsIWebGLBuffer buffer);
   void bindFramebuffer(in WebGLenum target, in nsIWebGLFramebuffer framebuffer);
   void bindRenderbuffer(in WebGLenum target, in nsIWebGLRenderbuffer renderbuffer);
   void bindTexture(in WebGLenum target, in nsIWebGLTexture texture);
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
@@ -32,36 +32,42 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMHTMLElement.idl"
 
+%{C++
+// for jsval
+#include "jsapi.h"
+%}
+
 /**
  * The nsIDOMHTMLCanvasElement interface is the interface to a HTML
  * <canvas> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#graphics
  *
  * @status UNDER_DEVELOPMENT
  */
 
 interface nsIDOMFile;
 
-[scriptable, uuid(28945fd6-c4a0-44e3-8629-98358eab5d7b)]
+[scriptable, uuid(53ad994a-3cd0-48fa-8ffb-7f3d8cd19c50)]
 interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
 {
   attribute long width;
   attribute long height;
   attribute boolean mozOpaque;
 
-  nsISupports getContext(in DOMString contextId);
+  nsISupports getContext(in DOMString contextId,
+                         [optional] in jsval contextOptions);
 
 
   // Valid calls are:
   //  toDataURL();              -- defaults to image/png
   //  toDataURL(type);          -- uses given type
   //  toDataURL(type, params);  -- only available to trusted callers
   [optional_argc] DOMString toDataURL([optional] in DOMString type,
                                       [optional] in DOMString params);
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -359,16 +359,18 @@ GLContext::InitWithPrefix(const char *pr
             if (mVendor < VendorOther) {
                 printf_stderr("OpenGL vendor ('%s') recognized as: %s\n",
                               glVendorString, vendors[mVendor]);
             } else {
                 printf_stderr("OpenGL vendor ('%s') unrecognized\n", glVendorString);
             }
         }
 #endif
+
+        UpdateActualFormat();
     }
 
 #ifdef DEBUG
     if (PR_GetEnv("MOZ_GL_DEBUG"))
         mDebugMode |= DebugEnabled;
 
     // enables extra verbose output, informing of the start and finish of every GL call.
     // useful e.g. to record information to investigate graphics system crashes/lockups
@@ -704,16 +706,20 @@ GLContext::ResizeOffscreenFBO(const gfxI
         !mIsGLES2 || IsExtensionSupported(OES_packed_depth_stencil);
 
     // save a few things for later restoring
     fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*) &curBoundTexture);
     fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*) &curBoundFramebuffer);
     fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*) &curBoundRenderbuffer);
     fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
 
+    // the context format of what we're defining;
+    // for some reason, UpdateActualFormat isn't working with a bound FBO.
+    ContextFormat cf;
+
     // If this is the first time we're going through this, we need
     // to create the objects we'll use.  Otherwise, just bind them.
     if (firstTime) {
         fGenTextures(1, &mOffscreenTexture);
         fBindTexture(LOCAL_GL_TEXTURE_2D, mOffscreenTexture);
         fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
         fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
 
@@ -741,37 +747,50 @@ GLContext::ResizeOffscreenFBO(const gfxI
         fTexImage2D(LOCAL_GL_TEXTURE_2D,
                     0,
                     LOCAL_GL_RGBA,
                     aSize.width, aSize.height,
                     0,
                     LOCAL_GL_RGBA,
                     LOCAL_GL_UNSIGNED_BYTE,
                     NULL);
+
+        cf.red = cf.green = cf.blue = cf.alpha = 8;
     } else {
         fTexImage2D(LOCAL_GL_TEXTURE_2D,
                     0,
                     LOCAL_GL_RGB,
                     aSize.width, aSize.height,
                     0,
                     LOCAL_GL_RGB,
 #ifdef XP_WIN
                     LOCAL_GL_UNSIGNED_BYTE,
 #else
                     mIsGLES2 ? LOCAL_GL_UNSIGNED_SHORT_5_6_5
                              : LOCAL_GL_UNSIGNED_BYTE,
 #endif
                     NULL);
+
+#ifdef XP_WIN
+        cf.red = cf.green = cf.blue = 8;
+#else
+        cf.red = 5;
+        cf.green = 6;
+        cf.blue = 5;
+#endif
+        cf.alpha = 0;
     }
 
     if (depth && stencil && useDepthStencil) {
         fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOffscreenDepthRB);
         fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
                              LOCAL_GL_DEPTH24_STENCIL8,
                              aSize.width, aSize.height);
+        cf.depth = 24;
+        cf.stencil = 8;
     } else {
         if (depth) {
             GLenum depthType;
             if (mIsGLES2) {
                 if (IsExtensionSupported(OES_depth32)) {
                     depthType = LOCAL_GL_DEPTH_COMPONENT32;
                 } else if (IsExtensionSupported(OES_depth24)) {
                     depthType = LOCAL_GL_DEPTH_COMPONENT24;
@@ -782,23 +801,25 @@ GLContext::ResizeOffscreenFBO(const gfxI
                 depthType = LOCAL_GL_DEPTH_COMPONENT24;
             }
 
             fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOffscreenDepthRB);
             fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
                                  mIsGLES2 ? LOCAL_GL_DEPTH_COMPONENT16
                                           : LOCAL_GL_DEPTH_COMPONENT24,
                                  aSize.width, aSize.height);
+            cf.depth = mIsGLES2 ? 16 : 24;
         }
 
         if (stencil) {
             fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mOffscreenStencilRB);
             fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
                                  LOCAL_GL_STENCIL_INDEX8,
                                  aSize.width, aSize.height);
+            cf.stencil = 8;
         }
     }
 
     // Now assemble the FBO, if we're creating one
     // for the first time.
     if (firstTime) {
         fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
@@ -838,17 +859,20 @@ GLContext::ResizeOffscreenFBO(const gfxI
         NS_WARNING("Error resizing offscreen framebuffer -- framebuffer not complete");
         return PR_FALSE;
     }
 
     mOffscreenSize = aSize;
     mOffscreenActualSize = aSize;
 
     if (firstTime) {
-        UpdateActualFormat();
+        // UpdateActualFormat() doesn't work for some reason, with a
+        // FBO bound, even though it should.
+        //UpdateActualFormat();
+        mActualFormat = cf;
 
 #ifdef DEBUG
         printf_stderr("Created offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d\n",
                       mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha,
                       mActualFormat.depth, mActualFormat.stencil);
 #endif
     }
 
--- a/gfx/thebes/GLContextProviderWGL.cpp
+++ b/gfx/thebes/GLContextProviderWGL.cpp
@@ -563,16 +563,17 @@ CreatePBufferOffscreenContext(const gfxI
 
     A2(attrs, LOCAL_WGL_COLOR_BITS_ARB, aFormat.colorBits());
     A2(attrs, LOCAL_WGL_RED_BITS_ARB, aFormat.red);
     A2(attrs, LOCAL_WGL_GREEN_BITS_ARB, aFormat.green);
     A2(attrs, LOCAL_WGL_BLUE_BITS_ARB, aFormat.blue);
     A2(attrs, LOCAL_WGL_ALPHA_BITS_ARB, aFormat.alpha);
 
     A2(attrs, LOCAL_WGL_DEPTH_BITS_ARB, aFormat.depth);
+    A2(attrs, LOCAL_WGL_STENCIL_BITS_ARB, aFormat.stencil);
 
     if (aFormat.alpha > 0) {
         A2(attrs, LOCAL_WGL_BIND_TO_TEXTURE_RGBA_ARB, LOCAL_GL_TRUE);
     } else {
         A2(attrs, LOCAL_WGL_BIND_TO_TEXTURE_RGB_ARB, LOCAL_GL_TRUE);
     }
 
     A2(attrs, LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE);
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -453,16 +453,17 @@ members = [
     '-nsICanvasRenderingContextWebGL.getParameter',
     '-nsICanvasRenderingContextWebGL.getBufferParameter',
     '-nsICanvasRenderingContextWebGL.getFramebufferAttachmentParameter',
     '-nsICanvasRenderingContextWebGL.getRenderbufferParameter',
     '-nsICanvasRenderingContextWebGL.getProgramParameter',
     '-nsICanvasRenderingContextWebGL.getUniform',
     '-nsICanvasRenderingContextWebGL.getVertexAttrib',
     '-nsICanvasRenderingContextWebGL.getShaderParameter',
+    '-nsICanvasRenderingContextWebGL.getContextAttributes',
 
     # Audio
     'nsIDOMNotifyAudioAvailableEvent.frameBuffer',
     'nsIDOMNotifyAudioAvailableEvent.time',
     'nsIDOMHTMLAudioElement.mozWriteAudio',
 
     # dom/indexedDB
     'nsIIDBCursor.*',