Bug 1002281 - WebGL2 - Refactor common functions into WebGLBindableName.; r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Wed, 07 May 2014 13:11:18 +1000
changeset 198176 baa68b41e879b9ed1e6d056732e5a33448e00667
parent 198175 969587e6bfcfa3774dab41c021933d0f31b12d86
child 198177 01d12ccde2bb16d89b553e57adb5231a47080d33
push id27264
push usernigelbabu@gmail.com
push dateThu, 07 Aug 2014 03:31:37 +0000
treeherdermozilla-central@afcb3af79d09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1002281
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1002281 - WebGL2 - Refactor common functions into WebGLBindableName.; r=jgilbert WebGLBindableName represents a GL 'name' (GLuint) that can be bound to part of the GL state machine. Similar code appeared in a number of classes that represent GL bindable names, such as WebGLBuffer, WebGLTexture, WebGLFramebuffer, etc. Cleanup to reduce copy-n-paste code that's needed for creating new objects for WebGL 2.
dom/canvas/WebGLBindableName.cpp
dom/canvas/WebGLBindableName.h
dom/canvas/WebGLBuffer.cpp
dom/canvas/WebGLBuffer.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextVertexArray.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLRenderbuffer.cpp
dom/canvas/WebGLRenderbuffer.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLVertexArray.cpp
dom/canvas/WebGLVertexArray.h
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLBindableName.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLBindableName.h"
+#include "GLConsts.h"
+#include "mozilla/Assertions.h"
+
+using namespace mozilla;
+
+WebGLBindableName::WebGLBindableName()
+    : mGLName(LOCAL_GL_NONE)
+    , mTarget(LOCAL_GL_NONE)
+{ }
+
+void
+WebGLBindableName::BindTo(GLenum target)
+{
+    MOZ_ASSERT(target != LOCAL_GL_NONE, "Can't bind to GL_NONE.");
+    MOZ_ASSERT(mTarget == LOCAL_GL_NONE || mTarget == target, "Rebinding is illegal.");
+
+    bool targetChanged = (target != mTarget);
+    mTarget = target;
+    if (targetChanged)
+        OnTargetChanged();
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLBindableName.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGLBINDABLENAME_H_
+#define WEBGLBINDABLENAME_H_
+
+#include "WebGLTypes.h"
+
+namespace mozilla {
+
+/** Represents a GL name that can be bound to a target.
+ */
+class WebGLBindableName
+{
+public:
+    WebGLBindableName();
+    void BindTo(GLenum target);
+
+    bool HasEverBeenBound() const { return mTarget != 0; }
+    GLuint GLName() const { return mGLName; }
+    GLenum Target() const { return mTarget; }
+
+protected:
+
+    //! Called after mTarget has been changed by BindTo(target).
+    virtual void OnTargetChanged() {}
+
+    GLuint mGLName;
+    GLenum mTarget;
+};
+
+} // namespace mozilla
+
+#endif // !WEBGLBINDABLENAME_H_
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -8,20 +8,19 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLElementArrayCache.h"
 
 using namespace mozilla;
 
 WebGLBuffer::WebGLBuffer(WebGLContext *context)
-    : WebGLContextBoundObject(context)
-    , mHasEverBeenBound(false)
+    : WebGLBindableName()
+    , WebGLContextBoundObject(context)
     , mByteLength(0)
-    , mTarget(LOCAL_GL_NONE)
 {
     SetIsDOMBinding();
     mContext->MakeContextCurrent();
     mContext->gl->fGenBuffers(1, &mGLName);
     mContext->mBuffers.insertBack(this);
 }
 
 WebGLBuffer::~WebGLBuffer() {
@@ -33,18 +32,17 @@ WebGLBuffer::Delete() {
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteBuffers(1, &mGLName);
     mByteLength = 0;
     mCache = nullptr;
     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
 }
 
 void
-WebGLBuffer::SetTarget(GLenum target) {
-    mTarget = target;
+WebGLBuffer::OnTargetChanged() {
     if (!mCache && mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
         mCache = new WebGLElementArrayCache;
 }
 
 bool
 WebGLBuffer::ElementArrayCacheBufferData(const void* ptr, size_t buffer_size_in_bytes) {
     if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
         return mCache->BufferData(ptr, buffer_size_in_bytes);
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -5,46 +5,42 @@
 
 #ifndef WEBGLBUFFER_H_
 #define WEBGLBUFFER_H_
 
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsWrapperCache.h"
+#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLElementArrayCache;
 
 class WebGLBuffer MOZ_FINAL
     : public nsWrapperCache
+    , public WebGLBindableName
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
     , public WebGLContextBoundObject
 {
 public:
     WebGLBuffer(WebGLContext *context);
 
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    GLuint GLName() const { return mGLName; }
     WebGLsizeiptr ByteLength() const { return mByteLength; }
-    GLenum Target() const { return mTarget; }
 
     void SetByteLength(WebGLsizeiptr byteLength) { mByteLength = byteLength; }
 
-    void SetTarget(GLenum target);
-
     bool ElementArrayCacheBufferData(const void* ptr, size_t buffer_size_in_bytes);
 
     void ElementArrayCacheBufferSubData(size_t pos, const void* ptr, size_t update_size_in_bytes);
 
     bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count,
                   uint32_t* out_upperBound);
 
     bool IsElementArrayUsedWithMultipleTypes() const;
@@ -56,17 +52,16 @@ public:
     virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
-    GLuint mGLName;
-    bool mHasEverBeenBound;
+    virtual void OnTargetChanged() MOZ_OVERRIDE;
+
     WebGLsizeiptr mByteLength;
-    GLenum mTarget;
 
     nsAutoPtr<WebGLElementArrayCache> mCache;
 };
 }
 #endif //WEBGLBUFFER_H_
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -26,19 +26,18 @@ WebGLContext::BindBuffer(GLenum target, 
 
     WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
 
     if (!bufferSlot) {
         return;
     }
 
     if (buffer) {
-        if (!buffer->Target()) {
-            buffer->SetTarget(target);
-            buffer->SetHasEverBeenBound(true);
+        if (!buffer->HasEverBeenBound()) {
+            buffer->BindTo(target);
         } else if (target != buffer->Target()) {
             return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
         }
     }
 
     *bufferSlot = buffer;
 
     MakeContextCurrent();
@@ -62,22 +61,21 @@ WebGLContext::BindBufferBase(GLenum targ
 
     WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase");
 
     if (!indexedBufferSlot) {
         return;
     }
 
     if (buffer) {
-        if (!buffer->Target()) {
-            buffer->SetTarget(target);
-            buffer->SetHasEverBeenBound(true);
-        } else if (target != buffer->Target()) {
+        if (!buffer->HasEverBeenBound())
+            buffer->BindTo(target);
+
+        if (target != buffer->Target())
             return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
-        }
     }
 
     WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bindBuffer");
 
     MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch");
 
     *indexedBufferSlot = buffer;
     *bufferSlot = buffer;
@@ -103,22 +101,22 @@ WebGLContext::BindBufferRange(GLenum tar
 
     WebGLRefPtr<WebGLBuffer>* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index, "bindBufferBase");
 
     if (!indexedBufferSlot) {
         return;
     }
 
     if (buffer) {
-        if (!buffer->Target()) {
-            buffer->SetTarget(target);
-            buffer->SetHasEverBeenBound(true);
-        } else if (target != buffer->Target()) {
+        if (!buffer->HasEverBeenBound())
+            buffer->BindTo(target);
+
+        if (target != buffer->Target())
             return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
-        }
+
         CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + size;
         if (!checked_neededByteLength.isValid() ||
             checked_neededByteLength.value() > buffer->ByteLength())
         {
             return ErrorInvalidValue("bindBufferRange: invalid range");
         }
     }
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -169,19 +169,19 @@ WebGLContext::BindFramebuffer(GLenum tar
     if (wfb && wfb->IsDeleted())
         return;
 
     MakeContextCurrent();
 
     if (!wfb) {
         gl->fBindFramebuffer(target, 0);
     } else {
+        wfb->BindTo(target);
         GLuint framebuffername = wfb->GLName();
         gl->fBindFramebuffer(target, framebuffername);
-        wfb->SetHasEverBeenBound(true);
     }
 
     mBoundFramebuffer = wfb;
 }
 
 void
 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer *wrb)
 {
@@ -194,17 +194,17 @@ WebGLContext::BindRenderbuffer(GLenum ta
     if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
         return;
 
     // silently ignore a deleted buffer
     if (wrb && wrb->IsDeleted())
         return;
 
     if (wrb)
-        wrb->SetHasEverBeenBound(true);
+        wrb->BindTo(target);
 
     MakeContextCurrent();
 
     // Sometimes we emulate renderbuffers (depth-stencil emu), so there's not
     // always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it.
     if (wrb) {
         wrb->BindRenderbuffer();
     } else {
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -84,10 +84,8 @@ WebGLContext::IsVertexArray(WebGLVertexA
 
     if (!array)
         return false;
 
     return ValidateObjectAllowDeleted("isVertexArray", array) &&
            !array->IsDeleted() &&
            array->HasEverBeenBound();
 }
-
-
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -19,19 +19,19 @@ using namespace mozilla::gl;
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext* cx)
 {
     return dom::WebGLFramebufferBinding::Wrap(cx, this);
 }
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* context)
-    : WebGLContextBoundObject(context)
+    : WebGLBindableName()
+    , WebGLContextBoundObject(context)
     , mStatus(0)
-    , mHasEverBeenBound(false)
     , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
 {
     SetIsDOMBinding();
     mContext->MakeContextCurrent();
     mContext->gl->fGenFramebuffers(1, &mGLName);
     mContext->mFramebuffers.insertBack(this);
@@ -74,38 +74,42 @@ WebGLFramebuffer::Attachment::HasAlpha()
     else if (Renderbuffer())
         format = Renderbuffer()->InternalFormat();
     return FormatHasAlpha(format);
 }
 
 bool
 WebGLFramebuffer::Attachment::IsReadableFloat() const
 {
-    if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
-        GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
+    const WebGLTexture* tex = Texture();
+    if (tex && tex->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
+        GLenum type = tex->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
         switch (type) {
         case LOCAL_GL_FLOAT:
         case LOCAL_GL_HALF_FLOAT_OES:
             return true;
         }
         return false;
     }
 
-    if (Renderbuffer()) {
-        GLenum format = Renderbuffer()->InternalFormat();
+    const WebGLRenderbuffer* rb = Renderbuffer();
+    if (rb) {
+        GLenum format = rb->InternalFormat();
         switch (format) {
         case LOCAL_GL_RGB16F:
         case LOCAL_GL_RGBA16F:
         case LOCAL_GL_RGB32F:
         case LOCAL_GL_RGBA32F:
             return true;
         }
         return false;
     }
 
+    // If we arrive here Attachment isn't correct setup because it has
+    // no texture nor render buffer pointer.
     MOZ_ASSERT(false, "Should not get here.");
     return false;
 }
 
 void
 WebGLFramebuffer::Attachment::SetTexImage(WebGLTexture* tex, GLenum target, GLint level)
 {
     mTexturePtr = tex;
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGLFRAMEBUFFER_H_
 #define WEBGLFRAMEBUFFER_H_
 
+#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/LinkedList.h"
 
 namespace mozilla {
 
@@ -18,16 +19,17 @@ class WebGLFramebufferAttachable;
 class WebGLTexture;
 class WebGLRenderbuffer;
 namespace gl {
     class GLContext;
 }
 
 class WebGLFramebuffer MOZ_FINAL
     : public nsWrapperCache
+    , public WebGLBindableName
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
     , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
 {
 public:
     MOZ_DECLARE_REFCOUNTED_TYPENAME(WebGLFramebuffer)
 
@@ -87,20 +89,16 @@ public:
         bool HasImage() const;
         bool IsComplete() const;
 
         void FinalizeAttachment(gl::GLContext* gl, GLenum attachmentLoc) const;
     };
 
     void Delete();
 
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    GLuint GLName() { return mGLName; }
-
     void FramebufferRenderbuffer(GLenum target,
                                  GLenum attachment,
                                  GLenum rbtarget,
                                  WebGLRenderbuffer* wrb);
 
     void FramebufferTexture2D(GLenum target,
                               GLenum attachment,
                               GLenum textarget,
@@ -178,19 +176,16 @@ public:
 
 private:
     ~WebGLFramebuffer() {
         DeleteOnce();
     }
 
     mutable GLenum mStatus;
 
-    GLuint mGLName;
-    bool mHasEverBeenBound;
-
     // we only store pointers to attached renderbuffers, not to attached textures, because
     // we will only need to initialize renderbuffers. Textures are already initialized.
     nsTArray<Attachment> mColorAttachments;
     Attachment mDepthAttachment,
                mStencilAttachment,
                mDepthStencilAttachment;
 };
 
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -38,22 +38,22 @@ NeedsDepthStencilEmu(GLContext* gl, GLen
 }
 
 JSObject*
 WebGLRenderbuffer::WrapObject(JSContext *cx) {
     return dom::WebGLRenderbufferBinding::Wrap(cx, this);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext *context)
-    : WebGLContextBoundObject(context)
+    : WebGLBindableName()
+    , WebGLContextBoundObject(context)
     , mPrimaryRB(0)
     , mSecondaryRB(0)
     , mInternalFormat(0)
     , mInternalFormatForGL(0)
-    , mHasEverBeenBound(false)
     , mImageDataStatus(WebGLImageDataStatus::NoImageData)
 {
     SetIsDOMBinding();
     mContext->MakeContextCurrent();
 
     mContext->gl->fGenRenderbuffers(1, &mPrimaryRB);
     if (!SupportsDepthStencil(mContext->gl))
         mContext->gl->fGenRenderbuffers(1, &mSecondaryRB);
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -1,41 +1,40 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGLRENDERBUFFER_H_
 #define WEBGLRENDERBUFFER_H_
 
+#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 #include "WebGLFramebufferAttachable.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/LinkedList.h"
 
 namespace mozilla {
 
 class WebGLRenderbuffer MOZ_FINAL
     : public nsWrapperCache
+    , public WebGLBindableName
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
 public:
     WebGLRenderbuffer(WebGLContext *context);
 
     void Delete();
 
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-
     bool HasUninitializedImageData() const { return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData; }
     void SetImageDataStatus(WebGLImageDataStatus x) {
         // there is no way to go from having image data to not having any
         MOZ_ASSERT(x != WebGLImageDataStatus::NoImageData ||
                    mImageDataStatus == WebGLImageDataStatus::NoImageData);
         mImageDataStatus = x;
     }
 
@@ -66,16 +65,15 @@ protected:
     ~WebGLRenderbuffer() {
         DeleteOnce();
     }
 
     GLuint mPrimaryRB;
     GLuint mSecondaryRB;
     GLenum mInternalFormat;
     GLenum mInternalFormatForGL;
-    bool mHasEverBeenBound;
     WebGLImageDataStatus mImageDataStatus;
 
     friend class WebGLFramebuffer;
 };
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -18,19 +18,18 @@
 using namespace mozilla;
 
 JSObject*
 WebGLTexture::WrapObject(JSContext *cx) {
     return dom::WebGLTextureBinding::Wrap(cx, this);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext *context)
-    : WebGLContextBoundObject(context)
-    , mHasEverBeenBound(false)
-    , mTarget(0)
+    : WebGLBindableName()
+    , WebGLContextBoundObject(context)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mFacesCount(0)
     , mMaxLevelWithCustomImages(0)
     , mHaveGeneratedMipmap(false)
     , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
@@ -103,51 +102,58 @@ WebGLTexture::DoesTexture2DMipmapHaveAll
     return false;
 }
 
 void
 WebGLTexture::Bind(GLenum aTarget) {
     // this function should only be called by bindTexture().
     // it assumes that the GL context is already current.
 
-    bool firstTimeThisTextureIsBound = !mHasEverBeenBound;
+    bool firstTimeThisTextureIsBound = !HasEverBeenBound();
 
-    if (!firstTimeThisTextureIsBound && aTarget != mTarget) {
+    if (firstTimeThisTextureIsBound) {
+        BindTo(aTarget);
+    } else if (aTarget != Target()) {
         mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
         // very important to return here before modifying texture state! This was the place when I lost a whole day figuring
         // very strange 'invalid write' crashes.
         return;
     }
 
-    mTarget = aTarget;
+    GLuint name = GLName();
+    GLenum target = Target();
 
-    mContext->gl->fBindTexture(mTarget, mGLName);
+    mContext->gl->fBindTexture(target, name);
 
     if (firstTimeThisTextureIsBound) {
         mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
         EnsureMaxLevelWithCustomImagesAtLeast(0);
         SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
 
         // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not
         // present in GLES 2, but is present in GL and it seems as if for cube maps
         // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior.
         if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES())
             mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
     }
-
-    mHasEverBeenBound = true;
 }
 
 void
 WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel,
                   GLsizei aWidth, GLsizei aHeight,
                   GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus)
 {
-    if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) )
+    // TODO(djg): I suspected the following ASSERT and check are
+    //            trying to express more than they're saying, probably
+    //            to do with cubemap targets. We should do this
+    //            properly. https://bugzilla.mozilla.org/show_bug.cgi?id=1006908
+    MOZ_ASSERT((aTarget == LOCAL_GL_TEXTURE_2D) == (mTarget == LOCAL_GL_TEXTURE_2D));
+    if ((aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D)) {
         return;
+    }
 
     EnsureMaxLevelWithCustomImagesAtLeast(aLevel);
 
     ImageInfoAt(aTarget, aLevel) = ImageInfo(aWidth, aHeight, aFormat, aType, aStatus);
 
     if (aLevel > 0)
         SetCustomMipmap();
 
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -1,18 +1,19 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGLTEXTURE_H_
 #define WEBGLTEXTURE_H_
 
+#include "WebGLBindableName.h"
+#include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
-#include "WebGLFramebufferAttachable.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
 #include <algorithm>
 
 namespace mozilla {
@@ -22,31 +23,27 @@ inline bool is_pot_assuming_nonnegative(
 {
     return x && (x & (x-1)) == 0;
 }
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture MOZ_FINAL
     : public nsWrapperCache
+    , public WebGLBindableName
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
 public:
     WebGLTexture(WebGLContext *context);
 
     void Delete();
 
-    bool HasEverBeenBound() const { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    GLuint GLName() const { return mGLName; }
-    GLenum Target() const { return mTarget; }
-
     WebGLContext *GetParentObject() const {
         return Context();
     }
 
     virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
@@ -54,19 +51,16 @@ public:
 protected:
     ~WebGLTexture() {
         DeleteOnce();
     }
 
     friend class WebGLContext;
     friend class WebGLFramebuffer;
 
-    bool mHasEverBeenBound;
-    GLuint mGLName;
-
     // we store information about the various images that are part of
     // this texture (cubemap faces, mipmap levels)
 
 public:
 
     class ImageInfo
         : public WebGLRectangleObject
     {
@@ -200,17 +194,16 @@ public:
         }
         imageInfo.mImageDataStatus = newStatus;
     }
 
     void DoDeferredImageInitialization(GLenum imageTarget, GLint level);
 
 protected:
 
-    GLenum mTarget;
     GLenum mMinFilter, mMagFilter, mWrapS, mWrapT;
 
     size_t mFacesCount, mMaxLevelWithCustomImages;
     nsTArray<ImageInfo> mImageInfos;
 
     bool mHaveGeneratedMipmap;
     WebGLTextureFakeBlackStatus mFakeBlackStatus;
 
@@ -273,17 +266,17 @@ public:
 
     bool IsMipmapTexture2DComplete() const;
 
     bool IsCubeComplete() const;
 
     bool IsMipmapCubeComplete() const;
 
     void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
-    
+
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -15,19 +15,18 @@
 using namespace mozilla;
 
 JSObject*
 WebGLVertexArray::WrapObject(JSContext *cx) {
     return dom::WebGLVertexArrayBinding::Wrap(cx, this);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* context)
-    : WebGLContextBoundObject(context)
-    , mGLName(0)
-    , mHasEverBeenBound(false)
+    : WebGLBindableName()
+    , WebGLContextBoundObject(context)
 {
     SetIsDOMBinding();
     context->mVertexArrays.insertBack(this);
 }
 
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* context)
 {
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -1,41 +1,45 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGLVERTEXARRAY_H_
 #define WEBGLVERTEXARRAY_H_
 
+#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/LinkedList.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake;
 
 class WebGLVertexArray
     : public nsWrapperCache
+    , public WebGLBindableName
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
     , public WebGLContextBoundObject
 {
 // -----------------------------------------------------------------------------
 // PUBLIC
 public:
     static WebGLVertexArray* Create(WebGLContext* context);
 
     void BindVertexArray() {
-        SetHasEverBeenBound(true);
+        /* Bind to dummy value to signal that this vertex array has
+           ever been bound */
+        BindTo(LOCAL_GL_VERTEX_ARRAY_BINDING);
         BindVertexArrayImpl();
     };
 
     virtual void GenVertexArray() = 0;
     virtual void BindVertexArrayImpl() = 0;
 
     GLuint GLName() const { return mGLName; }
 
@@ -53,19 +57,16 @@ public:
     virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray)
 
     // -------------------------------------------------------------------------
     // MEMBER FUNCTIONS
 
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-
     bool EnsureAttrib(GLuint index, const char *info);
     bool HasAttrib(GLuint index) {
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) {
         return HasAttrib(index) && mAttribs[index].enabled;
     }
 
@@ -77,18 +78,16 @@ protected:
 
     virtual ~WebGLVertexArray() {
         MOZ_ASSERT(IsDeleted());
     };
 
     // -------------------------------------------------------------------------
     // MEMBERS
 
-    GLuint mGLName;
-    bool mHasEverBeenBound;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
     // -------------------------------------------------------------------------
     // FRIENDSHIPS
 
     friend class WebGLVertexArrayFake;
     friend class WebGLContext;
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -36,16 +36,17 @@ UNIFIED_SOURCES += [
 ]
 
 if CONFIG['MOZ_WEBGL']:
     UNIFIED_SOURCES += [
         'MurmurHash3.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
         'WebGLActiveInfo.cpp',
+        'WebGLBindableName.cpp',
         'WebGLBuffer.cpp',
         'WebGLContext.cpp',
         'WebGLContextAsyncQueries.cpp',
         'WebGLContextBuffers.cpp',
         'WebGLContextDraw.cpp',
         'WebGLContextExtensions.cpp',
         'WebGLContextFramebufferOperations.cpp',
         'WebGLContextGL.cpp',