Bug 1320030 - Simplify marking and deletion checks. (flattened) - a=gchang
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 15 Dec 2016 18:39:24 -0800
changeset 368090 630ad73cc24b3a28262a27e40bde43e5e54f21a2
parent 368089 3d32a5d8dc3e7bcac8883d434cdcf11baba69dfd
child 368091 3ddf24a98ed75a69527322b771c870fe2694f5c1
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgchang
bugs1320030
milestone52.0a2
Bug 1320030 - Simplify marking and deletion checks. (flattened) - a=gchang MozReview-Commit-ID: DrXDLwbPeaE
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextPrograms.cpp
dom/canvas/WebGL2ContextQueries.cpp
dom/canvas/WebGL2ContextSamplers.cpp
dom/canvas/WebGL2ContextSync.cpp
dom/canvas/WebGL2ContextTransformFeedback.cpp
dom/canvas/WebGL2ContextUniforms.cpp
dom/canvas/WebGLBuffer.cpp
dom/canvas/WebGLBuffer.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextTextures.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLContextVertexArray.cpp
dom/canvas/WebGLExtensionDebugShaders.cpp
dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
dom/canvas/WebGLExtensionVertexArray.cpp
dom/canvas/WebGLExtensions.h
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLObjectModel.h
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLQuery.cpp
dom/canvas/WebGLQuery.h
dom/canvas/WebGLRenderbuffer.cpp
dom/canvas/WebGLRenderbuffer.h
dom/canvas/WebGLSampler.cpp
dom/canvas/WebGLSampler.h
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShader.h
dom/canvas/WebGLSync.cpp
dom/canvas/WebGLSync.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTransformFeedback.cpp
dom/canvas/WebGLTransformFeedback.h
dom/canvas/WebGLVertexArray.cpp
dom/canvas/WebGLVertexArray.h
dom/canvas/WebGLVertexArrayFake.cpp
dom/canvas/WebGLVertexArrayFake.h
dom/canvas/WebGLVertexArrayGL.cpp
dom/canvas/WebGLVertexArrayGL.h
dom/canvas/moz.build
dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -309,42 +309,42 @@ public:
 
     void ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth, GLint stencil);
 
     // -------------------------------------------------------------------------
     // Sampler Objects - WebGL2ContextSamplers.cpp
 
     already_AddRefed<WebGLSampler> CreateSampler();
     void DeleteSampler(WebGLSampler* sampler);
-    bool IsSampler(WebGLSampler* sampler);
+    bool IsSampler(const WebGLSampler* sampler);
     void BindSampler(GLuint unit, WebGLSampler* sampler);
     void SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint param);
     void SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param);
     void GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                              JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Sync objects - WebGL2ContextSync.cpp
 
     already_AddRefed<WebGLSync> FenceSync(GLenum condition, GLbitfield flags);
-    bool IsSync(WebGLSync* sync);
+    bool IsSync(const WebGLSync* sync);
     void DeleteSync(WebGLSync* sync);
     GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout);
     void WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout);
     void GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                           JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Transform Feedback - WebGL2ContextTransformFeedback.cpp
 
     already_AddRefed<WebGLTransformFeedback> CreateTransformFeedback();
     void DeleteTransformFeedback(WebGLTransformFeedback* tf);
-    bool IsTransformFeedback(WebGLTransformFeedback* tf);
+    bool IsTransformFeedback(const WebGLTransformFeedback* tf);
     void BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf);
     void BeginTransformFeedback(GLenum primitiveMode);
     void EndTransformFeedback();
     void PauseTransformFeedback();
     void ResumeTransformFeedback();
     void TransformFeedbackVaryings(WebGLProgram& program,
                                    const dom::Sequence<nsString>& varyings,
                                    GLenum bufferMode);
--- a/dom/canvas/WebGL2ContextPrograms.cpp
+++ b/dom/canvas/WebGL2ContextPrograms.cpp
@@ -14,15 +14,15 @@ namespace mozilla {
 // Programs and shaders
 
 GLint
 WebGL2Context::GetFragDataLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObjectRef("getFragDataLocation: program", prog))
+    if (!ValidateObject("getFragDataLocation: program", prog))
         return -1;
 
     return prog.GetFragDataLocation(name);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextQueries.cpp
+++ b/dom/canvas/WebGL2ContextQueries.cpp
@@ -73,63 +73,48 @@ WebGLContext::CreateQuery(const char* fu
 
 void
 WebGLContext::DeleteQuery(WebGLQuery* query, const char* funcName)
 {
     if (!funcName) {
         funcName = "deleteQuery";
     }
 
-    if (IsContextLost())
-        return;
-
-    if (!query)
-        return;
-
-    if (!ValidateObjectAllowDeleted(funcName, query))
+    if (!ValidateDeleteObject(funcName, query))
         return;
 
     query->DeleteQuery();
 }
 
 bool
 WebGLContext::IsQuery(const WebGLQuery* query, const char* funcName)
 {
     if (!funcName) {
         funcName = "isQuery";
     }
 
-    if (IsContextLost())
-        return false;
-
-    if (!query)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isQuery", query))
+    if (!ValidateIsObject(funcName, query))
         return false;
 
     return query->IsQuery();
 }
 
 void
 WebGLContext::BeginQuery(GLenum target, WebGLQuery& query, const char* funcName)
 {
     if (!funcName) {
         funcName = "beginQuery";
     }
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, &query))
+    if (!ValidateObject(funcName, query))
         return;
 
-    if (query.IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot begin a deleted query.", funcName);
-
     const auto& slot = ValidateQuerySlotByTarget(funcName, target);
     if (!slot)
         return;
 
     if (*slot)
         return ErrorInvalidOperation("%s: Query target already active.", funcName);
 
     ////
@@ -233,18 +218,15 @@ WebGLContext::GetQueryParameter(JSContex
     if (!funcName) {
         funcName = "getQueryParameter";
     }
 
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, &query))
+    if (!ValidateObject(funcName, query))
         return;
 
-    if (query.IsDeleted())
-        return ErrorInvalidOperation("%s: Query must not be deleted.", funcName);
-
     query.GetQueryParameter(pname, retval);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -21,116 +21,98 @@ WebGL2Context::CreateSampler()
 
     RefPtr<WebGLSampler> globj = new WebGLSampler(this, sampler);
     return globj.forget();
 }
 
 void
 WebGL2Context::DeleteSampler(WebGLSampler* sampler)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteSampler", sampler))
-        return;
-
-    if (!sampler || sampler->IsDeleted())
+    if (!ValidateDeleteObject("deleteSampler", sampler))
         return;
 
     for (int n = 0; n < mGLMaxTextureUnits; n++) {
         if (mBoundSamplers[n] == sampler) {
             mBoundSamplers[n] = nullptr;
 
             InvalidateResolveCacheForTextureWithTexUnit(n);
         }
     }
 
     sampler->RequestDelete();
 }
 
 bool
-WebGL2Context::IsSampler(WebGLSampler* sampler)
+WebGL2Context::IsSampler(const WebGLSampler* sampler)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!sampler)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isSampler", sampler))
-        return false;
-
-    if (sampler->IsDeleted())
+    if (!ValidateIsObject("isSampler", sampler))
         return false;
 
     MakeContextCurrent();
     return gl->fIsSampler(sampler->mGLName);
 }
 
 void
 WebGL2Context::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindSampler", sampler))
+    if (sampler && !ValidateObject("bindSampler", *sampler))
         return;
 
     if (GLint(unit) >= mGLMaxTextureUnits)
         return ErrorInvalidValue("bindSampler: unit must be < %d", mGLMaxTextureUnits);
 
-    if (sampler && sampler->IsDeleted())
-        return ErrorInvalidOperation("bindSampler: binding deleted sampler");
-
     ////
 
     gl->MakeCurrent();
     gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
 
     InvalidateResolveCacheForTextureWithTexUnit(unit);
     mBoundSamplers[unit] = sampler;
 }
 
 void
 WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint paramInt)
 {
     const char funcName[] = "samplerParameteri";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     sampler.SamplerParameter(funcName, pname, paramInt);
 }
 
 void
 WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat paramFloat)
 {
     const char funcName[] = "samplerParameterf";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     sampler.SamplerParameter(funcName, pname, WebGLIntOrFloat(paramFloat).AsInt());
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                                    JS::MutableHandleValue retval)
 {
     const char funcName[] = "getSamplerParameter";
     retval.setNull();
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     ////
 
     gl->MakeCurrent();
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -11,66 +11,60 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Sync objects
 
 already_AddRefed<WebGLSync>
 WebGL2Context::FenceSync(GLenum condition, GLbitfield flags)
 {
-   if (IsContextLost())
-       return nullptr;
+    if (IsContextLost())
+        return nullptr;
 
-   if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
-       ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
-       return nullptr;
-   }
+    if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
+        ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
+        return nullptr;
+    }
 
-   if (flags != 0) {
-       ErrorInvalidValue("fenceSync: flags must be 0");
-       return nullptr;
-   }
+    if (flags != 0) {
+        ErrorInvalidValue("fenceSync: flags must be 0");
+        return nullptr;
+    }
 
-   MakeContextCurrent();
-   RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
-   return globj.forget();
+    MakeContextCurrent();
+    RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
+    return globj.forget();
 }
 
 bool
-WebGL2Context::IsSync(WebGLSync* sync)
+WebGL2Context::IsSync(const WebGLSync* sync)
 {
-   if (IsContextLost())
-       return false;
+    if (!ValidateIsObject("isSync", sync))
+        return false;
 
-   return ValidateObjectAllowDeleted("isSync", sync) && !sync->IsDeleted();
+    return true;
 }
 
 void
 WebGL2Context::DeleteSync(WebGLSync* sync)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteSync", sync))
-        return;
-
-    if (!sync || sync->IsDeleted())
+    if (!ValidateDeleteObject("deleteSync", sync))
         return;
 
     sync->RequestDelete();
 }
 
 GLenum
 WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout)
 {
     const char funcName[] = "clientWaitSync";
     if (IsContextLost())
         return LOCAL_GL_WAIT_FAILED;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return LOCAL_GL_WAIT_FAILED;
 
     if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
         ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName);
         return LOCAL_GL_WAIT_FAILED;
     }
 
     MakeContextCurrent();
@@ -79,17 +73,17 @@ WebGL2Context::ClientWaitSync(const WebG
 
 void
 WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout)
 {
     const char funcName[] = "waitSync";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return;
 
     if (flags != 0) {
         ErrorInvalidValue("%s: `flags` must be 0.", funcName);
         return;
     }
 
     if (timeout != -1) {
@@ -105,17 +99,17 @@ void
 WebGL2Context::GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                                 JS::MutableHandleValue retval)
 {
     const char funcName[] = "getSyncParameter";
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return;
 
     ////
 
     gl->MakeCurrent();
 
     GLint result = 0;
     switch (pname) {
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -27,66 +27,54 @@ WebGL2Context::CreateTransformFeedback()
     RefPtr<WebGLTransformFeedback> ret = new WebGLTransformFeedback(this, tf);
     return ret.forget();
 }
 
 void
 WebGL2Context::DeleteTransformFeedback(WebGLTransformFeedback* tf)
 {
     const char funcName[] = "deleteTransformFeedback";
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObject(funcName, tf))
+    if (!ValidateDeleteObject(funcName, tf))
         return;
 
     if (tf->mIsActive) {
         ErrorInvalidOperation("%s: Cannot delete active transform feedbacks.", funcName);
         return;
     }
 
     if (mBoundTransformFeedback == tf) {
         BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
     }
 
     tf->RequestDelete();
 }
 
 bool
-WebGL2Context::IsTransformFeedback(WebGLTransformFeedback* tf)
+WebGL2Context::IsTransformFeedback(const WebGLTransformFeedback* tf)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeletedOrNull("isTransformFeedback", tf))
-        return false;
-
-    if (!tf || tf->IsDeleted())
+    if (!ValidateIsObject("isTransformFeedback", tf))
         return false;
 
     MakeContextCurrent();
     return gl->fIsTransformFeedback(tf->mGLName);
 }
 
 void
 WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
 {
     const char funcName[] = "bindTransformFeedback";
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
         return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName);
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, tf))
+    if (tf && !ValidateObject(funcName, *tf))
         return;
 
-    if (tf && tf->IsDeleted())
-        return ErrorInvalidOperation("%s: TFO already deleted.", funcName);
-
     if (mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
         ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
                               " paused.",
                               funcName);
         return;
     }
@@ -138,27 +126,27 @@ WebGL2Context::ResumeTransformFeedback()
 void
 WebGL2Context::TransformFeedbackVaryings(WebGLProgram& program,
                                          const dom::Sequence<nsString>& varyings,
                                          GLenum bufferMode)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("transformFeedbackVaryings: program", program))
+    if (!ValidateObject("transformFeedbackVaryings: program", program))
         return;
 
     program.TransformFeedbackVaryings(varyings, bufferMode);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGL2Context::GetTransformFeedbackVarying(const WebGLProgram& program, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getTransformFeedbackVarying: program", program))
+    if (!ValidateObject("getTransformFeedbackVarying: program", program))
         return nullptr;
 
     return program.GetTransformFeedbackVarying(index);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -133,17 +133,17 @@ void
 WebGL2Context::GetUniformIndices(const WebGLProgram& program,
                                  const dom::Sequence<nsString>& uniformNames,
                                  dom::Nullable< nsTArray<GLuint> >& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getUniformIndices: program", program))
+    if (!ValidateObject("getUniformIndices: program", program))
         return;
 
     if (!uniformNames.Length())
         return;
 
     program.GetUniformIndices(uniformNames, retval);
 }
 
@@ -174,17 +174,17 @@ WebGL2Context::GetActiveUniforms(JSConte
     const char funcName[] = "getActiveUniforms";
     retval.setNull();
     if (IsContextLost())
         return;
 
     if (!ValidateUniformEnum(this, pname, funcName))
         return;
 
-    if (!ValidateObjectRef("getActiveUniforms: program", program))
+    if (!ValidateObject("getActiveUniforms: program", program))
         return;
 
     const auto& count = uniformIndices.Length();
 
     JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
     UniquePtr<GLint[]> samples(new GLint[count]);
     if (!array || !samples) {
         ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
@@ -226,33 +226,33 @@ WebGL2Context::GetActiveUniforms(JSConte
 
 GLuint
 WebGL2Context::GetUniformBlockIndex(const WebGLProgram& program,
                                     const nsAString& uniformBlockName)
 {
     if (IsContextLost())
         return 0;
 
-    if (!ValidateObjectRef("getUniformBlockIndex: program", program))
+    if (!ValidateObject("getUniformBlockIndex: program", program))
         return 0;
 
     return program.GetUniformBlockIndex(uniformBlockName);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, const WebGLProgram& program,
                                               GLuint uniformBlockIndex, GLenum pname,
                                               JS::MutableHandleValue out_retval,
                                               ErrorResult& out_error)
 {
     out_retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getActiveUniformBlockParameter: program", program))
+    if (!ValidateObject("getActiveUniformBlockParameter: program", program))
         return;
 
     MakeContextCurrent();
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
@@ -273,28 +273,28 @@ WebGL2Context::GetActiveUniformBlockPara
 void
 WebGL2Context::GetActiveUniformBlockName(const WebGLProgram& program,
                                          GLuint uniformBlockIndex, nsAString& retval)
 {
     retval.SetIsVoid(true);
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getActiveUniformBlockName: program", program))
+    if (!ValidateObject("getActiveUniformBlockName: program", program))
         return;
 
     program.GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
 
 void
 WebGL2Context::UniformBlockBinding(WebGLProgram& program, GLuint uniformBlockIndex,
                                    GLuint uniformBlockBinding)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("uniformBlockBinding: program", program))
+    if (!ValidateObject("uniformBlockBinding: program", program))
         return;
 
     program.UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -8,17 +8,17 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLElementArrayCache.h"
 
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
     , mUsage(LOCAL_GL_STATIC_DRAW)
     , mByteLength(0)
 {
     mContext->mBuffers.insertBack(this);
 }
 
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -17,17 +17,16 @@
 namespace mozilla {
 
 class WebGLElementArrayCache;
 
 class WebGLBuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLTexture;
     friend class WebGLTransformFeedback;
 
 public:
     enum class Kind {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -217,17 +217,17 @@ WebGLContext::~WebGLContext()
     DestroyResourcesAndContext();
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::RemoveWebGLContext(this);
     }
 }
 
 template<typename T>
-static void
+void
 ClearLinkedList(LinkedList<T>& list)
 {
     while (!list.isEmpty()) {
         list.getLast()->DeleteOnce();
     }
 }
 
 void
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -608,21 +608,21 @@ public:
     {
         retval.set(GetUniform(cx, prog, loc));
     }
 
     already_AddRefed<WebGLUniformLocation>
     GetUniformLocation(const WebGLProgram& prog, const nsAString& name);
 
     void Hint(GLenum target, GLenum mode);
-    bool IsFramebuffer(WebGLFramebuffer* fb);
-    bool IsProgram(WebGLProgram* prog);
-    bool IsRenderbuffer(WebGLRenderbuffer* rb);
-    bool IsShader(WebGLShader* shader);
-    bool IsVertexArray(WebGLVertexArray* vao);
+    bool IsFramebuffer(const WebGLFramebuffer* fb);
+    bool IsProgram(const WebGLProgram* prog);
+    bool IsRenderbuffer(const WebGLRenderbuffer* rb);
+    bool IsShader(const WebGLShader* shader);
+    bool IsVertexArray(const WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
     bool StartVRPresentation();
 protected:
@@ -1619,43 +1619,105 @@ protected:
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     //////
 public:
-    // Returns false if `object` is null or not valid.
-    template<class ObjectType>
-    bool ValidateObject(const char* info, const ObjectType* object);
+    bool ValidateObjectAllowDeleted(const char* funcName,
+                                    const WebGLContextBoundObject& object)
+    {
+        if (!object.IsCompatibleWithContext(this)) {
+            ErrorInvalidOperation("%s: Object from different WebGL context (or older"
+                                  " generation of this one) passed as argument.",
+                                  funcName);
+            return false;
+        }
+
+        return true;
+    }
+
+    bool ValidateObject(const char* funcName, const WebGLDeletableObject& object,
+                        bool isShaderOrProgram = false)
+    {
+        if (!ValidateObjectAllowDeleted(funcName, object))
+            return false;
 
-    // Returns false if `object` is not valid.
-    template<class ObjectType>
-    bool ValidateObjectRef(const char* info, const ObjectType& object);
-
-    // Returns false if `object` is not valid.  Considers null to be valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowNull(const char* info, const ObjectType* object);
+        if (isShaderOrProgram) {
+            /* GLES 3.0.5 p45:
+             * "Commands that accept shader or program object names will generate the
+             *  error INVALID_VALUE if the provided name is not the name of either a
+             *  shader or program object[.]"
+             * Further, shaders and programs appear to be different from other objects,
+             * in that their lifetimes are better defined. However, they also appear to
+             * allow use of objects marked for deletion, and only reject
+             * actually-destroyed objects.
+             */
+            if (object.IsDeleted()) {
+                ErrorInvalidValue("%s: Shader or program object argument cannot have been"
+                                  " deleted.",
+                                  funcName);
+                return false;
+            }
+        } else {
+            if (object.IsDeleteRequested()) {
+                ErrorInvalidOperation("%s: Object argument cannot have been marked for"
+                                      " deletion.",
+                                      funcName);
+                return false;
+            }
+        }
 
-    // Returns false if `object` is not valid, but considers deleted objects and
-    // null objects valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object);
+        return true;
+    }
+
+    ////
+
+    bool ValidateObject(const char* funcName, const WebGLProgram& object);
+    bool ValidateObject(const char* funcName, const WebGLShader& object);
+
+    ////
+
+    bool ValidateIsObject(const char* funcName,
+                          const WebGLDeletableObject* object) const
+    {
+        if (IsContextLost())
+            return false;
+
+        if (!object)
+            return false;
+
+        if (!object->IsCompatibleWithContext(this))
+            return false;
 
-    // Returns false if `object` is null or not valid, but considers deleted
-    // objects valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowDeleted(const char* info, const ObjectType* object);
+        if (object->IsDeleted())
+            return false;
+
+        return true;
+    }
+
+    bool ValidateDeleteObject(const char* funcName, const WebGLDeletableObject* object) {
+        if (IsContextLost())
+            return false;
 
-private:
-    // Like ValidateObject, but only for cases when `object` is known to not be
-    // null already.
-    template<class ObjectType>
-    bool ValidateObjectAssumeNonNull(const char* info, const ObjectType* object);
+        if (!object)
+            return false;
+
+        if (!ValidateObjectAllowDeleted(funcName, *object))
+            return false;
+
+        if (object->IsDeleteRequested())
+            return false;
+
+        return true;
+    }
+
+    ////
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual WebGLVertexArray* CreateVertexArrayImpl();
 
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
@@ -1944,92 +2006,16 @@ public:
 
 // used by DOM bindings in conjunction with GetParentObject
 inline nsISupports*
 ToSupports(WebGLContext* webgl)
 {
     return static_cast<nsIDOMWebGLRenderingContext*>(webgl);
 }
 
-/**
- ** Template implementations
- **/
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object)
-{
-    if (object && !object->IsCompatibleWithContext(this)) {
-        ErrorInvalidOperation("%s: object from different WebGL context "
-                              "(or older generation of this one) "
-                              "passed as argument", info);
-        return false;
-    }
-
-    return true;
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAssumeNonNull(const char* info, const ObjectType* object)
-{
-    MOZ_ASSERT(object);
-
-    if (!ValidateObjectAllowDeletedOrNull(info, object))
-        return false;
-
-    if (object->IsDeleted()) {
-        ErrorInvalidValue("%s: Deleted object passed as argument.", info);
-        return false;
-    }
-
-    return true;
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowNull(const char* info, const ObjectType* object)
-{
-    if (!object)
-        return true;
-
-    return ValidateObjectAssumeNonNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowDeleted(const char* info, const ObjectType* object)
-{
-    if (!object) {
-        ErrorInvalidValue("%s: null object passed as argument", info);
-        return false;
-    }
-
-    return ValidateObjectAllowDeletedOrNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObject(const char* info, const ObjectType* object)
-{
-    if (!object) {
-        ErrorInvalidValue("%s: null object passed as argument", info);
-        return false;
-    }
-
-    return ValidateObjectAssumeNonNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectRef(const char* info, const ObjectType& object)
-{
-    return ValidateObjectAssumeNonNull(info, &object);
-}
-
 // Returns `value` rounded to the next highest multiple of `multiple`.
 // AKA PadToAlignment, StrideForAlignment.
 template<typename V, typename M>
 V
 RoundUpToMultipleOf(const V& value, const M& multiple)
 {
     return ((value + multiple - 1) / multiple) * multiple;
 }
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -114,22 +114,19 @@ WebGLContext::ValidateIndexedBufferSlot(
 
 void
 WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
 {
     const char funcName[] = "bindBuffer";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     const auto& slot = ValidateBufferSlot(funcName, target);
     if (!slot)
         return;
 
     if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
         return;
 
     gl->MakeCurrent();
@@ -178,22 +175,19 @@ WebGLContext::ValidateIndexedBufferBindi
 
 void
 WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
     const char funcName[] = "bindBufferBase";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
     if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
                                       &indexedBinding))
     {
         return;
     }
 
@@ -229,22 +223,19 @@ WebGLContext::BindBufferBase(GLenum targ
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
     const char funcName[] = "bindBufferRange";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     if (!ValidateNonNegative(funcName, "offset", offset) ||
         !ValidateNonNegative(funcName, "size", size))
     {
         return;
     }
 
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
@@ -471,23 +462,17 @@ WebGLContext::CreateBuffer()
 
     RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
     return globj.forget();
 }
 
 void
 WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer))
-        return;
-
-    if (!buffer || buffer->IsDeleted())
+    if (!ValidateDeleteObject("deleteBuffer", buffer))
         return;
 
     ////
 
     const auto fnClearIfBuffer = [&](WebGLRefPtr<WebGLBuffer>& bindPoint) {
         if (bindPoint == buffer) {
             bindPoint = nullptr;
         }
@@ -525,22 +510,16 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
     ////
 
     buffer->RequestDelete();
 }
 
 bool
 WebGLContext::IsBuffer(WebGLBuffer* buffer)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isBuffer", buffer))
-        return false;
-
-    if (buffer->IsDeleted())
+    if (!ValidateIsObject("isBuffer", buffer))
         return false;
 
     MakeContextCurrent();
     return gl->fIsBuffer(buffer->mGLName);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -52,16 +52,28 @@
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 
+bool
+WebGLContext::ValidateObject(const char* funcName, const WebGLProgram& object)
+{
+    return ValidateObject(funcName, object, true);
+}
+
+bool
+WebGLContext::ValidateObject(const char* funcName, const WebGLShader& object)
+{
+    return ValidateObject(funcName, object, true);
+}
+
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 //
 //  WebGL API
 //
 
@@ -87,53 +99,50 @@ WebGLContext::ActiveTexture(GLenum textu
 }
 
 void
 WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("attachShader: program", program) ||
-        !ValidateObjectRef("attachShader: shader", shader))
+    if (!ValidateObject("attachShader: program", program) ||
+        !ValidateObject("attachShader: shader", shader))
     {
         return;
     }
 
     program.AttachShader(&shader);
 }
 
 void
 WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
                                  const nsAString& name)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("bindAttribLocation: program", prog))
+    if (!ValidateObject("bindAttribLocation: program", prog))
         return;
 
     prog.BindAttribLocation(location, name);
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb))
+    if (wfb && !ValidateObject("bindFramebuffer", *wfb))
         return;
 
-    if (wfb && wfb->IsDeleted())
-        return ErrorInvalidOperation("bindFramebuffer: Cannot bind a deleted object.");
-
     MakeContextCurrent();
 
     if (!wfb) {
         gl->fBindFramebuffer(target, 0);
     } else {
         GLuint framebuffername = wfb->mGLName;
         gl->fBindFramebuffer(target, framebuffername);
 #ifdef ANDROID
@@ -161,22 +170,19 @@ void
 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
 {
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_RENDERBUFFER)
         return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
 
-    if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
+    if (wrb && !ValidateObject("bindRenderbuffer", *wrb))
         return;
 
-    if (wrb && wrb->IsDeleted())
-        return ErrorInvalidOperation("bindRenderbuffer: Cannot bind a deleted object.");
-
     // Usually, we would now call into glBindRenderbuffer. However, since we have to
     // potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
     // we know we should bind here.
     // Instead, we do all renderbuffer binding lazily.
 
     if (wrb) {
         wrb->mHasBeenBound = true;
     }
@@ -315,23 +321,17 @@ WebGLContext::CullFace(GLenum face)
 
     MakeContextCurrent();
     gl->fCullFace(face);
 }
 
 void
 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteFramebuffer", fbuf))
-        return;
-
-    if (!fbuf || fbuf->IsDeleted())
+    if (!ValidateDeleteObject("deleteFramebuffer", fbuf))
         return;
 
     fbuf->RequestDelete();
 
     if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer == fbuf) {
             BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
                             static_cast<WebGLFramebuffer*>(nullptr));
@@ -343,23 +343,17 @@ WebGLContext::DeleteFramebuffer(WebGLFra
         BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
                         static_cast<WebGLFramebuffer*>(nullptr));
     }
 }
 
 void
 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteRenderbuffer", rbuf))
-        return;
-
-    if (!rbuf || rbuf->IsDeleted())
+    if (!ValidateDeleteObject("deleteRenderbuffer", rbuf))
         return;
 
     if (mBoundDrawFramebuffer)
         mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
 
     if (mBoundReadFramebuffer)
         mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
 
@@ -369,23 +363,17 @@ WebGLContext::DeleteRenderbuffer(WebGLRe
         BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
 
     rbuf->RequestDelete();
 }
 
 void
 WebGLContext::DeleteTexture(WebGLTexture* tex)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteTexture", tex))
-        return;
-
-    if (!tex || tex->IsDeleted())
+    if (!ValidateDeleteObject("deleteTexture", tex))
         return;
 
     if (mBoundDrawFramebuffer)
         mBoundDrawFramebuffer->DetachTexture(tex);
 
     if (mBoundReadFramebuffer)
         mBoundReadFramebuffer->DetachTexture(tex);
 
@@ -403,53 +391,41 @@ WebGLContext::DeleteTexture(WebGLTexture
     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
 WebGLContext::DeleteProgram(WebGLProgram* prog)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteProgram", prog))
-        return;
-
-    if (!prog || prog->IsDeleted())
+    if (!ValidateDeleteObject("deleteProgram", prog))
         return;
 
     prog->RequestDelete();
 }
 
 void
 WebGLContext::DeleteShader(WebGLShader* shader)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteShader", shader))
-        return;
-
-    if (!shader || shader->IsDeleted())
+    if (!ValidateDeleteObject("deleteShader", shader))
         return;
 
     shader->RequestDelete();
 }
 
 void
 WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
     // It's valid to attempt to detach a deleted shader, since it's still a
     // shader.
-    if (!ValidateObjectRef("detachShader: program", program) ||
-        !ValidateObjectAllowDeleted("detachShader: shader", &shader))
+    if (!ValidateObject("detachShader: program", program) ||
+        !ValidateObjectAllowDeleted("detachShader: shader", shader))
     {
         return;
     }
 
     program.DetachShader(&shader);
 }
 
 void
@@ -564,55 +540,55 @@ WebGLContext::FrontFace(GLenum mode)
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getActiveAttrib: program", prog))
+    if (!ValidateObject("getActiveAttrib: program", prog))
         return nullptr;
 
     return prog.GetActiveAttrib(index);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getActiveUniform: program", prog))
+    if (!ValidateObject("getActiveUniform: program", prog))
         return nullptr;
 
     return prog.GetActiveUniform(index);
 }
 
 void
 WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
                                  dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getAttachedShaders", prog))
+    if (!ValidateObject("getAttachedShaders", prog))
         return;
 
     prog.GetAttachedShaders(&retval.SetValue());
 }
 
 GLint
 WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObjectRef("getAttribLocation: program", prog))
+    if (!ValidateObject("getAttribLocation: program", prog))
         return -1;
 
     return prog.GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
@@ -905,62 +881,62 @@ WebGLContext::GetError()
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getProgramParameter: program", &prog))
+    if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
         return JS::NullValue();
 
     return prog.GetProgramParameter(pname);
 }
 
 void
 WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getProgramInfoLog: program", prog))
+    if (!ValidateObject("getProgramInfoLog: program", prog))
         return;
 
     prog.GetProgramInfoLog(&retval);
 }
 
 JS::Value
 WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
                          const WebGLUniformLocation& loc)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getUniform: `program`", prog))
+    if (!ValidateObject("getUniform: `program`", prog))
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getUniform: `location`", loc))
+    if (!ValidateObjectAllowDeleted("getUniform: `location`", loc))
         return JS::NullValue();
 
     if (!loc.ValidateForProgram(&prog, "getUniform"))
         return JS::NullValue();
 
     return loc.GetUniform(js);
 }
 
 already_AddRefed<WebGLUniformLocation>
 WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getUniformLocation: program", prog))
+    if (!ValidateObject("getUniformLocation: program", prog))
         return nullptr;
 
     return prog.GetUniformLocation(name);
 }
 
 void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
@@ -990,80 +966,67 @@ WebGLContext::Hint(GLenum target, GLenum
     if (!isValid)
         return ErrorInvalidEnum("hint: invalid hint");
 
     MakeContextCurrent();
     gl->fHint(target, mode);
 }
 
 bool
-WebGLContext::IsFramebuffer(WebGLFramebuffer* fb)
+WebGLContext::IsFramebuffer(const WebGLFramebuffer* fb)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isFramebuffer", fb))
-        return false;
-
-    if (fb->IsDeleted())
+    if (!ValidateIsObject("isFramebuffer", fb))
         return false;
 
 #ifdef ANDROID
     if (gl->WorkAroundDriverBugs() &&
         gl->Renderer() == GLRenderer::AndroidEmulator)
     {
         return fb->mIsFB;
     }
 #endif
 
     MakeContextCurrent();
     return gl->fIsFramebuffer(fb->mGLName);
 }
 
 bool
-WebGLContext::IsProgram(WebGLProgram* prog)
+WebGLContext::IsProgram(const WebGLProgram* prog)
 {
-    if (IsContextLost())
+    if (!ValidateIsObject("isProgram", prog))
         return false;
 
-    return ValidateObjectAllowDeleted("isProgram", prog) && !prog->IsDeleted();
+    return true;
 }
 
 bool
-WebGLContext::IsRenderbuffer(WebGLRenderbuffer* rb)
+WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* rb)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isRenderBuffer", rb))
-        return false;
-
-    if (rb->IsDeleted())
+    if (!ValidateIsObject("isRenderbuffer", rb))
         return false;
 
     return rb->mHasBeenBound;
 }
 
 bool
-WebGLContext::IsShader(WebGLShader* shader)
+WebGLContext::IsShader(const WebGLShader* shader)
 {
-    if (IsContextLost())
+    if (!ValidateIsObject("isShader", shader))
         return false;
 
-    return ValidateObjectAllowDeleted("isShader", shader) &&
-        !shader->IsDeleted();
+    return true;
 }
 
 void
 WebGLContext::LinkProgram(WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("linkProgram", prog))
+    if (!ValidateObject("linkProgram", prog))
         return;
 
     prog.LinkProgram();
 
     if (!prog.IsLinked()) {
         // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
         // null out mActiveProgramLinkInfo.
         return;
@@ -2096,32 +2059,32 @@ WebGLContext::UseProgram(WebGLProgram* p
         return;
 
     if (!prog) {
         mCurrentProgram = nullptr;
         mActiveProgramLinkInfo = nullptr;
         return;
     }
 
-    if (!ValidateObject("useProgram", prog))
+    if (!ValidateObject("useProgram", *prog))
         return;
 
     if (prog->UseProgram()) {
         mCurrentProgram = prog;
         mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
     }
 }
 
 void
 WebGLContext::ValidateProgram(const WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("validateProgram", prog))
+    if (!ValidateObject("validateProgram", prog))
         return;
 
     prog.ValidateProgram();
 }
 
 already_AddRefed<WebGLFramebuffer>
 WebGLContext::CreateFramebuffer()
 {
@@ -2166,43 +2129,43 @@ WebGLContext::Viewport(GLint x, GLint y,
 }
 
 void
 WebGLContext::CompileShader(WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("compileShader", shader))
+    if (!ValidateObject("compileShader", shader))
         return;
 
     shader.CompileShader();
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getShaderParameter: shader", shader))
+    if (!ValidateObjectAllowDeleted("getShaderParameter: shader", shader))
         return JS::NullValue();
 
     return shader.GetShaderParameter(pname);
 }
 
 void
 WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getShaderInfoLog: shader", shader))
+    if (!ValidateObject("getShaderInfoLog: shader", shader))
         return;
 
     shader.GetShaderInfoLog(&retval);
 }
 
 already_AddRefed<WebGLShaderPrecisionFormat>
 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
 {
@@ -2254,29 +2217,29 @@ WebGLContext::GetShaderPrecisionFormat(G
 void
 WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getShaderSource: shader", shader))
+    if (!ValidateObject("getShaderSource: shader", shader))
         return;
 
     shader.GetShaderSource(&retval);
 }
 
 void
 WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("shaderSource: shader", shader))
+    if (!ValidateObject("shaderSource: shader", shader))
         return;
 
     shader.ShaderSource(source);
 }
 
 void
 WebGLContext::LoseContext()
 {
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -204,17 +204,17 @@ WebGLContext::InvalidateResolveCacheForT
 // GL calls
 
 void
 WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
 {
     if (IsContextLost())
         return;
 
-     if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
+     if (newTex && !ValidateObject("bindTexture", *newTex))
         return;
 
     // Need to check rawTarget first before comparing against newTex->Target() as
     // newTex->Target() returns a TexTarget, which will assert on invalid value.
     WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
     switch (rawTarget) {
     case LOCAL_GL_TEXTURE_2D:
         currentTexPtr = &mBound2DTextures[mActiveTexture];
@@ -285,20 +285,17 @@ WebGLContext::GetTexParameter(GLenum raw
     }
 
     return tex->GetTexParameter(texTarget, pname);
 }
 
 bool
 WebGLContext::IsTexture(WebGLTexture* tex)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isTexture", tex))
+    if (!ValidateIsObject("isTexture", tex))
         return false;
 
     return tex->IsTexture();
 }
 
 void
 WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
                                 GLfloat* maybeFloatParam)
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -202,17 +202,17 @@ WebGLContext::ValidateUniformLocation(We
     /* GLES 2.0.25, p38:
      *   If the value of location is -1, the Uniform* commands will silently
      *   ignore the data passed in, and the current uniform values will not be
      *   changed.
      */
     if (!loc)
         return false;
 
-    if (!ValidateObject(funcName, loc))
+    if (!ValidateObjectAllowDeleted(funcName, *loc))
         return false;
 
     if (!mCurrentProgram) {
         ErrorInvalidOperation("%s: No program is currently bound.", funcName);
         return false;
     }
 
     return loc->ValidateForProgram(mCurrentProgram, funcName);
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -13,30 +13,19 @@
 namespace mozilla {
 
 void
 WebGLContext::BindVertexArray(WebGLVertexArray* array)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindVertexArrayObject", array))
+    if (array && !ValidateObject("bindVertexArrayObject", *array))
         return;
 
-    if (array && array->IsDeleted()) {
-        /* http://www.khronos.org/registry/gles/extensions/OES/OES_vertex_array_object.txt
-         * BindVertexArrayOES fails and an INVALID_OPERATION error is
-         * generated if array is not a name returned from a previous call to
-         * GenVertexArraysOES, or if such a name has since been deleted with
-         * DeleteVertexArraysOES
-         */
-        ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
-        return;
-    }
-
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
     if (array == nullptr) {
         array = mDefaultVertexArray;
     }
 
@@ -63,43 +52,28 @@ WebGLVertexArray*
 WebGLContext::CreateVertexArrayImpl()
 {
     return WebGLVertexArray::Create(this);
 }
 
 void
 WebGLContext::DeleteVertexArray(WebGLVertexArray* array)
 {
-    if (IsContextLost())
-        return;
-
-    if (array == nullptr)
-        return;
-
-    if (array->IsDeleted())
+    if (!ValidateDeleteObject("deleteVertexArray", array))
         return;
 
     if (mBoundVertexArray == array)
         BindVertexArray(static_cast<WebGLVertexArray*>(nullptr));
 
     array->RequestDelete();
 }
 
 bool
-WebGLContext::IsVertexArray(WebGLVertexArray* array)
+WebGLContext::IsVertexArray(const WebGLVertexArray* array)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!array)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isVertexArray", array))
-        return false;
-
-    if (array->IsDeleted())
+    if (!ValidateIsObject("isVertexArray", array))
         return false;
 
     MakeContextCurrent();
     return array->IsVertexArray();
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDebugShaders.cpp
+++ b/dom/canvas/WebGLExtensionDebugShaders.cpp
@@ -33,17 +33,17 @@ WebGLExtensionDebugShaders::GetTranslate
         mContext->ErrorInvalidOperation("%s: Extension is lost.",
                                         "getTranslatedShaderSource");
         return;
     }
 
     if (mContext->IsContextLost())
         return;
 
-    if (!mContext->ValidateObjectRef("getShaderTranslatedSource: shader", shader))
+    if (!mContext->ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
     shader.GetShaderTranslatedSource(&retval);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugShaders, WEBGL_debug_shaders)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -78,17 +78,17 @@ WebGLExtensionDisjointTimerQuery::EndQue
 
 void
 WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery& query, GLenum target) const
 {
     const char funcName[] = "queryCounterEXT";
     if (mIsLost)
         return;
 
-    if (!mContext->ValidateObjectRef(funcName, query))
+    if (!mContext->ValidateObject(funcName, query))
         return;
 
     query.QueryCounter(funcName, target);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
                                               JS::MutableHandleValue retval) const
--- a/dom/canvas/WebGLExtensionVertexArray.cpp
+++ b/dom/canvas/WebGLExtensionVertexArray.cpp
@@ -36,17 +36,17 @@ WebGLExtensionVertexArray::DeleteVertexA
 {
     if (mIsLost)
         return;
 
     mContext->DeleteVertexArray(array);
 }
 
 bool
-WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
+WebGLExtensionVertexArray::IsVertexArrayOES(const WebGLVertexArray* array)
 {
     if (mIsLost)
         return false;
 
     return mContext->IsVertexArray(array);
 }
 
 void
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -321,17 +321,17 @@ class WebGLExtensionVertexArray
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionVertexArray(WebGLContext* webgl);
     virtual ~WebGLExtensionVertexArray();
 
     already_AddRefed<WebGLVertexArray> CreateVertexArrayOES();
     void DeleteVertexArrayOES(WebGLVertexArray* array);
-    bool IsVertexArrayOES(WebGLVertexArray* array);
+    bool IsVertexArrayOES(const WebGLVertexArray* array);
     void BindVertexArrayOES(WebGLVertexArray* array);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionInstancedArrays
     : public WebGLExtensionBase
 {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -599,17 +599,17 @@ WebGLFBAttachPoint::GetParameter(const c
     return JS::Int32Value(ret);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(fbo)
 #ifdef ANDROID
     , mIsFB(false)
 #endif
     , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
 {
@@ -1296,17 +1296,17 @@ WebGLFramebuffer::FramebufferRenderbuffe
 
     // `rbTarget`
     if (rbtarget != LOCAL_GL_RENDERBUFFER) {
         mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:", rbtarget);
         return;
     }
 
     // `rb`
-    if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: rb", rb))
+    if (rb && !mContext->ValidateObject("framebufferRenderbuffer: rb", *rb))
         return;
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
         mDepthAttachment.SetRenderbuffer(rb);
         mStencilAttachment.SetRenderbuffer(rb);
     } else {
@@ -1338,20 +1338,20 @@ WebGLFramebuffer::FramebufferTexture2D(c
          texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
     {
         mContext->ErrorInvalidEnumInfo("framebufferTexture2D: texImageTarget:",
                                        texImageTarget);
         return;
     }
 
     // `texture`
-    if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", tex))
-        return;
+    if (tex) {
+        if (!mContext->ValidateObject("framebufferTexture2D: texture", *tex))
+            return;
 
-    if (tex) {
         if (!tex->HasEverBeenBound()) {
             mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
                                             funcName);
             return;
         }
 
         const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
         if (tex->Target() != destTexTarget) {
@@ -1414,34 +1414,35 @@ WebGLFramebuffer::FramebufferTextureLaye
     // `attachment`
     const auto maybeAttach = GetAttachPoint(attachEnum);
     if (!maybeAttach || !maybeAttach.value()) {
         mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
         return;
     }
     const auto& attach = maybeAttach.value();
 
-    // `texture`
-    if (!mContext->ValidateObjectAllowNull("framebufferTextureLayer: texture", tex))
-        return;
-
-    if (tex && !tex->HasEverBeenBound()) {
-        mContext->ErrorInvalidOperation("%s: `texture` has never been bound.", funcName);
-        return;
-    }
-
     // `level`, `layer`
     if (layer < 0)
         return mContext->ErrorInvalidValue("%s: `layer` must be >= 0.", funcName);
 
     if (level < 0)
         return mContext->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
 
+    // `texture`
     TexImageTarget texImageTarget = LOCAL_GL_TEXTURE_3D;
     if (tex) {
+        if (!mContext->ValidateObject("framebufferTextureLayer: texture", *tex))
+            return;
+
+        if (!tex->HasEverBeenBound()) {
+            mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
+                                            funcName);
+            return;
+        }
+
         texImageTarget = tex->Target().get();
         switch (texImageTarget.get()) {
         case LOCAL_GL_TEXTURE_3D:
             if (uint32_t(layer) >= mContext->mImplMax3DTextureSize) {
                 mContext->ErrorInvalidValue("%s: `layer` must be < %s.", funcName,
                                             "MAX_3D_TEXTURE_SIZE");
                 return;
             }
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -127,17 +127,16 @@ public:
         }
     };
 };
 
 class WebGLFramebuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
-    , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
 {
     friend class WebGLContext;
 
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     const GLuint mGLName;
--- a/dom/canvas/WebGLObjectModel.h
+++ b/dom/canvas/WebGLObjectModel.h
@@ -7,18 +7,65 @@
 #define WEBGLOBJECTMODEL_H_
 
 #include "nsCycleCollectionNoteChild.h"
 
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
+template<typename> class LinkedList;
 class WebGLContext;
 
+////
+
+// This class is a mixin for objects that are tied to a specific
+// context (which is to say, all of them).  They provide initialization
+// as well as comparison with the current context.
+class WebGLContextBoundObject
+{
+public:
+    WebGLContext* const mContext;
+private:
+    const uint32_t mContextGeneration;
+
+public:
+    explicit WebGLContextBoundObject(WebGLContext* webgl);
+
+    bool IsCompatibleWithContext(const WebGLContext* other) const;
+};
+
+////
+
+class WebGLDeletableObject : public WebGLContextBoundObject
+{
+    template<typename> friend class WebGLRefCountedObject;
+
+private:
+    enum DeletionStatus { Default, DeleteRequested, Deleted };
+
+    DeletionStatus mDeletionStatus;
+
+    ////
+
+    explicit WebGLDeletableObject(WebGLContext* webgl)
+      : WebGLContextBoundObject(webgl)
+      , mDeletionStatus(Default)
+    { }
+
+    ~WebGLDeletableObject() {
+        MOZ_ASSERT(mDeletionStatus == Deleted,
+                   "Derived class destructor must call DeleteOnce().");
+    }
+
+public:
+    bool IsDeleted() const { return mDeletionStatus == Deleted; }
+    bool IsDeleteRequested() const { return mDeletionStatus != Default; }
+};
+
 /* Each WebGL object class WebGLFoo wants to:
  *  - inherit WebGLRefCountedObject<WebGLFoo>
  *  - implement a Delete() method
  *  - have its destructor call DeleteOnce()
  *
  * This base class provides two features to WebGL object types:
  * 1. support for OpenGL object reference counting
  * 2. support for OpenGL deletion statuses
@@ -86,32 +133,35 @@ class WebGLContext;
  * derived class were final, but that would be impossible to enforce and would
  * lead to strange bugs if it were subclassed.
  *
  * This WebGLRefCountedObject class takes the Derived type as template
  * parameter, as a means to allow DeleteOnce to call Delete() on the Derived
  * class, without either method being virtual. This is a common C++ pattern
  * known as the "curiously recursive template pattern (CRTP)".
  */
+
 template<typename Derived>
-class WebGLRefCountedObject
+class WebGLRefCountedObject : public WebGLDeletableObject
 {
+    friend class WebGLContext;
+    template<typename T> friend void ClearLinkedList(LinkedList<T>& list);
+
+private:
+    nsAutoRefCnt mWebGLRefCnt;
+
 public:
-    enum DeletionStatus { Default, DeleteRequested, Deleted };
-
-    WebGLRefCountedObject()
-      : mDeletionStatus(Default)
-    {}
+    explicit WebGLRefCountedObject(WebGLContext* webgl)
+        : WebGLDeletableObject(webgl)
+    { }
 
     ~WebGLRefCountedObject() {
         MOZ_ASSERT(mWebGLRefCnt == 0,
                    "Destroying WebGL object still referenced by other WebGL"
                    " objects.");
-        MOZ_ASSERT(mDeletionStatus == Deleted,
-                   "Derived class destructor must call DeleteOnce().");
     }
 
     // called by WebGLRefPtr
     void WebGLAddRef() {
         ++mWebGLRefCnt;
     }
 
     // called by WebGLRefPtr
@@ -124,43 +174,32 @@ public:
 
     // this is the function that WebGL.deleteXxx() functions want to call
     void RequestDelete() {
         if (mDeletionStatus == Default)
             mDeletionStatus = DeleteRequested;
         MaybeDelete();
     }
 
-    bool IsDeleted() const {
-        return mDeletionStatus == Deleted;
-    }
-
-    bool IsDeleteRequested() const {
-        return mDeletionStatus != Default;
-    }
-
+protected:
     void DeleteOnce() {
         if (mDeletionStatus != Deleted) {
             static_cast<Derived*>(this)->Delete();
             mDeletionStatus = Deleted;
         }
     }
 
 private:
     void MaybeDelete() {
         if (mWebGLRefCnt == 0 &&
             mDeletionStatus == DeleteRequested)
         {
             DeleteOnce();
         }
     }
-
-protected:
-    nsAutoRefCnt mWebGLRefCnt;
-    DeletionStatus mDeletionStatus;
 };
 
 /* This WebGLRefPtr class is meant to be used for references between WebGL
  * objects. For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's
  * attached to it.
  *
  * Why the need for a separate refptr class? The only special thing that
  * WebGLRefPtr does is that it increments and decrements the WebGL refcount of
@@ -254,31 +293,16 @@ private:
         mRawPtr = newPtr;
         ReleasePtr(oldPtr);
     }
 
 protected:
     T* mRawPtr;
 };
 
-// This class is a mixin for objects that are tied to a specific
-// context (which is to say, all of them).  They provide initialization
-// as well as comparison with the current context.
-class WebGLContextBoundObject
-{
-public:
-    explicit WebGLContextBoundObject(WebGLContext* webgl);
-
-    bool IsCompatibleWithContext(const WebGLContext* other) const;
-
-    WebGLContext* const mContext;
-protected:
-    const uint32_t mContextGeneration;
-};
-
 // this class is a mixin for GL objects that have dimensions
 // that we need to track.
 class WebGLRectangleObject
 {
 public:
     WebGLRectangleObject()
         : mWidth(0)
         , mHeight(0)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -438,17 +438,17 @@ webgl::LinkedProgramInfo::~LinkedProgram
 static GLuint
 CreateProgram(gl::GLContext* gl)
 {
     gl->MakeCurrent();
     return gl->fCreateProgram();
 }
 
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(CreateProgram(webgl->GL()))
     , mNumActiveTFOs(0)
     , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_SEPARATE_ATTRIBS)
 {
     mContext->mPrograms.insertBack(this);
 }
 
 WebGLProgram::~WebGLProgram()
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -125,17 +125,16 @@ struct LinkedProgramInfo final
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
-    , public WebGLContextBoundObject
 {
     friend class WebGLTransformFeedback;
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
 
     explicit WebGLProgram(WebGLContext* webgl);
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -35,17 +35,17 @@ GenQuery(gl::GLContext* gl)
     gl->MakeCurrent();
 
     GLuint ret = 0;
     gl->fGenQueries(1, &ret);
     return ret;
 }
 
 WebGLQuery::WebGLQuery(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(GenQuery(mContext->gl))
     , mTarget(0)
     , mActiveSlot(nullptr)
     , mCanBeAvailable(false)
 {
     mContext->mQueries.insertBack(this);
 }
 
@@ -206,30 +206,28 @@ WebGLQuery::GetQueryParameter(GLenum pna
     default:
         MOZ_CRASH("Bad `pname`.");
     }
 }
 
 bool
 WebGLQuery::IsQuery() const
 {
-    if (IsDeleted())
-        return false;
+    MOZ_ASSERT(!IsDeleted());
 
     if (!mTarget)
         return false;
 
     return true;
 }
 
 void
 WebGLQuery::DeleteQuery()
 {
-    if (IsDeleted())
-        return;
+    MOZ_ASSERT(!IsDeleteRequested());
 
     if (mActiveSlot) {
         EndQuery();
     }
 
     RequestDelete();
 }
 
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -13,17 +13,16 @@
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 class WebGLQuery final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLQuery>
     , public LinkedListElement<WebGLQuery>
-    , public WebGLContextBoundObject
 {
     friend class AvailableRunnable;
     friend class WebGLRefCountedObject<WebGLQuery>;
 
 public:
     const GLuint mGLName;
 private:
     GLenum mTarget;
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -42,17 +42,17 @@ DoCreateRenderbuffer(gl::GLContext* gl)
 
 static bool
 EmulatePackedDepthStencil(gl::GLContext* gl)
 {
     return !gl->IsSupported(gl::GLFeature::packed_depth_stencil);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mPrimaryRB( DoCreateRenderbuffer(webgl->gl) )
     , mEmulatePackedDepthStencil( EmulatePackedDepthStencil(webgl->gl) )
     , mSecondaryRB(0)
     , mFormat(nullptr)
     , mSamples(0)
     , mImageDataStatus(WebGLImageDataStatus::NoImageData)
     , mHasBeenBound(false)
 {
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -18,17 +18,16 @@ namespace webgl {
 struct FormatUsageInfo;
 }
 
 class WebGLRenderbuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
-    , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
     friend class WebGLContext;
     friend class WebGLFramebuffer;
     friend class WebGLFBAttachPoint;
 
 public:
     const GLuint mPrimaryRB;
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSampler::WebGLSampler(WebGLContext* webgl, GLuint sampler)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(sampler)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mWrapR(LOCAL_GL_REPEAT)
     , mMinLod(-1000)
     , mMaxLod(1000)
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -12,17 +12,16 @@
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 class WebGLSampler final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
     friend class WebGLTexture;
 
 public:
     WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
     const GLuint mGLName;
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -136,17 +136,17 @@ GetCompilationStatusAndLog(gl::GLContext
 static GLuint
 CreateShader(gl::GLContext* gl, GLenum type)
 {
     gl->MakeCurrent();
     return gl->fCreateShader(type);
 }
 
 WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(CreateShader(webgl->GL(), type))
     , mType(type)
     , mTranslationSuccessful(false)
     , mCompilationSuccessful(false)
 {
     mContext->mShaders.insertBack(this);
 }
 
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -23,17 +23,16 @@ namespace mozilla {
 namespace webgl {
 class ShaderValidator;
 } // namespace webgl
 
 class WebGLShader final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLShader>
     , public LinkedListElement<WebGLShader>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGLProgram;
 
 public:
     WebGLShader(WebGLContext* webgl, GLenum type);
 
 protected:
--- a/dom/canvas/WebGLSync.cpp
+++ b/dom/canvas/WebGLSync.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSync::WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
 {
    mContext->mSyncs.insertBack(this);
    mGLName = mContext->gl->fFenceSync(condition, flags);
 }
 
 WebGLSync::~WebGLSync()
 {
     DeleteOnce();
--- a/dom/canvas/WebGLSync.h
+++ b/dom/canvas/WebGLSync.h
@@ -11,17 +11,16 @@
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLSync final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSync>
     , public LinkedListElement<WebGLSync>
-    , public WebGLContextBoundObject
 {
     friend class WebGL2Context;
 
 public:
     WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
 
     void Delete();
     WebGLContext* GetParentObject() const;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -122,17 +122,17 @@ WebGLTexture::ImageInfo::SetIsDataInitia
 ////////////////////////////////////////
 
 JSObject*
 WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
     return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(tex)
     , mTarget(LOCAL_GL_NONE)
     , mFaceCount(0)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mImmutable(false)
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -52,17 +52,16 @@ DoesTargetMatchDimensions(WebGLContext* 
 
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
-    , public WebGLContextBoundObject
 {
     // Friends
     friend class WebGLContext;
     friend class WebGLFramebuffer;
 
     ////////////////////////////////////
     // Members
 public:
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGL2Context.h"
 
 namespace mozilla {
 
 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(tf)
     , mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
     , mIsPaused(false)
     , mIsActive(false)
     , mBuffersForTF_Dirty(true)
 {
     mContext->mTransformFeedbacks.insertBack(this);
 }
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -11,17 +11,16 @@
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLTransformFeedback final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
-    , public WebGLContextBoundObject
 {
     friend class ScopedDrawWithTransformFeedback;
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLProgram;
 
 public:
     const GLuint mGLName;
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -16,17 +16,17 @@ namespace mozilla {
 
 JSObject*
 WebGLVertexArray::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLVertexArrayObjectBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(0)
 {
     mContext->mVertexArrays.insertBack(this);
 }
 
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* webgl)
 {
@@ -45,17 +45,17 @@ WebGLVertexArray::Delete()
     DeleteImpl();
 
     LinkedListElement<WebGLVertexArray>::removeFrom(mContext->mVertexArrays);
     mElementArrayBuffer = nullptr;
     mAttribs.Clear();
 }
 
 bool
-WebGLVertexArray::IsVertexArray()
+WebGLVertexArray::IsVertexArray() const
 {
     return IsVertexArrayImpl();
 }
 
 void
 WebGLVertexArray::EnsureAttrib(GLuint index)
 {
     MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs));
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -18,17 +18,16 @@
 namespace mozilla {
 
 class WebGLVertexArrayFake;
 
 class WebGLVertexArray
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
-    , public WebGLContextBoundObject
 {
 public:
     static WebGLVertexArray* Create(WebGLContext* webgl);
 
     void BindVertexArray() {
         // Bind to dummy value to signal that this vertex array has ever been
         // bound.
         BindVertexArrayImpl();
@@ -39,17 +38,17 @@ public:
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) const {
         return HasAttrib(index) && mAttribs[index].mEnabled;
     }
 
     // Implement parent classes:
     void Delete();
-    bool IsVertexArray();
+    bool IsVertexArray() const;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
@@ -62,17 +61,17 @@ protected:
 
     virtual ~WebGLVertexArray() {
         MOZ_ASSERT(IsDeleted());
     }
 
     virtual void GenVertexArray() = 0;
     virtual void BindVertexArrayImpl() = 0;
     virtual void DeleteImpl() = 0;
-    virtual bool IsVertexArrayImpl() = 0;
+    virtual bool IsVertexArrayImpl() const = 0;
 
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
     friend class WebGLContext;
     friend class WebGLVertexArrayFake;
     friend class WebGL2Context;
--- a/dom/canvas/WebGLVertexArrayFake.cpp
+++ b/dom/canvas/WebGLVertexArrayFake.cpp
@@ -57,14 +57,14 @@ WebGLVertexArrayFake::BindVertexArrayImp
 
 void
 WebGLVertexArrayFake::DeleteImpl()
 {
     mIsVAO = false;
 }
 
 bool
-WebGLVertexArrayFake::IsVertexArrayImpl()
+WebGLVertexArrayFake::IsVertexArrayImpl() const
 {
     return mIsVAO;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLVertexArrayFake.h
+++ b/dom/canvas/WebGLVertexArrayFake.h
@@ -14,17 +14,17 @@ class WebGLVertexArrayFake final
     : public WebGLVertexArray
 {
     friend class WebGLVertexArray;
 
 protected:
     virtual void BindVertexArrayImpl() override;
     virtual void DeleteImpl() override;
     virtual void GenVertexArray() override {};
-    virtual bool IsVertexArrayImpl() override;
+    virtual bool IsVertexArrayImpl() const override;
 
 private:
     explicit WebGLVertexArrayFake(WebGLContext* webgl);
 
     ~WebGLVertexArrayFake() {
         DeleteOnce();
     }
 
--- a/dom/canvas/WebGLVertexArrayGL.cpp
+++ b/dom/canvas/WebGLVertexArrayGL.cpp
@@ -42,17 +42,17 @@ WebGLVertexArrayGL::BindVertexArrayImpl(
 
 void
 WebGLVertexArrayGL::GenVertexArray()
 {
     mContext->gl->fGenVertexArrays(1, &mGLName);
 }
 
 bool
-WebGLVertexArrayGL::IsVertexArrayImpl()
+WebGLVertexArrayGL::IsVertexArrayImpl() const
 {
     gl::GLContext* gl = mContext->gl;
     if (gl->WorkAroundDriverBugs())
     {
         return mIsVAO;
     }
 
     mContext->MakeContextCurrent();
--- a/dom/canvas/WebGLVertexArrayGL.h
+++ b/dom/canvas/WebGLVertexArrayGL.h
@@ -14,17 +14,17 @@ class WebGLVertexArrayGL
     : public WebGLVertexArray
 {
     friend class WebGLVertexArray;
 
 public:
     virtual void DeleteImpl() override;
     virtual void BindVertexArrayImpl() override;
     virtual void GenVertexArray() override;
-    virtual bool IsVertexArrayImpl() override;
+    virtual bool IsVertexArrayImpl() const override;
 
 protected:
     explicit WebGLVertexArrayGL(WebGLContext* webgl);
     ~WebGLVertexArrayGL();
 
     // Bug 1140459: Some drivers (including our test slaves!) don't
     // give reasonable answers for IsVertexArray, maybe others.
     //
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -4,17 +4,17 @@
 # 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/.
 
 TEST_DIRS += [
     'gtest'
 ]
 
 # Change the following line(s) to avoid bug 1081323 (clobber after changing a manifest):
-# * Implement ReadPixel with PBOs.
+# * Adjust failure errata for webgl-conf.
 
 MOCHITEST_MANIFESTS += [
     'test/crash/mochitest.ini',
     'test/crossorigin/mochitest.ini',
     'test/mochitest.ini',
     'test/webgl-conf/generated-mochitest.ini',
     'test/webgl-mochitest/mochitest.ini',
 ]
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html
@@ -121,33 +121,33 @@ function runSupportedTest(extensionEnabl
             testPassed("OES_vertex_array_object not listed as supported and getExtension failed -- this is legal");
         }
     }
 }
 
 function runBindingTestDisabled() {
     debug("");
     debug("Testing binding enum with extension disabled");
-    
+
     // Use the constant directly as we don't have the extension
     var VERTEX_ARRAY_BINDING_OES = 0x85B5;
-    
+
     gl.getParameter(VERTEX_ARRAY_BINDING_OES);
     wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "VERTEX_ARRAY_BINDING_OES should not be queryable if extension is disabled");
 }
 
 function runBindingTestEnabled() {
     debug("");
     debug("Testing binding enum with extension enabled");
-    
+
     shouldBe("ext.VERTEX_ARRAY_BINDING_OES", "0x85B5");
-    
+
     gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES);
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "VERTEX_ARRAY_BINDING_OES query should succeed if extension is enabled");
-    
+
     // Default value is null
     if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) === null) {
         testPassed("Default value of VERTEX_ARRAY_BINDING_OES is null");
     } else {
         testFailed("Default value of VERTEX_ARRAY_BINDING_OES is not null");
     }
 
     debug("");
@@ -174,88 +174,88 @@ function runBindingTestEnabled() {
     ext.bindVertexArrayOES(null);
     shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
     ext.deleteVertexArrayOES(vao1);
 }
 
 function runObjectTest() {
     debug("");
     debug("Testing object creation");
-    
+
     vao = ext.createVertexArrayOES();
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createVertexArrayOES should not set an error");
     shouldBeNonNull("vao");
-    
+
     // Expect false if never bound
     shouldBeFalse("ext.isVertexArrayOES(vao)");
     ext.bindVertexArrayOES(vao);
     shouldBeTrue("ext.isVertexArrayOES(vao)");
     ext.bindVertexArrayOES(null);
     shouldBeTrue("ext.isVertexArrayOES(vao)");
-    
+
     shouldBeFalse("ext.isVertexArrayOES(null)");
-    
+
     ext.deleteVertexArrayOES(vao);
     vao = null;
 }
 
 function runAttributeTests() {
     debug("");
     debug("Testing attributes work across bindings");
-    
+
     var states = [];
-    
+
     var attrCount = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
     for (var n = 0; n < attrCount; n++) {
         gl.bindBuffer(gl.ARRAY_BUFFER, null);
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-        
+
         var state = {};
         states.push(state);
-        
+
         var vao = state.vao = ext.createVertexArrayOES();
         ext.bindVertexArrayOES(vao);
-        
+
         var enableArray = (n % 2 == 0);
         if (enableArray) {
             gl.enableVertexAttribArray(n);
         } else {
             gl.disableVertexAttribArray(n);
         }
-        
+
         if (enableArray) {
             var buffer = state.buffer = gl.createBuffer();
             gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
             gl.bufferData(gl.ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
-            
+
             gl.vertexAttribPointer(n, 1 + n % 4, gl.FLOAT, true, n * 4, n * 4);
         }
-        
+
         if (enableArray) {
             var elbuffer = state.elbuffer = gl.createBuffer();
             gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elbuffer);
             gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
         }
-        
+
         ext.bindVertexArrayOES(null);
     }
-    
+
     var anyMismatch = false;
     for (var n = 0; n < attrCount; n++) {
         var state = states[n];
-        
+
         ext.bindVertexArrayOES(state.vao);
-        
+
         var shouldBeEnabled = (n % 2 == 0);
         var isEnabled = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_ENABLED);
         if (shouldBeEnabled != isEnabled) {
             testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved");
             anyMismatch = true;
         }
-        
+
         var buffer = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
         if (shouldBeEnabled) {
             if (buffer == state.buffer) {
                 // Matched
                 if ((gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_SIZE) == 1 + n % 4) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_TYPE) == gl.FLOAT) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) == true) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_STRIDE) == n * 4) &&
@@ -271,17 +271,17 @@ function runAttributeTests() {
             }
         } else {
             // GL_CURRENT_VERTEX_ATTRIB is not preserved
             if (buffer) {
                 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
                 anyMismatch = true;
             }
         }
-        
+
         var elbuffer = gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING);
         if (shouldBeEnabled) {
             if (elbuffer == state.elbuffer) {
                 // Matched
             } else {
                 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
                 anyMismatch = true;
             }
@@ -293,81 +293,81 @@ function runAttributeTests() {
                 anyMismatch = true;
             }
         }
     }
     ext.bindVertexArrayOES(null);
     if (!anyMismatch) {
         testPassed("All attributes preserved across bindings");
     }
-    
+
     for (var n = 0; n < attrCount; n++) {
         var state = states[n];
         ext.deleteVertexArrayOES(state.vao);
     }
 }
 
 function runAttributeValueTests() {
     debug("");
     debug("Testing that attribute values are not attached to bindings");
-    
+
     var v;
     var vao0 = ext.createVertexArrayOES();
     var anyFailed = false;
-    
+
     ext.bindVertexArrayOES(null);
     gl.vertexAttrib4f(0, 0, 1, 2, 3);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
         testFailed("Vertex attrib value not round-tripped?");
         anyFailed = true;
     }
-    
+
     ext.bindVertexArrayOES(vao0);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
         testFailed("Vertex attrib value reset across bindings");
         anyFailed = true;
     }
-    
+
     gl.vertexAttrib4f(0, 4, 5, 6, 7);
     ext.bindVertexArrayOES(null);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 4 && v[1] == 5 && v[2] == 6 && v[3] == 7)) {
         testFailed("Vertex attrib value bound to buffer");
         anyFailed = true;
     }
-    
+
     if (!anyFailed) {
         testPassed("Vertex attribute values are not attached to bindings")
     }
-    
+
     ext.bindVertexArrayOES(null);
     ext.deleteVertexArrayOES(vao0);
 }
 
 function runDrawTests() {
     debug("");
     debug("Testing draws with various VAO bindings");
-    
+
     canvas.width = 50; canvas.height = 50;
     gl.viewport(0, 0, canvas.width, canvas.height);
-    
+
     var vao0 = ext.createVertexArrayOES();
     var vao1 = ext.createVertexArrayOES();
     var vao2 = ext.createVertexArrayOES();
 
     var positionLocation = 0;
     var colorLocation = 1;
-    
+
     var program = wtu.setupSimpleVertexColorProgram(gl, positionLocation, colorLocation);
-    
+
     function setupQuad(s, colorsInArray) {
         var vertexObject = gl.createBuffer();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
              1.0 * s,  1.0 * s, 0.0,
             -1.0 * s,  1.0 * s, 0.0,
             -1.0 * s, -1.0 * s, 0.0,
              1.0 * s,  1.0 * s, 0.0,
@@ -388,17 +388,17 @@ function runDrawTests() {
                 0.0, 0.0, 0.0, 1.0,
                 0.0, 0.0, 0.0, 1.0]), gl.STATIC_DRAW);
             gl.enableVertexAttribArray(colorLocation);
             gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
         } else {
             gl.disableVertexAttribArray(colorLocation);
         }
     };
-    
+
     function verifyDiagonalPixels(s, expectedInside, drawDescription) {
         // Tests pixels along a diagonal running from the center of the canvas to the (0, 0) corner.
         // Values on the points list indicate relative position along this diagonal.
         var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
         for (var n = 0; n < points.length; n++) {
             var expected = points[n] <= s ? expectedInside : 255;
             var x = Math.round((1 - points[n]) * canvas.width / 2);
             var y = Math.round((1 - points[n]) * canvas.height / 2);
@@ -406,28 +406,28 @@ function runDrawTests() {
                 "Drawing " + drawDescription + " should pass", 2);
         }
     };
     function verifyDraw(drawDescription, s, colorsInArray) {
         wtu.clearAndDrawUnitQuad(gl);
         var expectedInside = colorsInArray ? 0 : 128;
         verifyDiagonalPixels(s, expectedInside, drawDescription);
     };
-    
+
     // Setup all bindings
     setupQuad(1, true);
     ext.bindVertexArrayOES(vao0);
     setupQuad(0.5, true);
     ext.bindVertexArrayOES(vao1);
     setupQuad(0.25, true);
     ext.bindVertexArrayOES(vao2);
     setupQuad(0.75, false);
 
     gl.vertexAttrib4f(colorLocation, 0.5, 0.5, 0.5, 1);
-    
+
     // Verify drawing
     ext.bindVertexArrayOES(null);
     verifyDraw("with the default VAO", 1, true);
     ext.bindVertexArrayOES(vao0);
     verifyDraw("with VAO #0", 0.5, true);
     ext.bindVertexArrayOES(vao1);
     verifyDraw("with VAO #1", 0.25, true);
     ext.bindVertexArrayOES(vao2);
@@ -619,35 +619,41 @@ function runBoundDeleteTests() {
 
     // delete the color buffers AND the position buffer, that are bound to the current VAO
     for (var ii = 0; ii < vaos.length; ++ii) {
         ext.bindVertexArrayOES(vaos[ii]);
 
         gl.deleteBuffer(colorBuffer);
         gl.deleteBuffer(positionBuffer);
 
-        // The buffers should not be accessible at this point. Deleted objects that are bound
-        // in the current context undergo an automatic unbinding
+        var expectRetained = (ii != 0);
+        var shouldBeStr = (expectRetained ? "retained" : "cleared");
+
         var boundPositionBuffer = gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
-        if(boundPositionBuffer == positionBuffer) {
-            testFailed("Position buffer should be automatically unbound when deleted");
+        if (expectRetained != (boundPositionBuffer == positionBuffer)) {
+            testFailed("Position attrib stored buffer should be " + shouldBeStr + ".");
         }
+
         var boundColorBuffer = gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
-        if(boundColorBuffer == colorBuffer) {
-            testFailed("Color buffer should be automatically unbound when deleted");
+        if (expectRetained != (boundColorBuffer == colorBuffer)) {
+            testFailed("Color attrib stored buffer should be " + shouldBeStr + ".");
         }
 
+        // If retained, everything should still work. If cleared, drawing should now fail.
         gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
-        wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Draw call should fail with unbound position and color buffers");
+        var expectedError = (expectRetained ? gl.NO_ERROR : gl.INVALID_OPERATION);
+        wtu.glErrorShouldBe(gl, expectedError,
+                            "Draw call should " + (expectRetained ? "not " : "") + "fail.");
 
-        var isPositionBuffer = gl.isBuffer(positionBuffer);
-        var isColorBuffer    = gl.isBuffer(colorBuffer);
-
-        if(isPositionBuffer)  testFailed("Position buffer should no longer exist after last ref removed");
-        if(isColorBuffer)     testFailed("Color buffer should no longer exist after last ref removed");
+        if (!gl.isBuffer(positionBuffer)) {
+            testFailed("Position buffer should count for isBuffer.");
+        }
+        if (!gl.isBuffer(colorBuffer)) {
+            testFailed("Color buffer should count for isBuffer.");
+        }
     }
 }
 
 function runArrayBufferBindTests() {
     debug("");
     debug("Testing that buffer bindings on VAOs don't affect default VAO ARRAY_BUFFER binding.");
 
     ext.bindVertexArrayOES(null);
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -6023,17 +6023,16 @@ fail-if = (os == 'android')
 [generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
 skip-if = (os == 'win' && os_version == '6.1')
 fail-if = (os == 'android')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__extensions__oes-vertex-array-object-bufferData.html]
 [generated/test_conformance__extensions__oes-vertex-array-object.html]
 skip-if = (os == 'mac' && os_version == '10.6')
-fail-if = (os == 'win') || (os == 'mac') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__extensions__webgl-compressed-texture-atc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-pvrtc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-size-limit.html]
 skip-if = (os == 'win')
 [generated/test_conformance__extensions__webgl-debug-renderer-info.html]
 [generated/test_conformance__extensions__webgl-debug-shaders.html]
 [generated/test_conformance__extensions__webgl-depth-texture.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -124,17 +124,16 @@ skip-if = (os == 'android') || (os == 'b
 fail-if = (os == 'android')
 # void mozilla::gl::GLContext::fDetachShader(GLuint, GLuint): Generated unexpected GL_INVALID_VALUE error. (0x0501)
 skip-if = (os == 'android' && debug)
 
 [generated/test_conformance__extensions__oes-vertex-array-object.html]
 # 10.6 crash:
 # PROCESS-CRASH | dom/canvas/test/webgl-conf/generated/test_conformance__extensions__oes-vertex-array-object.html | application crashed [@ gleRunVertexSubmitImmediate + 0xf24]
 skip-if = (os == 'mac' && os_version == '10.6')
-fail-if = (os == 'win') || (os == 'mac') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__textures__misc__texture-size.html]
 # application crashed [@ mozilla::gl::GLContext::AfterGLCall]
 skip-if = (os == 'android') || (os == 'win')
 
 [generated/test_2_conformance__textures__misc__cube-incomplete-fbo.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'win')
 [generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html]