Bug 1325301 (flattened) - Validate attribute base types match data base types. - r=bz,daoshengmu
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 21 Dec 2016 19:42:07 -0800
changeset 327530 c0eb5a38f7168f4731c3e796d5f25d9788e9d87a
parent 327529 c22ce760279e78eb670f23118d8c320893a09502
child 327531 c274a7c25dc7e3a9228b3b9a5c1917e985ff4192
push id31135
push userkwierso@gmail.com
push dateThu, 29 Dec 2016 20:04:00 +0000
treeherdermozilla-central@79ef93672445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, daoshengmu
bugs1325301
milestone53.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 1325301 (flattened) - Validate attribute base types match data base types. - r=bz,daoshengmu 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
@@ -403,27 +403,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:
@@ -1281,53 +1281,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
@@ -1356,18 +1377,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;
@@ -1404,17 +1425,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;
@@ -1868,26 +1889,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"]