Bug 1325301 (flattened) - Validate attribute base types match data base types. - r=bz,daoshengmu a=lizzard
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 21 Dec 2016 19:42:07 -0800
changeset 353262 cc389c3a0973b01da870c47734d69ecf81f952a2
parent 353261 4df21f477536abb8f06a3000bd5d48ba50403ea4
child 353263 b213de949cddebe0a123654be580ba114b951020
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, daoshengmu, lizzard
bugs1325301
milestone52.0a2
Bug 1325301 (flattened) - Validate attribute base types match data base types. - r=bz,daoshengmu a=lizzard Flattened with: * Clean up generic vertex attribs. - r=bz,daoshengmu * Remove EnsureAttrib() and friends. - r=daoshengmu * Simplify EnumName behavior and usage and add ErrorInvalidEnumArg(). - r=daoshengmu
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGL2ContextSamplers.cpp
dom/canvas/WebGL2ContextUniforms.cpp
dom/canvas/WebGL2ContextVertices.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLContextVertices.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLQuery.cpp
dom/canvas/WebGLSampler.cpp
dom/canvas/WebGLShader.h
dom/canvas/WebGLVertexArray.cpp
dom/canvas/WebGLVertexArray.h
dom/canvas/WebGLVertexAttribData.cpp
dom/canvas/WebGLVertexAttribData.h
dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertex-attrib-i-render.html
dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertexattribipointer-offsets.html
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
dom/webidl/WebGLRenderingContext.webidl
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -238,18 +238,20 @@ public:
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
     void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
     ////////////////
 
     // GL 3.0 & ES 3.0
-    void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
-    void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
+    void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w,
+                         const char* funcName = nullptr);
+    void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w,
+                          const char* funcName = nullptr);
 
     void VertexAttribI4iv(GLuint index, const Int32ListU& list) {
         const auto& arr = Int32Arr::From(list);
         if (!ValidateAttribArraySetter("vertexAttribI4iv", 4, arr.elemCount))
             return;
 
         const auto& itr = arr.elemBytes;
         VertexAttribI4i(index, itr[0], itr[1], itr[2], itr[3]);
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -320,18 +320,20 @@ WebGL2Context::ReadBuffer(GLenum mode)
         mBoundReadFramebuffer->ReadBuffer(funcName, mode);
         return;
     }
 
     // Operating on the default framebuffer.
     if (mode != LOCAL_GL_NONE &&
         mode != LOCAL_GL_BACK)
     {
+        nsCString enumName;
+        EnumName(mode, &enumName);
         ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or"
-                              " NONE. Was %s",
-                              funcName, EnumName(mode));
+                              " NONE. Was %s.",
+                              funcName, enumName.BeginReading());
         return;
     }
 
     gl->Screen()->SetReadBuffer(mode);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -134,14 +134,14 @@ WebGL2Context::GetSamplerParameter(JSCon
         {
             GLfloat param = 0;
             gl->fGetSamplerParameterfv(sampler.mGLName, pname, &param);
             retval.set(JS::Float32Value(param));
         }
         return;
 
     default:
-        ErrorInvalidEnum("%s: invalid pname: %s", funcName, EnumName(pname));
+        ErrorInvalidEnumArg(funcName, "pname", pname);
         return;
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -156,17 +156,17 @@ ValidateUniformEnum(WebGLContext* webgl,
     case LOCAL_GL_UNIFORM_BLOCK_INDEX:
     case LOCAL_GL_UNIFORM_OFFSET:
     case LOCAL_GL_UNIFORM_ARRAY_STRIDE:
     case LOCAL_GL_UNIFORM_MATRIX_STRIDE:
     case LOCAL_GL_UNIFORM_IS_ROW_MAJOR:
         return true;
 
     default:
-        webgl->ErrorInvalidEnum("%s: invalid pname: %s", info, webgl->EnumName(pname));
+        webgl->ErrorInvalidEnumArg(info, "pname", pname);
         return false;
     }
 }
 
 void
 WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
                                  const dom::Sequence<GLuint>& uniformIndices,
                                  GLenum pname, JS::MutableHandleValue retval)
--- a/dom/canvas/WebGL2ContextVertices.cpp
+++ b/dom/canvas/WebGL2ContextVertices.cpp
@@ -71,75 +71,22 @@ WebGL2Context::VertexAttribIPointer(GLui
 
   if (!ValidateAttribPointer(true, index, size, type, LOCAL_GL_FALSE, stride, offset,
                              "vertexAttribIPointer"))
   {
     return;
   }
 
   MOZ_ASSERT(mBoundVertexArray);
-  mBoundVertexArray->EnsureAttrib(index);
 
   InvalidateBufferFetching();
 
   MakeContextCurrent();
   gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
 
   WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
   const bool integerFunc = true;
   const bool normalized = false;
   vd.VertexAttribPointer(integerFunc, mBoundArrayBuffer, size, type, normalized, stride,
                          offset);
 }
 
-void
-WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
-{
-  if (IsContextLost())
-    return;
-
-  if (!ValidateAttribIndex(index, "vertexAttribI4i"))
-    return;
-
-  mVertexAttribType[index] = LOCAL_GL_INT;
-
-  MakeContextCurrent();
-
-  if (index) {
-    gl->fVertexAttribI4i(index, x, y, z, w);
-  } else {
-    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
-    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
-    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
-    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
-    if (gl->IsGLES()) {
-      gl->fVertexAttribI4i(index, x, y, z, w);
-    }
-  }
-}
-
-void
-WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
-{
-  if (IsContextLost())
-    return;
-
-  if (!ValidateAttribIndex(index, "vertexAttribI4ui"))
-    return;
-
-  mVertexAttribType[index] = LOCAL_GL_UNSIGNED_INT;
-
-  MakeContextCurrent();
-
-  if (index) {
-    gl->fVertexAttribI4ui(index, x, y, z, w);
-  } else {
-    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
-    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
-    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
-    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
-    if (gl->IsGLES()) {
-      gl->fVertexAttribI4ui(index, x, y, z, w);
-    }
-  }
-}
-
 } // namespace mozilla
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -138,28 +138,16 @@ WebGLContext::WebGLContext()
     mDisableExtensions = false;
     mIsMesa = false;
     mEmitContextLostErrorOnce = false;
     mWebGLError = 0;
     mUnderlyingGLError = 0;
 
     mActiveTexture = 0;
 
-    mVertexAttrib0Vector[0] = 0;
-    mVertexAttrib0Vector[1] = 0;
-    mVertexAttrib0Vector[2] = 0;
-    mVertexAttrib0Vector[3] = 1;
-    mFakeVertexAttrib0BufferObjectVector[0] = 0;
-    mFakeVertexAttrib0BufferObjectVector[1] = 0;
-    mFakeVertexAttrib0BufferObjectVector[2] = 0;
-    mFakeVertexAttrib0BufferObjectVector[3] = 1;
-    mFakeVertexAttrib0BufferObjectSize = 0;
-    mFakeVertexAttrib0BufferObject = 0;
-    mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
-
     mStencilRefFront = 0;
     mStencilRefBack = 0;
     mStencilValueMaskFront = 0;
     mStencilValueMaskBack = 0;
     mStencilWriteMaskFront = 0;
     mStencilWriteMaskBack = 0;
     mDepthWriteMask = 0;
     mStencilClearValue = 0;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -402,27 +402,27 @@ public:
     void ErrorInvalidValue(const char* fmt = 0, ...);
     void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...);
     void ErrorInvalidEnumInfo(const char* info, GLenum enumValue);
     void ErrorInvalidEnumInfo(const char* info, const char* funcName,
                               GLenum enumValue);
     void ErrorOutOfMemory(const char* fmt = 0, ...);
     void ErrorImplementationBug(const char* fmt = 0, ...);
 
+    void ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val);
+
     const char* ErrorName(GLenum error);
 
     /**
      * Return displayable name for GLenum.
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
+     * Returns hex formatted version of glenum if glenum is unknown.
      */
-    // Returns nullptr if glenum is unknown.
-    static const char* EnumName(GLenum glenum);
-    // Returns hex formatted version of glenum if glenum is unknown.
-    static void EnumName(GLenum glenum, nsACString* out_name);
+    static void EnumName(GLenum val, nsCString* out_name);
 
     void DummyReadFramebufferOperation(const char* funcName);
 
     WebGLTexture* ActiveBoundTextureForTarget(const TexTarget texTarget) const {
         switch (texTarget.get()) {
         case LOCAL_GL_TEXTURE_2D:
             return mBound2DTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_CUBE_MAP:
@@ -1280,53 +1280,74 @@ public:
     void GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                          JS::MutableHandle<JS::Value> retval, ErrorResult& rv)
     {
         retval.set(GetVertexAttrib(cx, index, pname, rv));
     }
 
     WebGLsizeiptr GetVertexAttribOffset(GLuint index, GLenum pname);
 
-    void VertexAttrib1f(GLuint index, GLfloat x0);
-    void VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1);
-    void VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2);
-    void VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2,
-                        GLfloat x3);
+    ////
+
+    void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w,
+                        const char* funcName = nullptr);
+
+    ////
 
-    void VertexAttrib1fv(GLuint idx, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        VertexAttrib1fv_base(idx, arr.LengthAllowShared(), arr.DataAllowShared());
+    void VertexAttrib1f(GLuint index, GLfloat x) {
+        VertexAttrib4f(index, x, 0, 0, 1, "vertexAttrib1f");
+    }
+    void VertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {
+        VertexAttrib4f(index, x, y, 0, 1, "vertexAttrib2f");
+    }
+    void VertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
+        VertexAttrib4f(index, x, y, z, 1, "vertexAttrib3f");
     }
-    void VertexAttrib1fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
-        VertexAttrib1fv_base(idx, arr.Length(), arr.Elements());
+
+    ////
+
+    void VertexAttrib1fv(GLuint index, const Float32ListU& list) {
+        const char funcName[] = "vertexAttrib1fv";
+        const auto& arr = Float32Arr::From(list);
+        if (!ValidateAttribArraySetter(funcName, 1, arr.elemCount))
+            return;
+
+        VertexAttrib4f(index, arr.elemBytes[0], 0, 0, 1, funcName);
     }
 
-    void VertexAttrib2fv(GLuint idx, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        VertexAttrib2fv_base(idx, arr.LengthAllowShared(), arr.DataAllowShared());
-    }
-    void VertexAttrib2fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
-        VertexAttrib2fv_base(idx, arr.Length(), arr.Elements());
+    void VertexAttrib2fv(GLuint index, const Float32ListU& list) {
+        const char funcName[] = "vertexAttrib2fv";
+        const auto& arr = Float32Arr::From(list);
+        if (!ValidateAttribArraySetter(funcName, 2, arr.elemCount))
+            return;
+
+        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], 0, 1, funcName);
     }
 
-    void VertexAttrib3fv(GLuint idx, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        VertexAttrib3fv_base(idx, arr.LengthAllowShared(), arr.DataAllowShared());
-    }
-    void VertexAttrib3fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
-        VertexAttrib3fv_base(idx, arr.Length(), arr.Elements());
+    void VertexAttrib3fv(GLuint index, const Float32ListU& list) {
+        const char funcName[] = "vertexAttrib3fv";
+        const auto& arr = Float32Arr::From(list);
+        if (!ValidateAttribArraySetter(funcName, 3, arr.elemCount))
+            return;
+
+        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], arr.elemBytes[2], 1,
+                       funcName);
     }
 
-    void VertexAttrib4fv(GLuint idx, const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        VertexAttrib4fv_base(idx, arr.LengthAllowShared(), arr.DataAllowShared());
+    void VertexAttrib4fv(GLuint index, const Float32ListU& list) {
+        const char funcName[] = "vertexAttrib4fv";
+        const auto& arr = Float32Arr::From(list);
+        if (!ValidateAttribArraySetter(funcName, 4, arr.elemCount))
+            return;
+
+        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], arr.elemBytes[2],
+                       arr.elemBytes[3], funcName);
     }
-    void VertexAttrib4fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
-        VertexAttrib4fv_base(idx, arr.Length(), arr.Elements());
-    }
+
+    ////
 
     void VertexAttribPointer(GLuint index, GLint size, GLenum type,
                              WebGLboolean normalized, GLsizei stride,
                              WebGLintptr byteOffset);
     void VertexAttribDivisor(GLuint index, GLuint divisor);
 
 private:
     // Cache the max number of vertices and instances that can be read from
@@ -1355,18 +1376,18 @@ private:
                               const GLfloat* ptr);
 
     bool ValidateBufferFetching(const char* info);
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
-    WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need();
-    bool DoFakeVertexAttrib0(GLuint vertexCount);
+    WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
+    bool DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount);
     void UndoFakeVertexAttrib0();
 
     inline void InvalidateBufferFetching()
     {
         mBufferFetchingIsVerified = false;
         mBufferFetchingHasPerVertex = false;
         mMaxFetchedVertices = 0;
         mMaxFetchedInstances = 0;
@@ -1403,17 +1424,17 @@ protected:
     GLenum mUnderlyingGLError;
     GLenum GetAndFlushUnderlyingGLErrors();
 
     bool mBypassShaderValidation;
 
     webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
-    int32_t mGLMaxVertexAttribs;
+    uint32_t mGLMaxVertexAttribs;
     int32_t mGLMaxTextureUnits;
     int32_t mGLMaxTextureImageUnits;
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
     uint32_t  mGLMaxTransformFeedbackSeparateAttribs;
     GLuint  mGLMaxUniformBufferBindings;
@@ -1867,26 +1888,27 @@ private:
 public:
     void OnUBIndexedBindingsChanged() const { mBuffersForUB_Dirty = true; }
     const decltype(mBuffersForUB)& BuffersForUB() const;
 
     ////////////////////////////////////
 
 protected:
     // Generic Vertex Attributes
-    UniquePtr<GLenum[]> mVertexAttribType;
-    GLfloat mVertexAttrib0Vector[4];
-    GLfloat mFakeVertexAttrib0BufferObjectVector[4];
-    size_t mFakeVertexAttrib0BufferObjectSize;
+    // Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the spec
+    // state tables, this isn't vertex shader /object/ state. This array is merely state
+    // useful to vertex shaders, but is global state.
+    UniquePtr<GLenum[]> mGenericVertexAttribTypes;
+    uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4];
+
     GLuint mFakeVertexAttrib0BufferObject;
-    WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
+    size_t mFakeVertexAttrib0BufferObjectSize;
+    bool mFakeVertexAttrib0DataDefined;
+    uint8_t mFakeVertexAttrib0Data[sizeof(float) * 4];
 
-    void GetVertexAttribFloat(GLuint index, GLfloat* out_result);
-    void GetVertexAttribInt(GLuint index, GLint* out_result);
-    void GetVertexAttribUint(GLuint index, GLuint* out_result);
     JSObject* GetVertexAttribFloat32Array(JSContext* cx, GLuint index);
     JSObject* GetVertexAttribInt32Array(JSContext* cx, GLuint index);
     JSObject* GetVertexAttribUint32Array(JSContext* cx, GLuint index);
 
     GLint mStencilRefFront;
     GLint mStencilRefBack;
     GLuint mStencilValueMaskFront;
     GLuint mStencilValueMaskBack;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -501,20 +501,18 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
             }
         }
 
         for (auto& binding : mIndexedUniformBufferBindings) {
             fnClearIfBuffer(binding.mBufferBinding);
         }
     }
 
-    for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) {
-        if (mBoundVertexArray->HasAttrib(i)) {
-            fnClearIfBuffer(mBoundVertexArray->mAttribs[i].mBuf);
-        }
+    for (auto& cur : mBoundVertexArray->mAttribs) {
+        fnClearIfBuffer(cur.mBuf);
     }
 
     ////
 
     buffer->RequestDelete();
 }
 
 bool
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -341,17 +341,17 @@ public:
             }
         } else {
             mWebGL->ClearBackbufferIfNeeded();
         }
 
         ////
 
         const size_t requiredVerts = firstVertex + vertCount;
-        if (!mWebGL->DoFakeVertexAttrib0(requiredVerts)) {
+        if (!mWebGL->DoFakeVertexAttrib0(funcName, requiredVerts)) {
             *out_error = true;
             return;
         }
         mDidFake = true;
 
         ////
         // Check UBO sizes.
 
@@ -389,16 +389,43 @@ public:
                                               funcName);
                 *out_error = true;
                 return;
             }
         }
 
         ////
 
+        for (const auto& progAttrib : mWebGL->mActiveProgramLinkInfo->attribs) {
+            const auto& loc = progAttrib.mLoc;
+
+            const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc];
+
+            GLenum attribDataBaseType;
+            if (attribData.mEnabled) {
+                attribDataBaseType = attribData.BaseType();
+            } else {
+                attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc];
+            }
+
+            if (attribDataBaseType != progAttrib.mBaseType) {
+                nsCString progType, dataType;
+                WebGLContext::EnumName(progAttrib.mBaseType, &progType);
+                WebGLContext::EnumName(attribDataBaseType, &dataType);
+                mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type"
+                                              " %s, but is being supplied with type %s.",
+                                              funcName, loc, progType.BeginReading(),
+                                              dataType.BeginReading());
+                *out_error = true;
+                return;
+            }
+        }
+
+        ////
+
         mWebGL->RunContextLossTimer();
     }
 
     ~ScopedDrawHelper() {
         if (mDidFake) {
             mWebGL->UndoFakeVertexAttrib0();
         }
     }
@@ -681,19 +708,21 @@ WebGLContext::DrawElements_check(const c
         ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
                               "size for given indices from the bound element array",
                               funcName);
         return false;
     }
 
     // Bug 1008310 - Check if buffer has been used with a different previous type
     if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) {
+        nsCString typeName;
+        WebGLContext::EnumName(type, &typeName);
         GenerateWarning("%s: bound element array buffer previously used with a type other than "
                         "%s, this will affect performance.",
-                        funcName, WebGLContext::EnumName(type));
+                        funcName, typeName.BeginReading());
     }
 
     return true;
 }
 
 static void
 HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
                          gl::GLContext::LocalErrorScope& errorScope)
@@ -952,141 +981,159 @@ WebGLContext::ValidateBufferFetching(con
     mBufferFetchingHasPerVertex = hasPerVertex;
     mMaxFetchedVertices = maxVertices;
     mMaxFetchedInstances = maxInstances;
 
     return true;
 }
 
 WebGLVertexAttrib0Status
-WebGLContext::WhatDoesVertexAttrib0Need()
+WebGLContext::WhatDoesVertexAttrib0Need() const
 {
     MOZ_ASSERT(mCurrentProgram);
     MOZ_ASSERT(mActiveProgramLinkInfo);
 
+    const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
+
     // work around Mac OSX crash, see bug 631420
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
-        mBoundVertexArray->IsAttribArrayEnabled(0) &&
+        isAttribArray0Enabled &&
         !mBufferFetch_IsAttrib0Active)
     {
         return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
     }
 #endif
 
-    if (MOZ_LIKELY(gl->IsGLES() ||
-                   mBoundVertexArray->IsAttribArrayEnabled(0)))
+    if (MOZ_LIKELY(!gl->IsCompatibilityProfile() ||
+                   isAttribArray0Enabled))
     {
         return WebGLVertexAttrib0Status::Default;
     }
 
     return mBufferFetch_IsAttrib0Active
            ? WebGLVertexAttrib0Status::EmulatedInitializedArray
            : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
 }
 
 bool
-WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
+WebGLContext::DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount)
 {
-    WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
+    if (!vertexCount) {
+        vertexCount = 1;
+    }
 
+    const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return true;
 
     if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
         GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
                         "to do expensive emulation work when running on desktop OpenGL "
                         "platforms, for example on Mac. It is preferable to always draw "
                         "with vertex attrib 0 array enabled, by using bindAttribLocation "
                         "to bind some always-used attribute to location 0.");
         mAlreadyWarnedAboutFakeVertexAttrib0 = true;
     }
 
-    CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
+    if (!mFakeVertexAttrib0BufferObject) {
+        gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
+        mFakeVertexAttrib0BufferObjectSize = 0;
+    }
+    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
+
+    ////
+
+    switch (mGenericVertexAttribTypes[0]) {
+    case LOCAL_GL_FLOAT:
+        gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0);
+        break;
 
+    case LOCAL_GL_INT:
+        gl->fVertexAttribIPointer(0, 4, LOCAL_GL_INT, 0, 0);
+        break;
+
+    case LOCAL_GL_UNSIGNED_INT:
+        gl->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT, 0, 0);
+        break;
+
+    default:
+        MOZ_CRASH();
+    }
+
+    ////
+
+    const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data);
+    const auto checked_dataSize = CheckedUint32(vertexCount) * bytesPerVert;
     if (!checked_dataSize.isValid()) {
         ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
                          "with %d vertices. Try reducing the number of vertices.", vertexCount);
         return false;
     }
+    const auto dataSize = checked_dataSize.value();
 
-    GLuint dataSize = checked_dataSize.value();
+    if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
+        gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
+        mFakeVertexAttrib0BufferObjectSize = dataSize;
+        mFakeVertexAttrib0DataDefined = false;
+    }
 
-    if (!mFakeVertexAttrib0BufferObject) {
-        gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
+    if (whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray)
+        return true;
+
+    ////
+
+    if (mFakeVertexAttrib0DataDefined &&
+        memcmp(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert) == 0)
+    {
+        return true;
     }
 
-    // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
-    // we don't need it to be, then consider it OK
-    bool vertexAttrib0BufferStatusOK =
-        mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need ||
-        (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray &&
-         whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray);
-
-    if (!vertexAttrib0BufferStatusOK ||
-        mFakeVertexAttrib0BufferObjectSize < dataSize ||
-        mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] ||
-        mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] ||
-        mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] ||
-        mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3])
-    {
-        mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need;
-        mFakeVertexAttrib0BufferObjectSize = dataSize;
-        mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
-        mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
-        mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
-        mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
-
-        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
-
-        GetAndFlushUnderlyingGLErrors();
+    ////
 
-        if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
-            auto array = MakeUniqueFallible<GLfloat[]>(4 * vertexCount);
-            if (!array) {
-                ErrorOutOfMemory("Fake attrib0 array.");
-                return false;
-            }
-            for(size_t i = 0; i < vertexCount; ++i) {
-                array[4 * i + 0] = mVertexAttrib0Vector[0];
-                array[4 * i + 1] = mVertexAttrib0Vector[1];
-                array[4 * i + 2] = mVertexAttrib0Vector[2];
-                array[4 * i + 3] = mVertexAttrib0Vector[3];
-            }
-            gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array.get(), LOCAL_GL_DYNAMIC_DRAW);
-        } else {
-            gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
-        }
-        GLenum error = GetAndFlushUnderlyingGLErrors();
+    const UniqueBuffer data(malloc(dataSize));
+    if (!data) {
+        ErrorOutOfMemory("%s: Failed to allocate fake vertex attrib 0 array.",
+                         funcName);
+        return false;
+    }
+    auto itr = (uint8_t*)data.get();
+    const auto itrEnd = itr + dataSize;
+    while (itr != itrEnd) {
+        memcpy(itr, mGenericVertexAttrib0Data, bytesPerVert);
+        itr += bytesPerVert;
+    }
 
-        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
+    {
+        gl::GLContext::LocalErrorScope errorScope(*gl);
 
-        // note that we do this error checking and early return AFTER having restored the buffer binding above
-        if (error) {
-            ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
-                             "with %d vertices. Try reducing the number of vertices.", vertexCount);
+        gl->fBufferSubData(LOCAL_GL_ARRAY_BUFFER, 0, dataSize, data.get());
+
+        const auto err = errorScope.GetError();
+        if (err) {
+            ErrorOutOfMemory("%s: Failed to upload fake vertex attrib 0 data.", funcName);
             return false;
         }
     }
 
-    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
-    gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
+    ////
 
+    memcpy(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert);
+    mFakeVertexAttrib0DataDefined = true;
     return true;
 }
 
 void
 WebGLContext::UndoFakeVertexAttrib0()
 {
-    WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
-
+    const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return;
 
-    if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].mBuf) {
+    if (mBoundVertexArray->mAttribs[0].mBuf) {
         const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.mBuf->mGLName);
         attrib0.DoVertexAttribPointer(gl, 0);
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -241,22 +241,23 @@ WebGLContext::ErrorName(GLenum error)
     case LOCAL_GL_NO_ERROR:
         return "NO_ERROR";
     default:
         MOZ_ASSERT(false);
         return "[unknown WebGL error]";
     }
 }
 
-// This version is 'fallible' and will return NULL if glenum is not recognized.
-const char*
-WebGLContext::EnumName(GLenum glenum)
+// This version is fallible and will return nullptr if unrecognized.
+static const char*
+GetEnumName(GLenum val)
 {
-    switch (glenum) {
+    switch (val) {
 #define XX(x) case LOCAL_GL_##x: return #x
+        XX(NONE);
         XX(ALPHA);
         XX(ATC_RGB);
         XX(ATC_RGBA_EXPLICIT_ALPHA);
         XX(ATC_RGBA_INTERPOLATED_ALPHA);
         XX(COMPRESSED_RGBA_PVRTC_2BPPV1);
         XX(COMPRESSED_RGBA_PVRTC_4BPPV1);
         XX(COMPRESSED_RGBA_S3TC_DXT1_EXT);
         XX(COMPRESSED_RGBA_S3TC_DXT3_EXT);
@@ -268,16 +269,17 @@ WebGLContext::EnumName(GLenum glenum)
         XX(DEPTH_COMPONENT);
         XX(DEPTH_COMPONENT16);
         XX(DEPTH_COMPONENT32);
         XX(DEPTH_STENCIL);
         XX(DEPTH24_STENCIL8);
         XX(DRAW_FRAMEBUFFER);
         XX(ETC1_RGB8_OES);
         XX(FLOAT);
+        XX(INT);
         XX(FRAMEBUFFER);
         XX(HALF_FLOAT);
         XX(LUMINANCE);
         XX(LUMINANCE_ALPHA);
         XX(READ_FRAMEBUFFER);
         XX(RGB);
         XX(RGB16F);
         XX(RGB32F);
@@ -569,26 +571,34 @@ WebGLContext::EnumName(GLenum glenum)
         XX(NUM_SAMPLE_COUNTS);
         XX(TEXTURE_IMMUTABLE_LEVELS);
 #undef XX
     }
 
     return nullptr;
 }
 
-void
-WebGLContext::EnumName(GLenum glenum, nsACString* out_name)
+/*static*/ void
+WebGLContext::EnumName(GLenum val, nsCString* out_name)
 {
-    const char* name = EnumName(glenum);
+    const char* name = GetEnumName(val);
     if (name) {
-        *out_name = nsDependentCString(name);
-    } else {
-        nsPrintfCString enumAsHex("<enum 0x%04x>", glenum);
-        *out_name = enumAsHex;
+        *out_name = name;
+        return;
     }
+
+    *out_name = nsPrintfCString("<enum 0x%04x>", val);
+}
+
+void
+WebGLContext::ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val)
+{
+    nsCString enumName;
+    EnumName(val, &enumName);
+    ErrorInvalidEnum("%s: Bad `%s`: %s", funcName, argName, enumName.BeginReading());
 }
 
 bool
 IsCompressedTextureFormat(GLenum format)
 {
     switch (format) {
     case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
     case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -538,17 +538,17 @@ WebGLContext::InitAndValidateGL(FailureR
 
     // For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled.
     if (gl->IsCompatibilityProfile())
         gl->fEnableVertexAttribArray(0);
 
     if (MinCapabilityMode())
         mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
     else
-        gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
+        gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
 
     if (mGLMaxVertexAttribs < 8) {
         const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
                                      mGLMaxVertexAttribs);
         *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason };
         return false;
     }
 
@@ -735,23 +735,16 @@ WebGLContext::InitAndValidateGL(FailureR
 
     if (IsWebGL2() &&
         !InitWebGL2(out_failReason))
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
-    // Default value for all disabled vertex attributes is [0, 0, 0, 1]
-    mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
-    for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {
-        mVertexAttribType[index] = LOCAL_GL_FLOAT;
-        VertexAttrib4f(index, 0, 0, 0, 1);
-    }
-
     mDefaultVertexArray = WebGLVertexArray::Create(this);
     mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
     mBoundVertexArray = mDefaultVertexArray;
 
     // OpenGL core profiles remove the default VAO object from version
     // 4.0.0. We create a default VAO for all core profiles,
     // regardless of version.
     //
@@ -778,16 +771,25 @@ WebGLContext::InitAndValidateGL(FailureR
     mPixelStore_UnpackAlignment = 4;
     mPixelStore_PackRowLength = 0;
     mPixelStore_PackSkipRows = 0;
     mPixelStore_PackSkipPixels = 0;
     mPixelStore_PackAlignment = 4;
 
     mPrimRestartTypeBytes = 0;
 
+    mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]);
+    std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT);
+
+    static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 };
+    memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
+           sizeof(mGenericVertexAttrib0Data));
+
+    mFakeVertexAttrib0BufferObject = 0;
+
     return true;
 }
 
 bool
 WebGLContext::ValidateFramebufferTarget(GLenum target,
                                         const char* const info)
 {
     bool isValid = true;
@@ -804,14 +806,13 @@ WebGLContext::ValidateFramebufferTarget(
         isValid = false;
         break;
     }
 
     if (MOZ_LIKELY(isValid)) {
         return true;
     }
 
-    ErrorInvalidEnum("%s: Invalid target: %s (0x%04x).", info, EnumName(target),
-                     target);
+    ErrorInvalidEnumArg(info, "target", target);
     return false;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -15,332 +15,199 @@
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #include "mozilla/Casting.h"
 
 namespace mozilla {
 
-void
-WebGLContext::GetVertexAttribFloat(GLuint index, GLfloat* out_result)
-{
-    if (index) {
-        gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
-    } else {
-        out_result[0] = mVertexAttrib0Vector[0];
-        out_result[1] = mVertexAttrib0Vector[1];
-        out_result[2] = mVertexAttrib0Vector[2];
-        out_result[3] = mVertexAttrib0Vector[3];
-    }
-}
-
-void
-WebGLContext::GetVertexAttribInt(GLuint index, GLint* out_result)
-{
-    if (index) {
-        gl->fGetVertexAttribIiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
-    } else {
-        out_result[0] = BitwiseCast<GLint>(mVertexAttrib0Vector[0]);
-        out_result[1] = BitwiseCast<GLint>(mVertexAttrib0Vector[1]);
-        out_result[2] = BitwiseCast<GLint>(mVertexAttrib0Vector[2]);
-        out_result[3] = BitwiseCast<GLint>(mVertexAttrib0Vector[3]);
-    }
-}
-
-void
-WebGLContext::GetVertexAttribUint(GLuint index, GLuint* out_result)
-{
-    if (index) {
-        gl->fGetVertexAttribIuiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
-    } else {
-        out_result[0] = BitwiseCast<GLuint>(mVertexAttrib0Vector[0]);
-        out_result[1] = BitwiseCast<GLuint>(mVertexAttrib0Vector[1]);
-        out_result[2] = BitwiseCast<GLuint>(mVertexAttrib0Vector[2]);
-        out_result[3] = BitwiseCast<GLuint>(mVertexAttrib0Vector[3]);
-    }
-}
-
 JSObject*
 WebGLContext::GetVertexAttribFloat32Array(JSContext* cx, GLuint index)
 {
     GLfloat attrib[4];
-    GetVertexAttribFloat(index, &attrib[0]);
+    if (index) {
+        gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, attrib);
+    } else {
+        memcpy(attrib, mGenericVertexAttrib0Data, sizeof(mGenericVertexAttrib0Data));
+    }
     return dom::Float32Array::Create(cx, this, 4, attrib);
 }
 
 JSObject*
 WebGLContext::GetVertexAttribInt32Array(JSContext* cx, GLuint index)
 {
     GLint attrib[4];
-    GetVertexAttribInt(index, &attrib[0]);
+    if (index) {
+        gl->fGetVertexAttribIiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, attrib);
+    } else {
+        memcpy(attrib, mGenericVertexAttrib0Data, sizeof(mGenericVertexAttrib0Data));
+    }
     return dom::Int32Array::Create(cx, this, 4, attrib);
 }
 
 JSObject*
-WebGLContext::GetVertexAttribUint32Array(JSContext* cx, GLuint index) {
+WebGLContext::GetVertexAttribUint32Array(JSContext* cx, GLuint index)
+{
     GLuint attrib[4];
-    GetVertexAttribUint(index, &attrib[0]);
+    if (index) {
+        gl->fGetVertexAttribIuiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, attrib);
+    } else {
+        memcpy(attrib, mGenericVertexAttrib0Data, sizeof(mGenericVertexAttrib0Data));
+    }
     return dom::Uint32Array::Create(cx, this, 4, attrib);
 }
 
-void
-WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
-{
-    if (IsContextLost())
-        return;
-
-    if (!ValidateAttribIndex(index, "vertexAttrib1f"))
-        return;
-
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib1f(index, x0);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = 0;
-        mVertexAttrib0Vector[2] = 0;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES())
-            gl->fVertexAttrib1f(index, x0);
-    }
-}
+////////////////////////////////////////
 
 void
-WebGLContext::VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1)
+WebGLContext::VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w,
+                             const char* funcName)
 {
+    if (!funcName) {
+        funcName = "vertexAttrib4f";
+    }
+
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "vertexAttrib2f"))
+    if (!ValidateAttribIndex(index, funcName))
         return;
 
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
+    ////
 
-    if (index) {
-        gl->fVertexAttrib2f(index, x0, x1);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = 0;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES())
-            gl->fVertexAttrib2f(index, x0, x1);
+    gl->MakeCurrent();
+    if (index || !gl->IsCompatibilityProfile()) {
+        gl->fVertexAttrib4f(index, x, y, z, w);
     }
-}
 
-void
-WebGLContext::VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2)
-{
-    if (IsContextLost())
-        return;
+    ////
 
-    if (!ValidateAttribIndex(index, "vertexAttrib3f"))
-        return;
-
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+    mGenericVertexAttribTypes[index] = LOCAL_GL_FLOAT;
 
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib3f(index, x0, x1, x2);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = x2;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES())
-            gl->fVertexAttrib3f(index, x0, x1, x2);
+    if (!index) {
+        const float data[4] = { x, y, z, w };
+        memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
-WebGLContext::VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1,
-                             GLfloat x2, GLfloat x3)
+WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w,
+                               const char* funcName)
 {
+    if (!funcName) {
+        funcName = "vertexAttribI4i";
+    }
+
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "vertexAttrib4f"))
+    if (!ValidateAttribIndex(index, funcName))
         return;
 
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
+    ////
 
-    if (index) {
-        gl->fVertexAttrib4f(index, x0, x1, x2, x3);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = x2;
-        mVertexAttrib0Vector[3] = x3;
-        if (gl->IsGLES())
-            gl->fVertexAttrib4f(index, x0, x1, x2, x3);
+    gl->MakeCurrent();
+    if (index || !gl->IsCompatibilityProfile()) {
+        gl->fVertexAttribI4i(index, x, y, z, w);
     }
-}
-
 
-void
-WebGLContext::VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
-                                   const GLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
-        return;
+    ////
 
-    if (!ValidateAttribIndex(index, "vertexAttrib1fv"))
-        return;
+    mGenericVertexAttribTypes[index] = LOCAL_GL_INT;
 
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
-    if (index) {
-        gl->fVertexAttrib1fv(index, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = GLfloat(0);
-        mVertexAttrib0Vector[2] = GLfloat(0);
-        mVertexAttrib0Vector[3] = GLfloat(1);
-        if (gl->IsGLES())
-            gl->fVertexAttrib1fv(index, ptr);
+    if (!index) {
+        const int32_t data[4] = { x, y, z, w };
+        memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
-WebGLContext::VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
-                                   const GLfloat* ptr)
+WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w,
+                                const char* funcName)
 {
-    if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
+    if (!funcName) {
+        funcName = "vertexAttribI4ui";
+    }
+
+    if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "vertexAttrib2fv"))
+    if (!ValidateAttribIndex(index, funcName))
         return;
 
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+    ////
+
+    gl->MakeCurrent();
+    if (index || !gl->IsCompatibilityProfile()) {
+        gl->fVertexAttribI4ui(index, x, y, z, w);
+    }
 
-    MakeContextCurrent();
-    if (index) {
-        gl->fVertexAttrib2fv(index, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = GLfloat(0);
-        mVertexAttrib0Vector[3] = GLfloat(1);
-        if (gl->IsGLES())
-            gl->fVertexAttrib2fv(index, ptr);
+    ////
+
+    mGenericVertexAttribTypes[index] = LOCAL_GL_UNSIGNED_INT;
+
+    if (!index) {
+        const uint32_t data[4] = { x, y, z, w };
+        memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
-void
-WebGLContext::VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
-                                   const GLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
-        return;
-
-    if (!ValidateAttribIndex(index, "vertexAttrib3fv"))
-        return;
-
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
-    if (index) {
-        gl->fVertexAttrib3fv(index, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = ptr[2];
-        mVertexAttrib0Vector[3] = GLfloat(1);
-        if (gl->IsGLES())
-            gl->fVertexAttrib3fv(index, ptr);
-    }
-}
-
-void
-WebGLContext::VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
-                                   const GLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
-        return;
-
-    if (!ValidateAttribIndex(index, "vertexAttrib4fv"))
-        return;
-
-    mVertexAttribType[index] = LOCAL_GL_FLOAT;
-
-    MakeContextCurrent();
-    if (index) {
-        gl->fVertexAttrib4fv(index, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = ptr[2];
-        mVertexAttrib0Vector[3] = ptr[3];
-        if (gl->IsGLES())
-            gl->fVertexAttrib4fv(index, ptr);
-    }
-}
+////////////////////////////////////////
 
 void
 WebGLContext::EnableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
     gl->fEnableVertexAttribArray(index);
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
     mBoundVertexArray->mAttribs[index].mEnabled = true;
 }
 
 void
 WebGLContext::DisableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
-    if (index || gl->IsGLES()) {
+    if (index || !gl->IsCompatibilityProfile()) {
         gl->fDisableVertexAttribArray(index);
     }
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
     mBoundVertexArray->mAttribs[index].mEnabled = false;
 }
 
 JS::Value
 WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv)
 {
+    const char funcName[] = "getVertexAttrib";
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateAttribIndex(index, "getVertexAttrib"))
+    if (!ValidateAttribIndex(index, funcName))
         return JS::NullValue();
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
 
     MakeContextCurrent();
 
     switch (pname) {
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
         return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv);
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
@@ -364,17 +231,17 @@ WebGLContext::GetVertexAttrib(JSContext*
         {
             return JS::Int32Value(mBoundVertexArray->mAttribs[index].mDivisor);
         }
         break;
 
     case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
         {
             JS::RootedObject obj(cx);
-            switch (mVertexAttribType[index]) {
+            switch (mGenericVertexAttribTypes[index]) {
             case LOCAL_GL_FLOAT:
                 obj = GetVertexAttribFloat32Array(cx, index);
                 break;
 
             case LOCAL_GL_INT:
                 obj =  GetVertexAttribInt32Array(cx, index);
                 break;
 
@@ -412,17 +279,16 @@ WebGLContext::GetVertexAttribOffset(GLui
         return 0;
 
     if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
         ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
         return 0;
     }
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
     return mBoundVertexArray->mAttribs[index].ByteOffset();
 }
 
 void
 WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
                                   WebGLboolean normalized, GLsizei stride,
                                   WebGLintptr byteOffset)
 {
@@ -431,17 +297,16 @@ WebGLContext::VertexAttribPointer(GLuint
 
     if (!ValidateAttribIndex(index, "vertexAttribPointer"))
         return;
 
     if (!ValidateAttribPointer(false, index, size, type, normalized, stride, byteOffset, "vertexAttribPointer"))
         return;
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
 
     InvalidateBufferFetching();
 
     /* XXX make work with bufferSubData & heterogeneous types
      if (type != mBoundArrayBuffer->GLType())
      return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
      */
 
@@ -460,17 +325,16 @@ WebGLContext::VertexAttribDivisor(GLuint
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
         return;
 
     MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
 
     WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
     vd.mDivisor = divisor;
 
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -418,23 +418,24 @@ WebGLFBAttachPoint::GetParameter(const c
             if (webgl->IsWebGL2())
                 return JS::NullValue();
 
             break;
 
         default:
             break;
         }
-
+        nsCString attachmentName;
+        WebGLContext::EnumName(attachment, &attachmentName);
         if (webgl->IsWebGL2()) {
             webgl->ErrorInvalidOperation("%s: No attachment at %s.", funcName,
-                                         webgl->EnumName(attachment));
+                                         attachmentName.BeginReading());
         } else {
             webgl->ErrorInvalidEnum("%s: No attachment at %s.", funcName,
-                                    webgl->EnumName(attachment));
+                                    attachmentName.BeginReading());
         }
         return JS::NullValue();
     }
 
     bool isPNameValid = false;
     switch (pname) {
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
         return JS::Int32Value(mTexturePtr ? LOCAL_GL_TEXTURE
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -78,17 +78,59 @@ AssembleName(const nsCString& baseName, 
     *out_name = baseName;
     if (isArray) {
         out_name->Append('[');
         out_name->AppendInt(uint64_t(arrayIndex));
         out_name->Append(']');
     }
 }
 
-//////////
+////
+
+static GLenum
+AttribBaseType(GLenum attribType)
+{
+    switch (attribType) {
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_FLOAT_VEC2:
+    case LOCAL_GL_FLOAT_VEC3:
+    case LOCAL_GL_FLOAT_VEC4:
+
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_MAT2x3:
+    case LOCAL_GL_FLOAT_MAT2x4:
+
+    case LOCAL_GL_FLOAT_MAT3x2:
+    case LOCAL_GL_FLOAT_MAT3:
+    case LOCAL_GL_FLOAT_MAT3x4:
+
+    case LOCAL_GL_FLOAT_MAT4x2:
+    case LOCAL_GL_FLOAT_MAT4x3:
+    case LOCAL_GL_FLOAT_MAT4:
+        return LOCAL_GL_FLOAT;
+
+    case LOCAL_GL_INT:
+    case LOCAL_GL_INT_VEC2:
+    case LOCAL_GL_INT_VEC3:
+    case LOCAL_GL_INT_VEC4:
+        return LOCAL_GL_INT;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_UNSIGNED_INT_VEC2:
+    case LOCAL_GL_UNSIGNED_INT_VEC3:
+    case LOCAL_GL_UNSIGNED_INT_VEC4:
+        return LOCAL_GL_UNSIGNED_INT;
+
+    default:
+        MOZ_ASSERT(false, "unexpected attrib elemType");
+        return 0;
+    }
+}
+
+////
 
 /*static*/ const webgl::UniformInfo::TexListT*
 webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
 {
     const auto& webgl = activeInfo->mWebGL;
 
     switch (activeInfo->mElemType) {
     case LOCAL_GL_SAMPLER_2D:
@@ -220,17 +262,18 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
         ///////
 
         const bool isArray = false;
         const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
                                                                        elemType, isArray,
                                                                        userName,
                                                                        mappedName);
-        const webgl::AttribInfo attrib = {activeInfo, uint32_t(loc)};
+        const GLenum baseType = AttribBaseType(elemType);
+        const webgl::AttribInfo attrib = {activeInfo, uint32_t(loc), baseType};
         info->attribs.push_back(attrib);
     }
 
     // Uniforms
 
     const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
 
     GLuint numActiveUniforms = 0;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -33,17 +33,18 @@ class OwningUnsignedLongOrUint32ArrayOrB
 template<typename> class Sequence;
 } // namespace dom
 
 namespace webgl {
 
 struct AttribInfo final
 {
     const RefPtr<WebGLActiveInfo> mActiveInfo;
-    uint32_t mLoc;
+    const uint32_t mLoc;
+    const GLenum mBaseType;
 };
 
 struct UniformInfo final
 {
     typedef decltype(WebGLContext::mBound2DTextures) TexListT;
 
     const RefPtr<WebGLActiveInfo> mActiveInfo;
     const TexListT* const mSamplerTexList;
@@ -117,16 +118,17 @@ struct LinkedProgramInfo final
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
 {
     friend class WebGLTransformFeedback;
+    friend struct webgl::LinkedProgramInfo;
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
 
     explicit WebGLProgram(WebGLContext* webgl);
 
     void Delete();
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -131,18 +131,17 @@ WebGLQuery::GetQueryParameter(GLenum pna
     const char funcName[] = "getQueryParameter";
 
     switch (pname) {
     case LOCAL_GL_QUERY_RESULT_AVAILABLE:
     case LOCAL_GL_QUERY_RESULT:
         break;
 
     default:
-        mContext->ErrorInvalidEnum("%s: Invalid pname: %s", funcName,
-                                   mContext->EnumName(pname));
+        mContext->ErrorInvalidEnumArg(funcName, "pname", pname);
         return;
     }
 
     if (!mTarget) {
         mContext->ErrorInvalidOperation("%s: Query has never been active.", funcName);
         return;
     }
 
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -128,22 +128,21 @@ ValidateSamplerParameterParams(WebGLCont
             return true;
 
         default:
             break;
         }
         break;
 
     default:
-        webgl->ErrorInvalidEnum("%s: invalid pname: %s", funcName,
-                                webgl->EnumName(pname));
+        webgl->ErrorInvalidEnumArg(funcName, "pname", pname);
         return false;
     }
 
-    webgl->ErrorInvalidEnum("%s: invalid param: %s", funcName, webgl->EnumName(paramInt));
+    webgl->ErrorInvalidEnumArg(funcName, "param", paramInt);
     return false;
 }
 
 void
 WebGLSampler::SamplerParameter(const char* funcName, GLenum pname,
                                const FloatOrInt& param)
 {
     if (!ValidateSamplerParameterParams(mContext, funcName, pname, param))
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -84,16 +84,17 @@ public:
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShader)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLShader)
 
 public:
     const GLuint mGLName;
     const GLenum mType;
+
 protected:
     nsString mSource;
     nsCString mCleanSource;
 
     UniquePtr<webgl::ShaderValidator> mValidator;
     nsCString mValidationLog;
     bool mTranslationSuccessful;
     nsCString mTranslatedSource;
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -19,16 +19,17 @@ WebGLVertexArray::WrapObject(JSContext* 
 {
     return dom::WebGLVertexArrayObjectBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
     : WebGLRefCountedObject(webgl)
     , mGLName(0)
 {
+    mAttribs.SetLength(mContext->mGLMaxVertexAttribs);
     mContext->mVertexArrays.insertBack(this);
 }
 
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* webgl)
 {
     WebGLVertexArray* array;
     if (webgl->gl->IsSupported(gl::GLFeature::vertex_array_object)) {
@@ -50,26 +51,16 @@ WebGLVertexArray::Delete()
 }
 
 bool
 WebGLVertexArray::IsVertexArray() const
 {
     return IsVertexArrayImpl();
 }
 
-void
-WebGLVertexArray::EnsureAttrib(GLuint index)
-{
-    MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs));
-
-    if (index >= mAttribs.Length()) {
-        mAttribs.SetLength(index + 1);
-    }
-}
-
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArray,
                                       mAttribs,
                                       mElementArrayBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLVertexArray, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLVertexArray, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -28,24 +28,16 @@ public:
     static WebGLVertexArray* Create(WebGLContext* webgl);
 
     void BindVertexArray() {
         // Bind to dummy value to signal that this vertex array has ever been
         // bound.
         BindVertexArrayImpl();
     };
 
-    void EnsureAttrib(GLuint index);
-    bool HasAttrib(GLuint index) const {
-        return index < mAttribs.Length();
-    }
-    bool IsAttribArrayEnabled(GLuint index) const {
-        return HasAttrib(index) && mAttribs[index].mEnabled;
-    }
-
     // Implement parent classes:
     void Delete();
     bool IsVertexArray() const;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
@@ -67,16 +59,17 @@ protected:
     virtual void BindVertexArrayImpl() = 0;
     virtual void DeleteImpl() = 0;
     virtual bool IsVertexArrayImpl() const = 0;
 
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
+    friend class ScopedDrawHelper;
     friend class WebGLContext;
     friend class WebGLVertexArrayFake;
     friend class WebGL2Context;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_VERTEX_ARRAY_H_
--- a/dom/canvas/WebGLVertexAttribData.cpp
+++ b/dom/canvas/WebGLVertexAttribData.cpp
@@ -38,24 +38,47 @@ CalcBytesPerVertex(GLenum type, uint8_t 
 
     default:
         MOZ_CRASH("Bad `type`.");
     }
 
     return bytesPerType * size;
 }
 
+static GLenum
+AttribPointerBaseType(bool integerFunc, GLenum type)
+{
+    if (!integerFunc)
+        return LOCAL_GL_FLOAT;
+
+    switch (type) {
+    case LOCAL_GL_BYTE:
+    case LOCAL_GL_SHORT:
+    case LOCAL_GL_INT:
+        return LOCAL_GL_INT;
+
+    case LOCAL_GL_UNSIGNED_BYTE:
+    case LOCAL_GL_UNSIGNED_SHORT:
+    case LOCAL_GL_UNSIGNED_INT:
+        return LOCAL_GL_UNSIGNED_INT;
+
+    default:
+        MOZ_CRASH();
+    }
+}
+
 void
 WebGLVertexAttribData::VertexAttribPointer(bool integerFunc, WebGLBuffer* buf,
                                            uint8_t size, GLenum type, bool normalized,
                                            uint32_t stride, uint64_t byteOffset)
 {
     mIntegerFunc = integerFunc;
     mBuf = buf;
     mType = type;
+    mBaseType = AttribPointerBaseType(integerFunc, type);
     mSize = size;
     mBytesPerVertex = CalcBytesPerVertex(mType, mSize);
     mNormalized = normalized;
     mStride = stride;
     mExplicitStride = (mStride ? mStride : mBytesPerVertex);
     mByteOffset = byteOffset;
 }
 
--- a/dom/canvas/WebGLVertexAttribData.h
+++ b/dom/canvas/WebGLVertexAttribData.h
@@ -20,29 +20,31 @@ public:
     bool mEnabled;
 
 private:
     bool mIntegerFunc;
 public:
     WebGLRefPtr<WebGLBuffer> mBuf;
 private:
     GLenum mType;
+    GLenum mBaseType;
     uint8_t mSize; // num of mType vals per vert
     uint8_t mBytesPerVertex;
     bool mNormalized;
     uint32_t mStride; // bytes
     uint32_t mExplicitStride;
     uint64_t mByteOffset;
 
 public:
 
 #define GETTER(X) const decltype(m##X)& X() const { return m##X; }
 
     GETTER(IntegerFunc)
     GETTER(Type)
+    GETTER(BaseType)
     GETTER(Size)
     GETTER(BytesPerVertex)
     GETTER(Normalized)
     GETTER(Stride)
     GETTER(ExplicitStride)
     GETTER(ByteOffset)
 
 #undef GETTER
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertex-attrib-i-render.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertex-attrib-i-render.html
@@ -35,16 +35,24 @@
 <script id='vshader' type='x-shader/x-vertex'>#version 300 es
 layout(location=0) in ivec2 p;
 layout(location=1) in ivec4 a;
 void main()
 {
     gl_Position = vec4(p.x + a.x + a.y + a.z + a.w, p.y, 0.0, 10.0);
 }
 </script>
+<script id='vshader_unsigned' type='x-shader/x-vertex'>#version 300 es
+layout(location=0) in ivec2 p;
+layout(location=1) in uvec4 a;
+void main()
+{
+    gl_Position = vec4(p.x + int(a.x + a.y + a.z + a.w), p.y, 0.0, 10.0);
+}
+</script>
 <script id='fshader' type='x-shader/x-fragment'>#version 300 es
 precision mediump float;
 layout(location=0) out vec4 oColor;
 void main()
 {
     oColor = vec4(1.0, 0.0, 0.0, 1.0);
 }
 </script>
@@ -64,30 +72,36 @@ function checkRedPortion(gl, w, low, hig
 
 function runTest() {
     var wtu = WebGLTestUtils;
     var gl = wtu.create3DContext('testbed', { preserveDrawingBuffer : true }, 2);
     if (!gl) {
         testFailed('could not create context');
         return;
     }
-    var program = wtu.setupProgram(gl, ['vshader', 'fshader'])
+    var program = wtu.setupProgram(gl, ['vshader', 'fshader']);
+    var program_unsigned = wtu.setupProgram(gl, ['vshader_unsigned', 'fshader']);
 
     gl.enableVertexAttribArray(0);
     var pos = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, pos);
     gl.bufferData(gl.ARRAY_BUFFER, new Int32Array([-10, -10, 10, -10, -10, 10, 10, 10]), gl.STATIC_DRAW);
 
     gl.vertexAttribIPointer(0, 2, gl.INT, 4 * 2, 0);
 
     debug('Test vertexAttribI4[ui][v] by setting different combinations that add up to 15 and use that when rendering.');
     var vals = [[2, -3, 6, 10], [1, 3, 1, 10], [-10, 3, 2, 20], [5, 6, 2, 2]];
     var tests = ['vertexAttribI4i', 'vertexAttribI4ui', 'vertexAttribI4iv', 'vertexAttribI4uiv'];
 
     for (var ii = 0; ii < 4; ++ii) {
+        if (ii % 2 == 0) {
+            gl.useProgram(program);
+        } else {
+            gl.useProgram(program_unsigned);
+        }
         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
         if (ii < 2) {
             gl[tests[ii]](1, vals[ii][0], vals[ii][1], vals[ii][2], vals[ii][3]);
         } else {
             gl[tests[ii]](1, vals[ii]);
         }
         gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
 
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertexattribipointer-offsets.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/attribs/gl-vertexattribipointer-offsets.html
@@ -46,16 +46,27 @@ layout(location=1) in vec4 aColor;
 out vec4 vColor;
 void main()
 {
     gl_Position = vec4(aPosition);
     vColor = aColor;
 }
 </script>
 
+<script id="vshader_unsigned" type="x-shader/x-vertex">#version 300 es
+layout(location=0) in uvec4 aPosition;
+layout(location=1) in vec4 aColor;
+out vec4 vColor;
+void main()
+{
+    gl_Position = vec4(aPosition);
+    vColor = aColor;
+}
+
+</script>
 <script id="fshader" type="x-shader/x-fragment">#version 300 es
 precision mediump float;
 in vec4 vColor;
 layout(location=0) out vec4 oColor;
 void main()
 {
     oColor = vColor;
 }
@@ -65,16 +76,17 @@ void main()
 "use strict";
 function init()
 {
     description("test vertexAttribIPointer offsets work");
 
     var wtu = WebGLTestUtils;
     var gl = wtu.create3DContext("example", undefined, 2);
     var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["vPosition"]);
+    var program_unsigned = wtu.setupProgram(gl, ["vshader_unsigned", "fshader"], ["vPosition"]);
 
     var tests = [
       { data: new Int32Array([ 0, 1, 0, 1, 0, 0, 0, 0, 0 ]),
         type: gl.INT,
         componentSize: 4,
       },
       { data: new Uint32Array([ 0, 1, 0, 1, 0, 0, 0, 0, 0 ]),
         type: gl.UNSIGNED_INT,
@@ -111,16 +123,21 @@ function init()
         var test = tests[tt];
         for (var oo = 0; oo < 3; ++oo) {
             for (var ss = 0; ss < 3; ++ss) {
                 var offset = (oo + 1) * test.componentSize;
                 var color = (count % 2) ? [1, 0, 0, 1] : [0, 1, 0, 1];
                 var stride = test.componentSize * kNumComponents + test.componentSize * ss;
                 debug("");
                 debug("check with " + wtu.glEnumToString(gl, test.type) + " at offset: " + offset + " with stride:" + stride);
+                if (test.type == gl.INT || test.type == gl.SHORT || test.type == gl.BYTE) {
+                    gl.useProgram(program);
+                } else {
+                    gl.useProgram(program_unsigned);
+                }
                 gl.vertexAttrib4fv(1, color);
                 var data = new Uint8Array(test.componentSize * kNumVerts * kNumComponents + stride * (kNumVerts - 1));
                 var view = new Uint8Array(test.data.buffer);
                 var size = test.componentSize * kNumComponents;
                 for (var jj = 0; jj < kNumVerts; ++jj) {
                     var off1 = jj * size;
                     var off2 = jj * stride;
                     for (var zz = 0; zz < size; ++zz) {
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -5923,17 +5923,16 @@ skip-if = (os == 'android')
 [generated/test_conformance__attribs__gl-bindAttribLocation-matrix.html]
 skip-if = (os == 'android')
 [generated/test_conformance__attribs__gl-bindAttribLocation-repeated.html]
 [generated/test_conformance__attribs__gl-disabled-vertex-attrib.html]
 fail-if = (os == 'android')
 [generated/test_conformance__attribs__gl-enable-vertex-attrib.html]
 [generated/test_conformance__attribs__gl-matrix-attributes.html]
 [generated/test_conformance__attribs__gl-vertex-attrib-render.html]
-fail-if = (os == 'linux')
 [generated/test_conformance__attribs__gl-vertex-attrib-zero-issues.html]
 [generated/test_conformance__attribs__gl-vertex-attrib.html]
 [generated/test_conformance__attribs__gl-vertexattribpointer-offsets.html]
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__buffers__buffer-bind-test.html]
 [generated/test_conformance__buffers__buffer-data-and-buffer-sub-data.html]
 [generated/test_conformance__buffers__buffer-data-array-buffer-delete.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -470,18 +470,16 @@ fail-if = (os == 'b2g')
 fail-if = (os == 'b2g')
 [generated/test_conformance__programs__get-active-test.html]
 fail-if = (os == 'b2g')
 
 
 ########################################################################
 ########################################################################
 # Linux
-[generated/test_conformance__attribs__gl-vertex-attrib-render.html]
-fail-if = (os == 'linux')
 [generated/test_conformance__glsl__constructors__glsl-construct-vec-mat-corner-cases.html]
 # mozalloc_abort in libglsl.so
 skip-if = (os == 'linux')
 [generated/test_conformance__glsl__constructors__glsl-construct-vec3.html]
 # Crashes
 skip-if = (os == 'linux')
 [generated/test_conformance__glsl__constructors__glsl-construct-vec4.html]
 # Inferred crash from vec3 above.
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -688,27 +688,23 @@ interface WebGLRenderingContextBase {
     void uniform2i(WebGLUniformLocation? location, GLint x, GLint y);
     void uniform3i(WebGLUniformLocation? location, GLint x, GLint y, GLint z);
     void uniform4i(WebGLUniformLocation? location, GLint x, GLint y, GLint z, GLint w);
 
     void useProgram(WebGLProgram? program);
     void validateProgram(WebGLProgram program);
 
     void vertexAttrib1f(GLuint indx, GLfloat x);
-    void vertexAttrib1fv(GLuint indx, Float32Array values);
-    void vertexAttrib1fv(GLuint indx, sequence<GLfloat> values);
+    void vertexAttrib1fv(GLuint indx, Float32List values);
     void vertexAttrib2f(GLuint indx, GLfloat x, GLfloat y);
-    void vertexAttrib2fv(GLuint indx, Float32Array values);
-    void vertexAttrib2fv(GLuint indx, sequence<GLfloat> values);
+    void vertexAttrib2fv(GLuint indx, Float32List values);
     void vertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z);
-    void vertexAttrib3fv(GLuint indx, Float32Array values);
-    void vertexAttrib3fv(GLuint indx, sequence<GLfloat> values);
+    void vertexAttrib3fv(GLuint indx, Float32List values);
     void vertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
-    void vertexAttrib4fv(GLuint indx, Float32Array values);
-    void vertexAttrib4fv(GLuint indx, sequence<GLfloat> values);
+    void vertexAttrib4fv(GLuint indx, Float32List values);
     void vertexAttribPointer(GLuint indx, GLint size, GLenum type,
                              GLboolean normalized, GLsizei stride, GLintptr offset);
 
     void viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 };
 
 [Exposed=(Window,Worker),
  Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]