Bug 1109945 - Rewrite shader/program handling. - r=kamidphish
☠☠ backed out by 83c4b180a815 ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 12 Jan 2015 19:51:20 -0800
changeset 250581 b5c6dd9423cf8830adf266c672ef550e83dc43f6
parent 250580 02fb8f978e91aee84765ab33d863b320e25764f8
child 250582 7e4b2f88f560bb9f296d0ac35c4e945438b29050
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs1109945
milestone38.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 1109945 - Rewrite shader/program handling. - r=kamidphish
browser/devtools/shadereditor/test/browser_se_shaders-edit-02.js
browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js
dom/canvas/WebGL2Context.cpp
dom/canvas/WebGL2ContextTransformFeedback.cpp
dom/canvas/WebGL2ContextUniforms.cpp
dom/canvas/WebGLActiveInfo.cpp
dom/canvas/WebGLActiveInfo.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLContextVertices.cpp
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShader.h
dom/canvas/WebGLShaderValidator.cpp
dom/canvas/WebGLShaderValidator.h
dom/canvas/WebGLUniformInfo.h
dom/canvas/WebGLUniformLocation.cpp
dom/canvas/WebGLUniformLocation.h
dom/canvas/WebGLValidateStrings.cpp
dom/canvas/WebGLValidateStrings.h
dom/canvas/moz.build
dom/canvas/test/webgl-conformance/conformance/misc/bad-arguments-test.html
gfx/gl/GLContext.h
gfx/gl/GLContextSymbols.h
modules/libpref/init/all.js
--- a/browser/devtools/shadereditor/test/browser_se_shaders-edit-02.js
+++ b/browser/devtools/shadereditor/test/browser_se_shaders-edit-02.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 ///////////////////
 //
 // Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed. 
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Shader Editor is still waiting for a WebGL context to be created.");
 
 /**
  * Tests if compile or linkage errors are emitted when a shader source
  * gets malformed after being edited.
  */
 
@@ -21,45 +21,51 @@ function ifWebGLSupported() {
   yield promise.all([
     once(gFront, "program-linked"),
     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
   ]);
 
   let vsEditor = yield ShadersEditorsView._getEditor("vs");
   let fsEditor = yield ShadersEditorsView._getEditor("fs");
 
+
   vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
   let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
 
   ok(error,
     "The new vertex shader source was compiled with errors.");
-  is(error.compile, "",
-    "The compilation status should be empty.");
-  isnot(error.link, "",
-    "The linkage status should not be empty.");
-  is(error.link.split("ERROR").length - 1, 2,
-    "The linkage status contains two errors.");
-  ok(error.link.contains("ERROR: 0:8: 'constructor'"),
-    "A constructor error is contained in the linkage status.");
-  ok(error.link.contains("ERROR: 0:8: 'assign'"),
-    "An assignment error is contained in the linkage status.");
+
+  // The implementation has the choice to defer all compile-time errors to link time.
+  let infoLog = (error.compile != "") ? error.compile : error.link;
+
+  isnot(infoLog, "",
+    "The one of the compile or link info logs should not be empty.");
+  is(infoLog.split("ERROR").length - 1, 2,
+    "The info log status contains two errors.");
+  ok(infoLog.contains("ERROR: 0:8: 'constructor'"),
+    "A constructor error is contained in the info log.");
+  ok(infoLog.contains("ERROR: 0:8: 'assign'"),
+    "An assignment error is contained in the info log.");
+
 
   fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
   [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
 
   ok(error,
     "The new fragment shader source was compiled with errors.");
-  is(error.compile, "",
-    "The compilation status should be empty.");
-  isnot(error.link, "",
-    "The linkage status should not be empty.");
-  is(error.link.split("ERROR").length - 1, 1,
-    "The linkage status contains one error.");
-  ok(error.link.contains("ERROR: 0:6: 'constructor'"),
-    "A constructor error is contained in the linkage status.");
+
+  infoLog = (error.compile != "") ? error.compile : error.link;
+
+  isnot(infoLog, "",
+    "The one of the compile or link info logs should not be empty.");
+  is(infoLog.split("ERROR").length - 1, 1,
+    "The info log contains one error.");
+  ok(infoLog.contains("ERROR: 0:6: 'constructor'"),
+    "A constructor error is contained in the info log.");
+
 
   yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
   yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
 
   vsEditor.replaceText("vec4", { line: 7, ch: 22 }, { line: 7, ch: 26 });
   [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
   ok(!error, "The new vertex shader source was compiled successfully.");
 
--- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js
+++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js
@@ -18,26 +18,28 @@ function ifWebGLSupported() {
   let newVertSource = oldVertSource.replace("vec4", "vec3");
 
   try {
     yield vertexShader.compile(newVertSource);
     ok(false, "Vertex shader was compiled with a defective source!");
   } catch (error) {
     ok(error,
       "The new vertex shader source was compiled with errors.");
-    is(error.compile, "",
-      "The compilation status should be empty.");
-    isnot(error.link, "",
-      "The linkage status should not be empty.");
-    is(error.link.split("ERROR").length - 1, 2,
-      "The linkage status contains two errors.");
-    ok(error.link.contains("ERROR: 0:8: 'constructor'"),
-      "A constructor error is contained in the linkage status.");
-    ok(error.link.contains("ERROR: 0:8: 'assign'"),
-      "An assignment error is contained in the linkage status.");
+
+    // The implementation has the choice to defer all compile-time errors to link time.
+    let infoLog = (error.compile != "") ? error.compile : error.link;
+
+    isnot(infoLog, "",
+      "The one of the compile or link info logs should not be empty.");
+    is(infoLog.split("ERROR").length - 1, 2,
+      "The info log contains two errors.");
+    ok(infoLog.contains("ERROR: 0:8: 'constructor'"),
+      "A constructor error is contained in the info log.");
+    ok(infoLog.contains("ERROR: 0:8: 'assign'"),
+      "An assignment error is contained in the info log.");
   }
 
   yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
   yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
   ok(true, "The shader was reverted to the old source.");
 
   let vertSource = yield vertexShader.getText();
   ok(vertSource.contains("vec4(aVertexPosition, 1.0);"),
@@ -47,24 +49,26 @@ function ifWebGLSupported() {
   let newFragSource = oldFragSource.replace("vec3", "vec4");
 
   try {
     yield fragmentShader.compile(newFragSource);
     ok(false, "Fragment shader was compiled with a defective source!");
   } catch (error) {
     ok(error,
       "The new fragment shader source was compiled with errors.");
-    is(error.compile, "",
-      "The compilation status should be empty.");
-    isnot(error.link, "",
-      "The linkage status should not be empty.");
-    is(error.link.split("ERROR").length - 1, 1,
-      "The linkage status contains one error.");
-    ok(error.link.contains("ERROR: 0:6: 'constructor'"),
-      "A constructor error is contained in the linkage status.");
+
+    // The implementation has the choice to defer all compile-time errors to link time.
+    let infoLog = (error.compile != "") ? error.compile : error.link;
+
+    isnot(infoLog, "",
+      "The one of the compile or link info logs should not be empty.");
+    is(infoLog.split("ERROR").length - 1, 1,
+      "The info log contains one error.");
+    ok(infoLog.contains("ERROR: 0:6: 'constructor'"),
+      "A constructor error is contained in the info log.");
   }
 
   yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
   yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
   ok(true, "The shader was reverted to the old source.");
 
   let fragSource = yield fragmentShader.getText();
   ok(fragSource.contains("vec3 vFragmentColor;"),
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -147,12 +147,14 @@ WebGLContext::InitWebGL2()
     mBoundUniformBuffers =
         MakeUnique<WebGLRefPtr<WebGLBuffer>[]>(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
     mBoundTransformFeedback = mDefaultTransformFeedback;
     auto xfBuffers = new WebGLRefPtr<WebGLBuffer>[mGLMaxTransformFeedbackSeparateAttribs];
     mBoundTransformFeedbackBuffers.reset(xfBuffers);
 
+    mBypassShaderValidation = true;
+
     return true;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -207,48 +207,50 @@ WebGL2Context::TransformFeedbackVaryings
 
     GLsizei count = varyings.Length();
     GLchar** tmpVaryings = (GLchar**) nsMemory::Alloc(count * sizeof(GLchar*));
 
     for (GLsizei n = 0; n < count; n++) {
         tmpVaryings[n] = (GLchar*) ToNewCString(varyings[n]);
     }
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     MakeContextCurrent();
     gl->fTransformFeedbackVaryings(progname, count, tmpVaryings, bufferMode);
 
     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, tmpVaryings);
 }
 
-
 already_AddRefed<WebGLActiveInfo>
 WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getTransformFeedbackVarying: program", program))
         return nullptr;
 
     MakeContextCurrent();
 
     GLint len = 0;
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     gl->fGetProgramiv(progname, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &len);
     if (!len)
         return nullptr;
 
     UniquePtr<char[]> name(new char[len]);
     GLint tfsize = 0;
     GLuint tftype = 0;
 
     gl->fGetTransformFeedbackVarying(progname, index, len, &len, &tfsize, &tftype, name.get());
     if (len == 0 || tfsize == 0 || tftype == 0)
         return nullptr;
 
-    // TODO(djg): Reverse lookup of name
-    // nsCString reverseMappedName;
-    // prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName);
+    MOZ_CRASH("todo");
+    /*
+    // Reverse lookup of name
+    nsCString reverseMappedName;
+    prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName);
 
     nsRefPtr<WebGLActiveInfo> result = new WebGLActiveInfo(tfsize, tftype, nsDependentCString(name.get()));
     return result.forget();
+    */
 }
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -391,17 +391,17 @@ WebGL2Context::GetUniformIndices(WebGLPr
         return;
 
     if (!ValidateObject("getUniformIndices: program", program))
         return;
 
     if (!uniformNames.Length())
         return;
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     size_t count = uniformNames.Length();
     nsTArray<GLuint>& arr = retval.SetValue();
 
     MakeContextCurrent();
 
     for (size_t n = 0; n < count; n++) {
         NS_LossyConvertUTF16toASCII name(uniformNames[n]);
         //        const GLchar* glname = name.get();
@@ -426,17 +426,17 @@ WebGL2Context::GetActiveUniforms(WebGLPr
 
     if (!ValidateObject("getActiveUniforms: program", program))
         return;
 
     size_t count = uniformIndices.Length();
     if (!count)
         return;
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     nsTArray<GLint>& arr = retval.SetValue();
     arr.SetLength(count);
 
     MakeContextCurrent();
     gl->fGetActiveUniformsiv(progname, count, uniformIndices.Elements(), pname,
                              arr.Elements());
 }
 
@@ -445,27 +445,24 @@ WebGL2Context::GetUniformBlockIndex(WebG
                                     const nsAString& uniformBlockName)
 {
     if (IsContextLost())
         return 0;
 
     if (!ValidateObject("getUniformBlockIndex: program", program))
         return 0;
 
-    if (!ValidateGLSLVariableName(uniformBlockName, "getUniformBlockIndex"))
-        return 0;
+    // Leave this unchecked for now.
 
-    NS_LossyConvertUTF16toASCII cname(uniformBlockName);
-    nsCString mappedName;
-    program->MapIdentifier(cname, &mappedName);
+    const NS_LossyConvertUTF16toASCII cname(uniformBlockName);
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
 
     MakeContextCurrent();
-    return gl->fGetUniformBlockIndex(progname, mappedName.get());
+    return gl->fGetUniformBlockIndex(progname, cname.BeginReading());
 }
 
 static bool
 GetUniformBlockActiveUniforms(gl::GLContext* gl, JSContext* cx,
                               WebGL2Context* owner, GLuint progname,
                               GLuint uniformBlockIndex,
                               JS::MutableHandleObject out_array)
 {
@@ -496,17 +493,17 @@ WebGL2Context::GetActiveUniformBlockPara
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockParameter: program", program))
         return;
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     GLint param = 0;
 
     MakeContextCurrent();
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
         gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex, pname, &param);
@@ -546,17 +543,17 @@ WebGL2Context::GetActiveUniformBlockName
                                          nsAString& retval)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockName: program", program))
         return;
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
     GLchar nameBuffer[WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH];
     GLsizei length = 0;
 
     MakeContextCurrent();
     gl->fGetActiveUniformBlockName(progname, uniformBlockIndex,
                                    WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH, &length,
                                    nameBuffer);
     retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(nameBuffer)));
@@ -569,13 +566,13 @@ WebGL2Context::UniformBlockBinding(WebGL
                                    GLuint uniformBlockBinding)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("uniformBlockBinding: program", program))
         return;
 
-    GLuint progname = program->GLName();
+    GLuint progname = program->mGLName;
 
     MakeContextCurrent();
     gl->fUniformBlockBinding(progname, uniformBlockIndex, uniformBlockBinding);
 }
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -1,20 +1,99 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLActiveInfo.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
-#include "WebGLContext.h"
-#include "WebGLTexture.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLActiveInfo::WrapObject(JSContext* cx)
+static uint8_t
+ElemSizeFromType(GLenum elemType)
 {
-    return dom::WebGLActiveInfoBinding::Wrap(cx, this);
+    switch (elemType) {
+    case LOCAL_GL_BOOL:
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_INT:
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+        return 1;
+
+    case LOCAL_GL_BOOL_VEC2:
+    case LOCAL_GL_FLOAT_VEC2:
+    case LOCAL_GL_INT_VEC2:
+        return 2;
+
+    case LOCAL_GL_BOOL_VEC3:
+    case LOCAL_GL_FLOAT_VEC3:
+    case LOCAL_GL_INT_VEC3:
+        return 3;
+
+
+    case LOCAL_GL_BOOL_VEC4:
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_VEC4:
+    case LOCAL_GL_INT_VEC4:
+        return 4;
+
+    case LOCAL_GL_FLOAT_MAT2x3:
+    case LOCAL_GL_FLOAT_MAT3x2:
+        return 6;
+
+    case LOCAL_GL_FLOAT_MAT2x4:
+    case LOCAL_GL_FLOAT_MAT4x2:
+        return 8;
+
+    case LOCAL_GL_FLOAT_MAT3:
+        return 9;
+
+    case LOCAL_GL_FLOAT_MAT3x4:
+    case LOCAL_GL_FLOAT_MAT4x3:
+        return 12;
+
+    case LOCAL_GL_FLOAT_MAT4:
+        return 16;
+
+    default:
+        MOZ_CRASH("Bad `elemType`.");
+    }
 }
 
+WebGLActiveInfo::WebGLActiveInfo(GLint elemCount, GLenum elemType, bool isArray,
+                                 const nsACString& baseUserName,
+                                 const nsACString& baseMappedName)
+    : mElemCount(elemCount)
+    , mElemType(elemType)
+    , mBaseUserName(baseUserName)
+    , mIsArray(isArray)
+    , mElemSize(ElemSizeFromType(elemType))
+    , mBaseMappedName(baseMappedName)
+{ }
+
+////////////////////////////////////////////////////////////////////////////////
+
+JSObject*
+WebGLActiveInfo::WrapObject(JSContext* js)
+{
+    return dom::WebGLActiveInfoBinding::Wrap(js, this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLActiveInfo)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLActiveInfo, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLActiveInfo, Release)
+
 } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -1,55 +1,83 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_ACTIVE_INFO_H_
 #define WEBGL_ACTIVE_INFO_H_
 
+#include "GLDefs.h"
 #include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "nsISupportsImpl.h" // NS_INLINE_DECL_REFCOUNTING
 #include "nsString.h"
-#include "WebGLObjectModel.h"
+#include "nsWrapperCache.h"
 
 namespace mozilla {
 
 class WebGLActiveInfo MOZ_FINAL
+    : public nsWrapperCache
 {
 public:
-    WebGLActiveInfo(GLint size, GLenum type, const nsACString& name)
-        : mSize(size)
-        , mType(type)
-        , mName(NS_ConvertASCIItoUTF16(name))
-    {}
+    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLActiveInfo)
+
+    const GLint mElemCount; // `size`
+    const GLenum mElemType; // `type`
+    const nsCString mBaseUserName; // `name`, but ASCII, and without any final "[0]".
+
+    // Not actually part of ActiveInfo:
+    const bool mIsArray;
+    const uint8_t mElemSize;
+    const nsCString mBaseMappedName; // Without any final "[0]".
+
+    WebGLActiveInfo(GLint elemCount, GLenum elemType, bool isArray,
+                    const nsACString& baseUserName, const nsACString& baseMappedName);
+
+
+    /* GLES 2.0.25, p33:
+     *   This command will return as much information about active
+     *   attributes as possible. If no information is available, length will
+     *   be set to zero and name will be an empty string. This situation
+     *   could arise if GetActiveAttrib is issued after a failed link.
+     *
+     * It's the same for GetActiveUniform.
+     */
+    static WebGLActiveInfo* CreateInvalid() {
+        return new WebGLActiveInfo();
+    }
 
     // WebIDL attributes
-
     GLint Size() const {
-        return mSize;
+        return mElemCount;
     }
 
     GLenum Type() const {
-        return mType;
+        return mElemType;
     }
 
     void GetName(nsString& retval) const {
-        retval = mName;
+        CopyASCIItoUTF16(mBaseUserName, retval);
+        if (mIsArray)
+            retval.AppendLiteral("[0]");
     }
 
-    JSObject* WrapObject(JSContext* cx);
-
-   NS_INLINE_DECL_REFCOUNTING(WebGLActiveInfo)
+    virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
 
 private:
+    WebGLActiveInfo()
+        : mElemCount(0)
+        , mElemType(0)
+        , mBaseUserName("")
+        , mIsArray(false)
+        , mElemSize(0)
+        , mBaseMappedName("")
+    { }
+
     // Private destructor, to discourage deletion outside of Release():
-    ~WebGLActiveInfo()
-    {
-    }
-
-    GLint mSize;
-    GLenum mType;
-    nsString mName;
+    ~WebGLActiveInfo() { }
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_ACTIVE_INFO_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -197,31 +197,30 @@ WebGLContextOptions::WebGLContextOptions
 {
     // Set default alpha state based on preference.
     if (Preferences::GetBool("webgl.default-no-alpha", false))
         alpha = false;
 }
 
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
+    , mBypassShaderValidation(false)
     , mNeedsFakeNoAlpha(false)
 {
     mGeneration = 0;
     mInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
 
     mActiveTexture = 0;
     mPixelStoreFlipY = false;
     mPixelStorePremultiplyAlpha = false;
     mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
 
-    mShaderValidation = true;
-
     mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
 
     mVertexAttrib0Vector[0] = 0;
     mVertexAttrib0Vector[1] = 0;
     mVertexAttrib0Vector[2] = 0;
     mVertexAttrib0Vector[3] = 1;
     mFakeVertexAttrib0BufferObjectVector[0] = 0;
     mFakeVertexAttrib0BufferObjectVector[1] = 0;
@@ -325,16 +324,17 @@ WebGLContext::DestroyResourcesAndContext
     mBoundArrayBuffer = nullptr;
     mBoundCopyReadBuffer = nullptr;
     mBoundCopyWriteBuffer = nullptr;
     mBoundPixelPackBuffer = nullptr;
     mBoundPixelUnpackBuffer = nullptr;
     mBoundTransformFeedbackBuffer = nullptr;
     mBoundUniformBuffer = nullptr;
     mCurrentProgram = nullptr;
+    mActiveProgramLinkInfo = nullptr;
     mBoundDrawFramebuffer = nullptr;
     mBoundReadFramebuffer = nullptr;
     mActiveOcclusionQuery = nullptr;
     mBoundRenderbuffer = nullptr;
     mBoundVertexArray = nullptr;
     mDefaultVertexArray = nullptr;
     mBoundTransformFeedback = nullptr;
     mDefaultTransformFeedback = nullptr;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -14,16 +14,17 @@
 #include "mozilla/WeakPtr.h"
 
 #include "GLDefs.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLContextUnchecked.h"
 #include "WebGLObjectModel.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
+#include "WebGLShaderValidator.h"
 #include "WebGLStrongTypes.h"
 #include <stdarg.h>
 
 #include "nsTArray.h"
 #include "nsCycleCollectionNoteChild.h"
 
 #include "nsIDOMWebGLRenderingContext.h"
 #include "nsICanvasRenderingContextInternal.h"
@@ -91,16 +92,20 @@ class ImageData;
 struct WebGLContextAttributes;
 template<typename> struct Nullable;
 }
 
 namespace gfx {
 class SourceSurface;
 }
 
+namespace webgl {
+struct LinkedProgramInfo;
+}
+
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
     // these are defaults
     WebGLContextOptions();
@@ -370,17 +375,19 @@ public:
                            GLenum srcAlpha, GLenum dstAlpha);
     GLenum CheckFramebufferStatus(GLenum target);
     void Clear(GLbitfield mask);
     void ClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a);
     void ClearDepth(GLclampf v);
     void ClearStencil(GLint v);
     void ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a);
     void CompileShader(WebGLShader* shader);
-    void CompressedTexImage2D(GLenum texImageTarget, GLint level,
+    void CompileShaderANGLE(WebGLShader* shader);
+    void CompileShaderBypass(WebGLShader* shader, const nsCString& shaderSource);
+    void CompressedTexImage2D(GLenum target, GLint level,
                               GLenum internalformat, GLsizei width,
                               GLsizei height, GLint border,
                               const dom::ArrayBufferView& view);
     void CompressedTexSubImage2D(GLenum texImageTarget, GLint level,
                                  GLint xoffset, GLint yoffset, GLsizei width,
                                  GLsizei height, GLenum format,
                                  const dom::ArrayBufferView& view);
     void CopyTexImage2D(GLenum texImageTarget, GLint level,
@@ -832,18 +839,20 @@ public:
         UniformMatrix4fv_base(loc, transpose, value.Length(),
                               value.Elements());
     }
     void UniformMatrix4fv_base(WebGLUniformLocation* loc,
                                WebGLboolean transpose, size_t arrayLength,
                                const float* data);
 
     void UseProgram(WebGLProgram* prog);
+
     bool ValidateAttribArraySetter(const char* name, uint32_t count,
                                    uint32_t arrayLength);
+    bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName);
     bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
                                GLenum setterType, const char* info,
                                GLuint* out_rawLoc);
     bool ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
                                     size_t setterArraySize, const char* info,
                                     GLuint* out_rawLoc,
                                     GLsizei* out_numElementsToUpload);
@@ -1113,18 +1122,19 @@ protected:
     GLuint mActiveTexture;
 
     // glGetError sources:
     bool mEmitContextLostErrorOnce;
     GLenum mWebGLError;
     GLenum mUnderlyingGLError;
     GLenum GetAndFlushUnderlyingGLErrors();
 
-    // whether shader validation is supported
-    bool mShaderValidation;
+    bool mBypassShaderValidation;
+
+    webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
     int32_t mGLMaxVertexAttribs;
     int32_t mGLMaxTextureUnits;
     int32_t mGLMaxTextureSize;
     int32_t mGLMaxTextureSizeLog2;
     int32_t mGLMaxCubeMapTextureSize;
     int32_t mGLMaxCubeMapTextureSizeLog2;
@@ -1139,16 +1149,20 @@ protected:
     GLuint  mGLMaxTransformFeedbackSeparateAttribs;
     GLuint  mGLMaxUniformBufferBindings;
 
 public:
     GLuint MaxVertexAttribs() const {
         return mGLMaxVertexAttribs;
     }
 
+    GLuint GLMaxTextureUnits() const {
+        return mGLMaxTextureUnits;
+    }
+
 
     bool IsFormatValidForFB(GLenum sizedFormat) const;
 
 protected:
     // Represents current status of the context with respect to context loss.
     // That is, whether the context is lost, and what part of the context loss
     // process we currently are at.
     // This is used to support the WebGL spec's asyncronous nature in handling
@@ -1220,20 +1234,16 @@ protected:
                               WebGLTexImageFunc func, WebGLTexDimensions dims);
     bool ValidateDrawModeEnum(GLenum mode, const char* info);
     bool ValidateAttribIndex(GLuint index, const char* info);
     bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
                                WebGLboolean normalized, GLsizei stride,
                                WebGLintptr byteOffset, const char* info);
     bool ValidateStencilParamsForDrawCall();
 
-    bool ValidateGLSLVariableName(const nsAString& name, const char* info);
-    bool ValidateGLSLCharacter(char16_t c);
-    bool ValidateGLSLString(const nsAString& string, const char* info);
-
     bool ValidateCopyTexImage(GLenum internalFormat, WebGLTexImageFunc func,
                               WebGLTexDimensions dims);
 
     bool ValidateSamplerParameterName(GLenum pname, const char* info);
     bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info);
 
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
@@ -1270,16 +1280,20 @@ protected:
                                   GLsizei levelHeight, WebGLTexImageFunc func,
                                   WebGLTexDimensions dims);
     bool ValidateCompTexImageDataSize(GLint level, GLenum internalFormat,
                                       GLsizei width, GLsizei height,
                                       uint32_t byteLength,
                                       WebGLTexImageFunc func,
                                       WebGLTexDimensions dims);
 
+    bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
+                                           WebGLProgram* program,
+                                           const char* funcName);
+
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
 
     // If jsArrayType is MaxTypedArrayViewType, it means no array.
@@ -1398,16 +1412,17 @@ protected:
     void ForceLoseContext(bool simulateLoss = false);
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
+    RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
     uint32_t mMaxFramebufferColorAttachments;
 
     bool ValidateFramebufferTarget(GLenum target, const char* const info);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
     WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
@@ -1590,17 +1605,17 @@ inline bool
 WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object)
 {
     MOZ_ASSERT(object);
 
     if (!ValidateObjectAllowDeletedOrNull(info, object))
         return false;
 
     if (object->IsDeleted()) {
-        ErrorInvalidValue("%s: deleted object passed as argument", info);
+        ErrorInvalidValue("%s: Deleted object passed as argument.", info);
         return false;
     }
 
     return true;
 }
 
 template<class ObjectType>
 inline bool
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/CheckedInt.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
-#include "WebGLUniformInfo.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gl;
 
 // For a Tegra workaround.
@@ -405,21 +404,25 @@ void WebGLContext::Draw_cleanup()
 /*
  * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
  * that will be legal to be read from bound VBOs.
  */
 
 bool
 WebGLContext::ValidateBufferFetching(const char* info)
 {
+    MOZ_ASSERT(mCurrentProgram);
+    // Note that mCurrentProgram->IsLinked() is NOT GUARANTEED.
+    MOZ_ASSERT(mActiveProgramLinkInfo);
+
 #ifdef DEBUG
     GLint currentProgram = 0;
     MakeContextCurrent();
     gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
-    MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
+    MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName,
                "WebGL: current program doesn't agree with GL state");
 #endif
 
     if (mBufferFetchingIsVerified)
         return true;
 
     bool hasPerVertex = false;
     uint32_t maxVertices = UINT32_MAX;
@@ -436,17 +439,17 @@ WebGLContext::ValidateBufferFetching(con
 
         if (vd.buf == nullptr) {
             ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
             return false;
         }
 
         // If the attrib is not in use, then we don't have to validate
         // it, just need to make sure that the binding is non-null.
-        if (!mCurrentProgram->IsAttribInUse(i))
+        if (!mActiveProgramLinkInfo->HasActiveAttrib(i))
             continue;
 
         // the base offset
         CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
         CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
 
         if (!checked_byteLength.isValid() ||
             !checked_sizeOfLastElement.isValid())
@@ -493,34 +496,35 @@ WebGLContext::ValidateBufferFetching(con
 
     return true;
 }
 
 WebGLVertexAttrib0Status
 WebGLContext::WhatDoesVertexAttrib0Need()
 {
     MOZ_ASSERT(mCurrentProgram);
+    MOZ_ASSERT(mActiveProgramLinkInfo);
 
     // work around Mac OSX crash, see bug 631420
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
         mBoundVertexArray->IsAttribArrayEnabled(0) &&
-        !mCurrentProgram->IsAttribInUse(0))
+        !mActiveProgramLinkInfo->HasActiveAttrib(0))
     {
         return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
     }
 #endif
 
     if (MOZ_LIKELY(gl->IsGLES() ||
                    mBoundVertexArray->IsAttribArrayEnabled(0)))
     {
         return WebGLVertexAttrib0Status::Default;
     }
 
-    return mCurrentProgram->IsAttribInUse(0)
+    return mActiveProgramLinkInfo->HasActiveAttrib(0)
            ? WebGLVertexAttrib0Status::EmulatedInitializedArray
            : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
 }
 
 bool
 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
 {
     WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -47,21 +47,18 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Endian.h"
 #include "mozilla/fallible.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::gfx;
 using namespace mozilla::gl;
-using namespace mozilla::gfx;
-
-static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum* baseType,
-                                           GLint* unitSize);
 
 static const WebGLRectangleObject*
 CurValidFBRectObject(const WebGLContext* webgl,
                      const WebGLFramebuffer* boundFB)
 {
     const WebGLRectangleObject* rect = nullptr;
 
     if (boundFB) {
@@ -117,63 +114,34 @@ WebGLContext::ActiveTexture(GLenum textu
 void
 WebGLContext::AttachShader(WebGLProgram* program, WebGLShader* shader)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("attachShader: program", program) ||
         !ValidateObject("attachShader: shader", shader))
+    {
         return;
-
-    // Per GLSL ES 2.0, we can only have one of each type of shader
-    // attached.  This renders the next test somewhat moot, but we'll
-    // leave it for when we support more than one shader of each type.
-    if (program->HasAttachedShaderOfType(shader->ShaderType()))
-        return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program");
-
-    if (!program->AttachShader(shader))
-        return ErrorInvalidOperation("attachShader: shader is already attached");
+    }
+
+    program->AttachShader(shader);
 }
 
 void
 WebGLContext::BindAttribLocation(WebGLProgram* prog, GLuint location,
                                  const nsAString& name)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("bindAttribLocation: program", prog))
         return;
 
-    GLuint progname = prog->GLName();
-
-    if (!ValidateGLSLVariableName(name, "bindAttribLocation"))
-        return;
-
-    if (location >= MaxVertexAttribs()) {
-        return ErrorInvalidValue("bindAttribLocation: `location` must be less"
-                                 " than MAX_VERTEX_ATTRIBS.");
-    }
-
-    if (StringBeginsWith(name, NS_LITERAL_STRING("gl_")))
-        return ErrorInvalidOperation("bindAttribLocation: can't set the"
-                                     " location of a name that starts with"
-                                     " 'gl_'.");
-
-    NS_LossyConvertUTF16toASCII cname(name);
-    nsCString mappedName;
-    if (mShaderValidation) {
-        WebGLProgram::HashMapIdentifier(cname, &mappedName);
-    } else {
-        mappedName.Assign(cname);
-    }
-
-    MakeContextCurrent();
-    gl->fBindAttribLocation(progname, location, mappedName.get());
+    prog->BindAttribLocation(location, name);
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
 {
     if (IsContextLost())
         return;
 
@@ -824,24 +792,25 @@ WebGLContext::DeleteShader(WebGLShader* 
 }
 
 void
 WebGLContext::DetachShader(WebGLProgram* program, WebGLShader* shader)
 {
     if (IsContextLost())
         return;
 
+    // It's valid to attempt to detach a deleted shader, since it's still a
+    // shader.
     if (!ValidateObject("detachShader: program", program) ||
-        // it's valid to attempt to detach a deleted shader, since it's
-        // still a shader
         !ValidateObjectAllowDeleted("detashShader: shader", shader))
+    {
         return;
-
-    if (!program->DetachShader(shader))
-        return ErrorInvalidOperation("detachShader: shader is not attached");
+    }
+
+    program->DetachShader(shader);
 }
 
 void
 WebGLContext::DepthFunc(GLenum func)
 {
     if (IsContextLost())
         return;
 
@@ -969,60 +938,16 @@ WebGLContext::FrontFace(GLenum mode)
         default:
             return ErrorInvalidEnumInfo("frontFace: mode", mode);
     }
 
     MakeContextCurrent();
     gl->fFrontFace(mode);
 }
 
-already_AddRefed<WebGLActiveInfo>
-WebGLContext::GetActiveAttrib(WebGLProgram* prog, uint32_t index)
-{
-    if (IsContextLost())
-        return nullptr;
-
-    if (!ValidateObject("getActiveAttrib: program", prog))
-        return nullptr;
-
-    MakeContextCurrent();
-    GLuint progname = prog->GLName();
-
-    GLuint activeAttribs = 0;
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTES,
-                      (GLint*)&activeAttribs);
-    if (index >= activeAttribs) {
-        ErrorInvalidValue("`index` (%i) must be less than ACTIVE_ATTRIBUTES"
-                          " (%i).",
-                          index, activeAttribs);
-        return nullptr;
-    }
-
-    GLint len = 0;
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
-    if (len == 0)
-        return nullptr;
-
-    nsAutoArrayPtr<char> name(new char[len]);
-    GLint attrsize = 0;
-    GLuint attrtype = 0;
-
-    gl->fGetActiveAttrib(progname, index, len, &len, &attrsize, &attrtype, name);
-    if (attrsize == 0 || attrtype == 0) {
-        return nullptr;
-    }
-
-    nsCString reverseMappedName;
-    prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
-
-    nsRefPtr<WebGLActiveInfo> retActiveInfo =
-        new WebGLActiveInfo(attrsize, attrtype, reverseMappedName);
-    return retActiveInfo.forget();
-}
-
 void
 WebGLContext::GenerateMipmap(GLenum rawTarget)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateTextureTargetEnum(rawTarget, "generateMipmap"))
         return;
@@ -1078,119 +1003,68 @@ WebGLContext::GenerateMipmap(GLenum rawT
         gl->fGenerateMipmap(target.get());
         gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter().get());
     } else {
         gl->fGenerateMipmap(target.get());
     }
 }
 
 already_AddRefed<WebGLActiveInfo>
-WebGLContext::GetActiveUniform(WebGLProgram* prog, uint32_t index)
+WebGLContext::GetActiveAttrib(WebGLProgram* prog, GLuint index)
+{
+    if (IsContextLost())
+        return nullptr;
+
+    if (!ValidateObject("getActiveAttrib: program", prog))
+        return nullptr;
+
+    return prog->GetActiveAttrib(index);
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLContext::GetActiveUniform(WebGLProgram* prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getActiveUniform: program", prog))
         return nullptr;
 
-    MakeContextCurrent();
-    GLuint progname = prog->GLName();
-
-    GLuint activeUniforms = 0;
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS,
-                      (GLint*)&activeUniforms);
-    if (index >= activeUniforms) {
-        ErrorInvalidValue("`index` (%i) must be less than ACTIVE_UNIFORMS"
-                          " (%i).",
-                          index, activeUniforms);
-        return nullptr;
-    }
-
-    GLint len = 0;
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len);
-    if (len == 0)
-        return nullptr;
-
-    nsAutoArrayPtr<char> name(new char[len]);
-
-    GLint usize = 0;
-    GLuint utype = 0;
-
-    gl->fGetActiveUniform(progname, index, len, &len, &usize, &utype, name);
-    if (len == 0 || usize == 0 || utype == 0) {
-        return nullptr;
-    }
-
-    nsCString reverseMappedName;
-    prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
-
-    // OpenGL ES 2.0 specifies that if foo is a uniform array, GetActiveUniform returns its name as "foo[0]".
-    // See section 2.10 page 35 in the OpenGL ES 2.0.24 specification:
-    //
-    // > If the active uniform is an array, the uniform name returned in name will always
-    // > be the name of the uniform array appended with "[0]".
-    //
-    // There is no such requirement in the OpenGL (non-ES) spec and indeed we have OpenGL implementations returning
-    // "foo" instead of "foo[0]". So, when implementing WebGL on top of desktop OpenGL, we must check if the
-    // returned name ends in [0], and if it doesn't, append that.
-    //
-    // In principle we don't need to do that on OpenGL ES, but this is such a tricky difference between the ES and non-ES
-    // specs that it seems probable that some ES implementers will overlook it. Since the work-around is quite cheap,
-    // we do it unconditionally.
-    if (usize > 1 && reverseMappedName.CharAt(reverseMappedName.Length()-1) != ']')
-        reverseMappedName.AppendLiteral("[0]");
-
-    nsRefPtr<WebGLActiveInfo> retActiveInfo =
-        new WebGLActiveInfo(usize, utype, reverseMappedName);
-    return retActiveInfo.forget();
+    return prog->GetActiveUniform(index);
 }
 
 void
 WebGLContext::GetAttachedShaders(WebGLProgram* prog,
                                  Nullable<nsTArray<nsRefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowNull("getAttachedShaders", prog))
+    if (!prog) {
+        ErrorInvalidValue("getAttachedShaders: Invalid program.");
         return;
-
-    MakeContextCurrent();
-
-    if (!prog) {
-        retval.SetNull();
-        ErrorInvalidValue("getAttachedShaders: invalid program");
-    } else if (prog->AttachedShaders().Length() == 0) {
-        retval.SetValue().TruncateLength(0);
-    } else {
-        retval.SetValue().AppendElements(prog->AttachedShaders());
     }
+
+    if (!ValidateObject("getAttachedShaders", prog))
+        return;
+
+    prog->GetAttachedShaders(&retval.SetValue());
 }
 
 GLint
 WebGLContext::GetAttribLocation(WebGLProgram* prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
     if (!ValidateObject("getAttribLocation: program", prog))
         return -1;
 
-    if (!ValidateGLSLVariableName(name, "getAttribLocation"))
-        return -1;
-
-    NS_LossyConvertUTF16toASCII cname(name);
-    nsCString mappedName;
-    prog->MapIdentifier(cname, &mappedName);
-
-    GLuint progname = prog->GLName();
-
-    MakeContextCurrent();
-    return gl->fGetAttribLocation(progname, mappedName.get());
+    return prog->GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
@@ -1541,110 +1415,33 @@ JS::Value
 WebGLContext::GetProgramParameter(WebGLProgram* prog, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
     if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
         return JS::NullValue();
 
-    GLuint progname = prog->GLName();
-
-    MakeContextCurrent();
-
-    GLint i = 0;
-
-    if (IsWebGL2()) {
-        switch (pname) {
-        case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
-        case LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
-            gl->fGetProgramiv(progname, pname, &i);
-            return JS::Int32Value(i);
-        }
-    }
-
-    switch (pname) {
-        case LOCAL_GL_ATTACHED_SHADERS:
-        case LOCAL_GL_ACTIVE_UNIFORMS:
-        case LOCAL_GL_ACTIVE_ATTRIBUTES:
-            gl->fGetProgramiv(progname, pname, &i);
-            return JS::Int32Value(i);
-
-        case LOCAL_GL_DELETE_STATUS:
-            return JS::BooleanValue(prog->IsDeleteRequested());
-
-        case LOCAL_GL_LINK_STATUS:
-            return JS::BooleanValue(prog->LinkStatus());
-
-        case LOCAL_GL_VALIDATE_STATUS:
-#ifdef XP_MACOSX
-            // See comment in ValidateProgram below.
-            if (gl->WorkAroundDriverBugs())
-                i = 1;
-            else
-                gl->fGetProgramiv(progname, pname, &i);
-#else
-            gl->fGetProgramiv(progname, pname, &i);
-#endif
-            return JS::BooleanValue(bool(i));
-
-        default:
-            ErrorInvalidEnumInfo("getProgramParameter: parameter", pname);
-    }
-
-    return JS::NullValue();
+    return prog->GetProgramParameter(pname);
 }
 
 void
 WebGLContext::GetProgramInfoLog(WebGLProgram* prog, nsAString& retval)
 {
-    nsAutoCString s;
-    GetProgramInfoLog(prog, s);
-    if (s.IsVoid())
-        retval.SetIsVoid(true);
-    else
-        CopyASCIItoUTF16(s, retval);
-}
-
-void
-WebGLContext::GetProgramInfoLog(WebGLProgram* prog, nsACString& retval)
-{
+    retval.SetIsVoid(true);
+
     if (IsContextLost())
-    {
-        retval.SetIsVoid(true);
-        return;
-    }
-
-    if (!ValidateObject("getProgramInfoLog: program", prog)) {
-        retval.Truncate();
         return;
-    }
-
-    GLuint progname = prog->GLName();
-
-    MakeContextCurrent();
-
-    GLint k = -1;
-    gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k);
-    if (k == -1) {
-        // If GetProgramiv doesn't modify |k|,
-        // it's because there was a GL error.
-        // GetProgramInfoLog should return null on error. (Bug 746740)
-        retval.SetIsVoid(true);
+
+    if (!ValidateObject("getProgramInfoLog: program", prog))
         return;
-    }
-
-    if (k == 0) {
-        retval.Truncate();
-        return;
-    }
-
-    retval.SetCapacity(k);
-    gl->fGetProgramInfoLog(progname, k, &k, (char*) retval.BeginWriting());
-    retval.SetLength(k);
+
+    prog->GetProgramInfoLog(&retval);
+
+    retval.SetIsVoid(false);
 }
 
 // here we have to support all pnames with both int and float params.
 // See this discussion:
 //  https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
 void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname,
                                      GLint* intParamPtr,
                                      GLfloat* floatParamPtr)
@@ -1842,181 +1639,44 @@ WebGLContext::GetTexParameterInternal(co
         default:
             ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
     }
 
     return JS::NullValue();
 }
 
 JS::Value
-WebGLContext::GetUniform(JSContext* cx, WebGLProgram* prog,
-                         WebGLUniformLocation* location)
+WebGLContext::GetUniform(JSContext* js, WebGLProgram* prog,
+                         WebGLUniformLocation* loc)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObject("getUniform: program", prog))
-        return JS::NullValue();
-
-    if (!ValidateObject("getUniform: location", location))
+    if (!ValidateObject("getUniform: `program`", prog))
         return JS::NullValue();
 
-    if (location->Program() != prog) {
-        ErrorInvalidValue("getUniform: this uniform location corresponds to another program");
-        return JS::NullValue();
-    }
-
-    if (location->ProgramGeneration() != prog->Generation()) {
-        ErrorInvalidOperation("getUniform: this uniform location is obsolete since the program has been relinked");
+    if (!ValidateObject("getUniform: `location`", loc))
         return JS::NullValue();
-    }
-
-    GLuint progname = prog->GLName();
-
-    MakeContextCurrent();
-
-    GLint uniforms = 0;
-    GLint uniformNameMaxLength = 0;
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, &uniforms);
-    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength);
-
-    // we now need the type info to switch between fGetUniformfv and fGetUniformiv
-    // the only way to get that is to iterate through all active uniforms by index until
-    // one matches the given uniform location.
-    GLenum uniformType = 0;
-    nsAutoArrayPtr<GLchar> uniformName(new GLchar[uniformNameMaxLength]);
-    // this buffer has 16 more bytes to be able to store [index] at the end.
-    nsAutoArrayPtr<GLchar> uniformNameBracketIndex(new GLchar[uniformNameMaxLength + 16]);
-
-    GLint index;
-    for (index = 0; index < uniforms; ++index) {
-        GLsizei length;
-        GLint size;
-        gl->fGetActiveUniform(progname, index, uniformNameMaxLength, &length,
-                              &size, &uniformType, uniformName);
-        if (gl->fGetUniformLocation(progname, uniformName) == location->Location())
-            break;
-
-        // now we handle the case of array uniforms. In that case, fGetActiveUniform returned as 'size'
-        // the biggest index used plus one, so we need to loop over that. The 0 index has already been handled above,
-        // so we can start at one. For each index, we construct the string uniformName + "[" + index + "]".
-        if (size > 1) {
-            bool found_it = false;
-            if (uniformName[length - 1] == ']') { // if uniformName ends in [0]
-                // remove the [0] at the end
-                length -= 3;
-                uniformName[length] = 0;
-            }
-            for (GLint arrayIndex = 1; arrayIndex < size; arrayIndex++) {
-                sprintf(uniformNameBracketIndex.get(), "%s[%d]", uniformName.get(), arrayIndex);
-                if (gl->fGetUniformLocation(progname, uniformNameBracketIndex) == location->Location()) {
-                    found_it = true;
-                    break;
-                }
-            }
-            if (found_it) break;
-        }
-    }
-
-    if (index == uniforms) {
-        GenerateWarning("getUniform: internal error: hit an OpenGL driver bug");
+
+    if (!loc->ValidateForProgram(prog, this, "getUniform"))
         return JS::NullValue();
-    }
-
-    GLenum baseType;
-    GLint unitSize;
-    if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) {
-        GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType);
-        return JS::NullValue();
-    }
-
-    // this should never happen
-    if (unitSize > 16) {
-        GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize);
-        return JS::NullValue();
-    }
-
-    if (baseType == LOCAL_GL_FLOAT) {
-        GLfloat fv[16] = { GLfloat(0) };
-        gl->fGetUniformfv(progname, location->Location(), fv);
-        if (unitSize == 1) {
-            return JS::DoubleValue(fv[0]);
-        } else {
-            JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
-            if (!obj) {
-                ErrorOutOfMemory("getUniform: out of memory");
-                return JS::NullValue();
-            }
-            return JS::ObjectOrNullValue(obj);
-        }
-    } else if (baseType == LOCAL_GL_INT) {
-        GLint iv[16] = { 0 };
-        gl->fGetUniformiv(progname, location->Location(), iv);
-        if (unitSize == 1) {
-            return JS::Int32Value(iv[0]);
-        } else {
-            JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
-            if (!obj) {
-                ErrorOutOfMemory("getUniform: out of memory");
-                return JS::NullValue();
-            }
-            return JS::ObjectOrNullValue(obj);
-        }
-    } else if (baseType == LOCAL_GL_BOOL) {
-        GLint iv[16] = { 0 };
-        gl->fGetUniformiv(progname, location->Location(), iv);
-        if (unitSize == 1) {
-            return JS::BooleanValue(iv[0] ? true : false);
-        } else {
-            bool uv[16];
-            for (int k = 0; k < unitSize; k++)
-                uv[k] = iv[k];
-            JS::Rooted<JS::Value> val(cx);
-            // Be careful: we don't want to convert all of |uv|!
-            if (!ToJSValue(cx, uv, unitSize, &val)) {
-                ErrorOutOfMemory("getUniform: out of memory");
-                return JS::NullValue();
-            }
-            return val;
-        }
-    }
-
-    // Else preserving behavior, but I'm not sure this is correct per spec
-    return JS::UndefinedValue();
+
+    return loc->GetUniform(js, this);
 }
 
 already_AddRefed<WebGLUniformLocation>
 WebGLContext::GetUniformLocation(WebGLProgram* prog, const nsAString& name)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getUniformLocation: program", prog))
         return nullptr;
 
-    if (!ValidateGLSLVariableName(name, "getUniformLocation"))
-        return nullptr;
-
-    NS_LossyConvertUTF16toASCII cname(name);
-    nsCString mappedName;
-    prog->MapIdentifier(cname, &mappedName);
-
-    GLuint progname = prog->GLName();
-    MakeContextCurrent();
-    GLint intlocation = gl->fGetUniformLocation(progname, mappedName.get());
-
-    nsRefPtr<WebGLUniformLocation> loc;
-    if (intlocation >= 0) {
-        WebGLUniformInfo info = prog->GetUniformInfoForMappedIdentifier(mappedName);
-        loc = new WebGLUniformLocation(this,
-                                       prog,
-                                       intlocation,
-                                       info);
-    }
-    return loc.forget();
+    return prog->GetUniformLocation(name);
 }
 
 void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
     if (IsContextLost())
         return;
 
@@ -2086,175 +1746,37 @@ WebGLContext::IsTexture(WebGLTexture* te
     if (IsContextLost())
         return false;
 
     return ValidateObjectAllowDeleted("isTexture", tex) &&
         !tex->IsDeleted() &&
         tex->HasEverBeenBound();
 }
 
-// Try to bind an attribute that is an array to location 0:
-bool
-WebGLContext::BindArrayAttribToLocation0(WebGLProgram* program)
-{
-    if (mBoundVertexArray->IsAttribArrayEnabled(0)) {
-        return false;
-    }
-
-    GLint leastArrayLocation = -1;
-
-    std::map<GLint, nsCString>::iterator itr;
-    for (itr = program->mActiveAttribMap.begin();
-         itr != program->mActiveAttribMap.end();
-         itr++) {
-        int32_t index = itr->first;
-        if (mBoundVertexArray->IsAttribArrayEnabled(index) &&
-            index < leastArrayLocation)
-        {
-            leastArrayLocation = index;
-        }
-    }
-
-    if (leastArrayLocation > 0) {
-        nsCString& attrName = program->mActiveAttribMap.find(leastArrayLocation)->second;
-        const char* attrNameCStr = attrName.get();
-        gl->fBindAttribLocation(program->GLName(), 0, attrNameCStr);
-        return true;
-    }
-    return false;
-}
-
-static void
-LinkAndUpdateProgram(GLContext* gl, WebGLProgram* prog)
-{
-    GLuint name = prog->GLName();
-    gl->fLinkProgram(name);
-
-    prog->SetLinkStatus(false);
-
-    GLint ok = 0;
-    gl->fGetProgramiv(name, LOCAL_GL_LINK_STATUS, &ok);
-    if (!ok)
-        return;
-
-    if (!prog->UpdateInfo())
-        return;
-
-    prog->SetLinkStatus(true);
-}
-
 void
-WebGLContext::LinkProgram(WebGLProgram* program)
+WebGLContext::LinkProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("linkProgram", program))
-        return;
-
-    InvalidateBufferFetching(); // we do it early in this function
-    // as some of the validation below changes program state
-
-    if (!program->NextGeneration()) {
-        // XXX throw?
-        return;
-    }
-
-    if (!program->HasBothShaderTypesAttached()) {
-        GenerateWarning("linkProgram: this program doesn't have both a vertex"
-                        " shader and a fragment shader");
-        program->SetLinkStatus(false);
-        return;
-    }
-
-    if (program->HasBadShaderAttached()) {
-        GenerateWarning("linkProgram: The program has bad shaders attached.");
-        program->SetLinkStatus(false);
+    if (!ValidateObject("linkProgram", prog))
         return;
-    }
-
-    // bug 777028
-    // Mesa can't handle more than 16 samplers per program, counting each array entry.
-    if (gl->WorkAroundDriverBugs() &&
-        mIsMesa &&
-        program->UpperBoundNumSamplerUniforms() > 16)
-    {
-        GenerateWarning("Programs with more than 16 samplers are disallowed on"
-                        " Mesa drivers to avoid a Mesa crasher.");
-        program->SetLinkStatus(false);
-        return;
-    }
-
-    MakeContextCurrent();
-    LinkAndUpdateProgram(gl, program);
-
-    if (program->LinkStatus()) {
-        if (BindArrayAttribToLocation0(program)) {
-            GenerateWarning("linkProgram: Relinking program to make attrib0 an"
-                            " array.");
-            LinkAndUpdateProgram(gl, program);
+
+    prog->LinkProgram();
+
+    if (prog->IsLinked()) {
+        mActiveProgramLinkInfo = prog->LinkInfo();
+
+        if (gl->WorkAroundDriverBugs() &&
+            gl->Vendor() == gl::GLVendor::NVIDIA)
+        {
+            if (mCurrentProgram == prog)
+                gl->fUseProgram(prog->mGLName);
         }
     }
-
-    if (!program->LinkStatus()) {
-        if (ShouldGenerateWarnings()) {
-            // report shader/program infoLogs as warnings.
-            // note that shader compilation errors can be deferred to linkProgram,
-            // which is why we can't do anything in compileShader. In practice we could
-            // report in compileShader the translation errors generated by ANGLE,
-            // but it seems saner to keep a single way of obtaining shader infologs.
-
-            nsAutoCString log;
-
-            bool alreadyReportedShaderInfoLog = false;
-
-            for (size_t i = 0; i < program->AttachedShaders().Length(); i++) {
-
-                WebGLShader* shader = program->AttachedShaders()[i];
-
-                if (shader->CompileStatus())
-                    continue;
-
-                const char* shaderTypeName = nullptr;
-                if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) {
-                    shaderTypeName = "vertex";
-                } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) {
-                    shaderTypeName = "fragment";
-                } else {
-                    // should have been validated earlier
-                    MOZ_ASSERT(false);
-                    shaderTypeName = "<unknown>";
-                }
-
-                GetShaderInfoLog(shader, log);
-
-                GenerateWarning("linkProgram: a %s shader used in this program failed to "
-                                "compile, with this log:\n%s\n",
-                                shaderTypeName,
-                                log.get());
-                alreadyReportedShaderInfoLog = true;
-            }
-
-            if (!alreadyReportedShaderInfoLog) {
-                GetProgramInfoLog(program, log);
-                if (!log.IsEmpty()) {
-                    GenerateWarning("linkProgram failed, with this log:\n%s\n",
-                                    log.get());
-                }
-            }
-        }
-        return;
-    }
-
-    if (gl->WorkAroundDriverBugs() &&
-        gl->Vendor() == gl::GLVendor::NVIDIA)
-    {
-        if (program == mCurrentProgram)
-            gl->fUseProgram(program->GLName());
-    }
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
     if (IsContextLost())
         return;
 
@@ -2883,17 +2405,17 @@ WebGLContext::SurfaceFromElementResultTo
 void
 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
 {
     GLuint rawLoc;
     if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc))
         return;
 
     // Only uniform1i can take sampler settings.
-    if (!ValidateSamplerUniformSetter("Uniform1i", loc, a1))
+    if (!loc->ValidateSamplerSetter(a1, this, "uniform1i"))
         return;
 
     MakeContextCurrent();
     gl->fUniform1i(rawLoc, a1);
 }
 
 void
 WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
@@ -2986,17 +2508,17 @@ WebGLContext::Uniform1iv_base(WebGLUnifo
     GLsizei numElementsToUpload;
     if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_INT, arrayLength,
                                     "uniform1iv", &rawLoc,
                                     &numElementsToUpload))
     {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("uniform1iv", loc, data[0]))
+    if (!loc->ValidateSamplerSetter(data[0], this, "uniform1iv"))
         return;
 
     MakeContextCurrent();
     gl->fUniform1iv(rawLoc, numElementsToUpload, data);
 }
 
 void
 WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
@@ -3006,18 +2528,18 @@ WebGLContext::Uniform2iv_base(WebGLUnifo
     GLsizei numElementsToUpload;
     if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_INT, arrayLength,
                                     "uniform2iv", &rawLoc,
                                     &numElementsToUpload))
     {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("uniform2iv", loc, data[0]) ||
-        !ValidateSamplerUniformSetter("uniform2iv", loc, data[1]))
+    if (!loc->ValidateSamplerSetter(data[0], this, "uniform2iv") ||
+        !loc->ValidateSamplerSetter(data[1], this, "uniform2iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform2iv(rawLoc, numElementsToUpload, data);
 }
 
@@ -3029,19 +2551,19 @@ WebGLContext::Uniform3iv_base(WebGLUnifo
     GLsizei numElementsToUpload;
     if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_INT, arrayLength,
                                     "uniform3iv", &rawLoc,
                                     &numElementsToUpload))
     {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("uniform3iv", loc, data[0]) ||
-        !ValidateSamplerUniformSetter("uniform3iv", loc, data[1]) ||
-        !ValidateSamplerUniformSetter("uniform3iv", loc, data[2]))
+    if (!loc->ValidateSamplerSetter(data[0], this, "uniform3iv") ||
+        !loc->ValidateSamplerSetter(data[1], this, "uniform3iv") ||
+        !loc->ValidateSamplerSetter(data[2], this, "uniform3iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform3iv(rawLoc, numElementsToUpload, data);
 }
 
@@ -3053,20 +2575,20 @@ WebGLContext::Uniform4iv_base(WebGLUnifo
     GLsizei numElementsToUpload;
     if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_INT, arrayLength,
                                     "uniform4iv", &rawLoc,
                                     &numElementsToUpload))
     {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("uniform4iv", loc, data[0]) ||
-        !ValidateSamplerUniformSetter("uniform4iv", loc, data[1]) ||
-        !ValidateSamplerUniformSetter("uniform4iv", loc, data[2]) ||
-        !ValidateSamplerUniformSetter("uniform4iv", loc, data[3]))
+    if (!loc->ValidateSamplerSetter(data[0], this, "uniform4iv") ||
+        !loc->ValidateSamplerSetter(data[1], this, "uniform4iv") ||
+        !loc->ValidateSamplerSetter(data[2], this, "uniform4iv") ||
+        !loc->ValidateSamplerSetter(data[3], this, "uniform4iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform4iv(rawLoc, numElementsToUpload, data);
 }
 
@@ -3195,54 +2717,41 @@ WebGLContext::UniformMatrix4fv_base(WebG
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 WebGLContext::UseProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowNull("useProgram", prog))
+    if (!prog) {
+        mCurrentProgram = nullptr;
+        mActiveProgramLinkInfo = nullptr;
         return;
-
-    MakeContextCurrent();
-
-    InvalidateBufferFetching();
-
-    GLuint progname = prog ? prog->GLName() : 0;
-
-    if (prog && !prog->LinkStatus())
-        return ErrorInvalidOperation("useProgram: program was not linked successfully");
-
-    gl->fUseProgram(progname);
-
-    mCurrentProgram = prog;
+    }
+
+    if (!ValidateObject("useProgram", prog))
+        return;
+
+    if (prog->UseProgram()) {
+        mCurrentProgram = prog;
+        mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
+    }
 }
 
 void
 WebGLContext::ValidateProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("validateProgram", prog))
         return;
 
-    MakeContextCurrent();
-
-#ifdef XP_MACOSX
-    // see bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed with Mac OS 10.6.7
-    if (gl->WorkAroundDriverBugs()) {
-        GenerateWarning("validateProgram: implemented as a no-operation on Mac to work around crashes");
-        return;
-    }
-#endif
-
-    GLuint progname = prog->GLName();
-    gl->fValidateProgram(progname);
+    prog->ValidateProgram();
 }
 
 already_AddRefed<WebGLFramebuffer>
 WebGLContext::CreateFramebuffer()
 {
     if (IsContextLost())
         return nullptr;
 
@@ -3285,315 +2794,17 @@ void
 WebGLContext::CompileShader(WebGLShader* shader)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("compileShader", shader))
         return;
 
-    GLuint shadername = shader->GLName();
-
-    shader->SetCompileStatus(false);
-
-    // nothing to do if the validator is disabled
-    if (!mShaderValidation)
-        return;
-
-    // nothing to do if translation was already done
-    if (!shader->NeedsTranslation())
-        return;
-
-    MakeContextCurrent();
-
-    ShShaderOutput targetShaderSourceLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT;
-
-    ShHandle compiler = 0;
-    ShBuiltInResources resources;
-
-    memset(&resources, 0, sizeof(ShBuiltInResources));
-
-    ShInitBuiltInResources(&resources);
-
-    resources.MaxVertexAttribs = mGLMaxVertexAttribs;
-    resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
-    resources.MaxVaryingVectors = mGLMaxVaryingVectors;
-    resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
-    resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
-    resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
-    resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
-    resources.MaxDrawBuffers = mGLMaxDrawBuffers;
-
-    if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
-        resources.EXT_frag_depth = 1;
-
-    if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
-        resources.OES_standard_derivatives = 1;
-
-    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
-        resources.EXT_draw_buffers = 1;
-
-    if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
-        resources.EXT_shader_texture_lod = 1;
-
-    // Tell ANGLE to allow highp in frag shaders. (unless disabled)
-    // If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
-    resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
-
-    resources.HashFunction = WebGLProgram::IdentifierHashFunction;
-
-    if (gl->WorkAroundDriverBugs()) {
-#ifdef XP_MACOSX
-        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
-            // Work around bug 890432
-            resources.MaxExpressionComplexity = 1000;
-        }
-#endif
-    }
-
-    // We're storing an actual instance of StripComments because, if we don't, the
-    // cleanSource nsAString instance will be destroyed before the reference is
-    // actually used.
-    StripComments stripComments(shader->Source());
-    const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
-    if (!ValidateGLSLString(cleanSource, "compileShader"))
-        return;
-
-    // shaderSource() already checks that the source stripped of comments is in the
-    // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
-    NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
-
-    if (gl->WorkAroundDriverBugs()) {
-        const uint32_t maxSourceLength = 0x3ffff;
-        if (sourceCString.Length() > maxSourceLength)
-            return ErrorInvalidValue("compileShader: source has more than %d characters",
-                                     maxSourceLength);
-    }
-
-    const char* s = sourceCString.get();
-
-#define WEBGL2_BYPASS_ANGLE
-#ifdef WEBGL2_BYPASS_ANGLE
-    /*
-     * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose
-     * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData
-     *
-     * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above
-     *
-     * To bypass angle, the context must be a WebGL 2 and the shader must have the
-     * following line at the very top :
-     *      #version proto-200
-     *
-     * In this case, byPassANGLE == true and here is what we do :
-     *  We create two shader source code:
-     *    - one for the driver, that enable GL_EXT_gpu_shader4
-     *    - one for the angle compilor, to get informations about vertex attributes
-     *      and uniforms
-     */
-    static const char* bypassPrefixSearch = "#version proto-200";
-    static const char* bypassANGLEPrefix[2] = {"precision mediump float;\n"
-                                               "#define gl_VertexID 0\n"
-                                               "#define gl_InstanceID 0\n",
-
-                                               "precision mediump float;\n"
-                                               "#extension GL_EXT_draw_buffers : enable\n"
-                                               "#define gl_PrimitiveID 0\n"};
-
-    const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0);
-
-    const char* angleShaderCode = s;
-    nsTArray<char> bypassANGLEShaderCode;
-    nsTArray<char> bypassDriverShaderCode;
-
-    if (bypassANGLE) {
-        const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0;
-        const char* originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch);
-        int originalShaderSize = strlen(s) - (originalShader - s);
-        int bypassShaderCodeSize = originalShaderSize + 4096 + 1;
-
-        bypassANGLEShaderCode.SetLength(bypassShaderCodeSize);
-        strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]);
-        strcat(bypassANGLEShaderCode.Elements(), originalShader);
-
-        bypassDriverShaderCode.SetLength(bypassShaderCodeSize);
-        strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n");
-        strcat(bypassDriverShaderCode.Elements(), originalShader);
-
-        angleShaderCode = bypassANGLEShaderCode.Elements();
-    }
-#endif
-
-    compiler = ShConstructCompiler(shader->ShaderType(),
-                                   SH_WEBGL_SPEC,
-                                   targetShaderSourceLanguage,
-                                   &resources);
-
-    int compileOptions = SH_VARIABLES |
-                         SH_ENFORCE_PACKING_RESTRICTIONS |
-                         SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
-                         SH_OBJECT_CODE |
-                         SH_LIMIT_CALL_STACK_DEPTH;
-
-    if (resources.MaxExpressionComplexity > 0) {
-        compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY;
-    }
-
-#ifndef XP_MACOSX
-    // We want to do this everywhere, but to do this on Mac, we need
-    // to do it only on Mac OSX > 10.6 as this causes the shader
-    // compiler in 10.6 to crash
-    compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
-#endif
-
-#ifdef XP_MACOSX
-    if (gl->WorkAroundDriverBugs()) {
-        // Work around bug 665578 and bug 769810
-        if (gl->Vendor() == gl::GLVendor::ATI) {
-            compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
-        // Work around bug 735560
-        if (gl->Vendor() == gl::GLVendor::Intel) {
-            compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
-        }
-
-        // Work around bug 636926
-        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
-            compileOptions |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
-        }
-
-        // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
-        // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
-        compileOptions |= SH_UNFOLD_SHORT_CIRCUIT;
-    }
-#endif
-
-#ifdef WEBGL2_BYPASS_ANGLE
-    if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) {
-#else
-    if (!ShCompile(compiler, &s, 1, compileOptions)) {
-#endif
-        size_t lenWithNull = 0;
-        ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &lenWithNull);
-
-        if (!lenWithNull) {
-            // Error in ShGetInfo.
-            shader->SetTranslationFailure(NS_LITERAL_CSTRING("Internal error: failed to get shader info log"));
-        } else {
-            size_t len = lenWithNull - 1;
-
-            nsAutoCString info;
-            if (len) {
-                // Don't allocate or try to write to zero length string
-                info.SetLength(len); // Allocates len+1, for the null-term.
-                ShGetInfoLog(compiler, info.BeginWriting());
-            }
-            shader->SetTranslationFailure(info);
-        }
-        ShDestruct(compiler);
-        shader->SetCompileStatus(false);
-        return;
-    }
-
-    size_t num_attributes = 0;
-    ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTES, &num_attributes);
-    size_t num_uniforms = 0;
-    ShGetInfo(compiler, SH_ACTIVE_UNIFORMS, &num_uniforms);
-    size_t attrib_max_length = 0;
-    ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length);
-    size_t uniform_max_length = 0;
-    ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_length);
-    size_t mapped_max_length = 0;
-    ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_max_length);
-
-    shader->mAttribMaxNameLength = attrib_max_length;
-
-    shader->mAttributes.Clear();
-    shader->mUniforms.Clear();
-    shader->mUniformInfos.Clear();
-
-    nsAutoArrayPtr<char> attribute_name(new char[attrib_max_length+1]);
-    nsAutoArrayPtr<char> uniform_name(new char[uniform_max_length+1]);
-    nsAutoArrayPtr<char> mapped_name(new char[mapped_max_length+1]);
-
-    for (size_t i = 0; i < num_uniforms; i++) {
-        size_t length;
-        int size;
-        sh::GLenum type;
-        ShPrecisionType precision;
-        int staticUse;
-        ShGetVariableInfo(compiler, SH_ACTIVE_UNIFORMS, (int)i,
-                          &length, &size, &type,
-                          &precision, &staticUse,
-                          uniform_name,
-                          mapped_name);
-
-        shader->mUniforms.AppendElement(WebGLMappedIdentifier(
-                                            nsDependentCString(uniform_name),
-                                            nsDependentCString(mapped_name)));
-
-        // we need uniform info to validate uniform setter calls
-        char mappedNameLength = strlen(mapped_name);
-        char mappedNameLastChar = mappedNameLength > 1
-                                  ? mapped_name[mappedNameLength - 1]
-                                  : 0;
-        shader->mUniformInfos.AppendElement(WebGLUniformInfo(
-                                                size,
-                                                mappedNameLastChar == ']',
-                                                type));
-    }
-
-    for (size_t i = 0; i < num_attributes; i++) {
-        size_t length;
-        int size;
-        sh::GLenum type;
-        ShPrecisionType precision;
-        int staticUse;
-        ShGetVariableInfo(compiler, SH_ACTIVE_ATTRIBUTES, (int)i,
-                          &length, &size, &type,
-                          &precision, &staticUse,
-                          attribute_name,
-                          mapped_name);
-        shader->mAttributes.AppendElement(WebGLMappedIdentifier(
-                                              nsDependentCString(attribute_name),
-                                              nsDependentCString(mapped_name)));
-    }
-
-    size_t lenWithNull = 0;
-    ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &lenWithNull);
-    MOZ_ASSERT(lenWithNull >= 1);
-    size_t len = lenWithNull - 1;
-
-    nsAutoCString translatedSrc;
-    translatedSrc.SetLength(len); // Allocates len+1, for the null-term.
-    ShGetObjectCode(compiler, translatedSrc.BeginWriting());
-
-    CopyASCIItoUTF16(translatedSrc, shader->mTranslatedSource);
-
-    const char* ts = translatedSrc.get();
-
-#ifdef WEBGL2_BYPASS_ANGLE
-    if (bypassANGLE) {
-        const char* driverShaderCode = bypassDriverShaderCode.Elements();
-        gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr);
-    } else {
-        gl->fShaderSource(shadername, 1, &ts, nullptr);
-    }
-#else
-    gl->fShaderSource(shadername, 1, &ts, nullptr);
-#endif
-
-    shader->SetTranslationSuccess();
-
-    ShDestruct(compiler);
-
-    gl->fCompileShader(shadername);
-    GLint ok;
-    gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok);
-    shader->SetCompileStatus(ok);
+    shader->CompileShader();
 }
 
 void
 WebGLContext::CompressedTexImage2D(GLenum rawTexImgTarget,
                                    GLint level,
                                    GLenum internalformat,
                                    GLsizei width, GLsizei height, GLint border,
                                    const ArrayBufferView& view)
@@ -3715,91 +2926,33 @@ JS::Value
 WebGLContext::GetShaderParameter(WebGLShader* shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
     if (!ValidateObject("getShaderParameter: shader", shader))
         return JS::NullValue();
 
-    GLuint shadername = shader->GLName();
-
-    MakeContextCurrent();
-
-    switch (pname) {
-        case LOCAL_GL_SHADER_TYPE:
-        {
-            GLint i = 0;
-            gl->fGetShaderiv(shadername, pname, &i);
-            return JS::NumberValue(uint32_t(i));
-        }
-            break;
-        case LOCAL_GL_DELETE_STATUS:
-            return JS::BooleanValue(shader->IsDeleteRequested());
-            break;
-        case LOCAL_GL_COMPILE_STATUS:
-        {
-            GLint i = 0;
-            gl->fGetShaderiv(shadername, pname, &i);
-            return JS::BooleanValue(bool(i));
-        }
-            break;
-        default:
-            ErrorInvalidEnumInfo("getShaderParameter: parameter", pname);
-    }
-
-    return JS::NullValue();
+    return shader->GetShaderParameter(pname);
 }
 
 void
 WebGLContext::GetShaderInfoLog(WebGLShader* shader, nsAString& retval)
 {
-    nsAutoCString s;
-    GetShaderInfoLog(shader, s);
-    if (s.IsVoid())
-        retval.SetIsVoid(true);
-    else
-        CopyASCIItoUTF16(s, retval);
-}
-
-void
-WebGLContext::GetShaderInfoLog(WebGLShader* shader, nsACString& retval)
-{
+    retval.SetIsVoid(true);
+
     if (IsContextLost())
-    {
-        retval.SetIsVoid(true);
         return;
-    }
 
     if (!ValidateObject("getShaderInfoLog: shader", shader))
         return;
 
-    retval = shader->TranslationLog();
-    if (!retval.IsVoid()) {
-        return;
-    }
-
-    MakeContextCurrent();
-
-    GLuint shadername = shader->GLName();
-    GLint k = -1;
-    gl->fGetShaderiv(shadername, LOCAL_GL_INFO_LOG_LENGTH, &k);
-    if (k == -1) {
-        // XXX GL Error? should never happen.
-        return;
-    }
-
-    if (k == 0) {
-        retval.Truncate();
-        return;
-    }
-
-    retval.SetCapacity(k);
-    gl->fGetShaderInfoLog(shadername, k, &k, (char*) retval.BeginWriting());
-    retval.SetLength(k);
+    shader->GetShaderInfoLog(&retval);
+
+    retval.SetIsVoid(false);
 }
 
 already_AddRefed<WebGLShaderPrecisionFormat>
 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
 {
     if (IsContextLost())
         return nullptr;
 
@@ -3843,61 +2996,51 @@ WebGLContext::GetShaderPrecisionFormat(G
     nsRefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
         = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
     return retShaderPrecisionFormat.forget();
 }
 
 void
 WebGLContext::GetShaderSource(WebGLShader* shader, nsAString& retval)
 {
-    if (IsContextLost()) {
-        retval.SetIsVoid(true);
+    retval.SetIsVoid(true);
+
+    if (IsContextLost())
         return;
-    }
 
     if (!ValidateObject("getShaderSource: shader", shader))
         return;
 
-    retval.Assign(shader->Source());
+    shader->GetShaderSource(&retval);
 }
 
 void
 WebGLContext::ShaderSource(WebGLShader* shader, const nsAString& source)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("shaderSource: shader", shader))
         return;
 
-    // We're storing an actual instance of StripComments because, if we don't, the
-    // cleanSource nsAString instance will be destroyed before the reference is
-    // actually used.
-    StripComments stripComments(source);
-    const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
-    if (!ValidateGLSLString(cleanSource, "compileShader"))
-        return;
-
-    shader->SetSource(source);
-
-    shader->SetNeedsTranslation();
+    shader->ShaderSource(source);
 }
 
 void
 WebGLContext::GetShaderTranslatedSource(WebGLShader* shader, nsAString& retval)
 {
-    if (IsContextLost()) {
-        retval.SetIsVoid(true);
+    retval.SetIsVoid(true);
+
+    if (IsContextLost())
         return;
-    }
 
     if (!ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
-    retval.Assign(shader->TranslatedSource());
+    shader->GetShaderTranslatedSource(&retval);
 }
 
 GLenum WebGLContext::CheckedTexImage2D(TexImageTarget texImageTarget,
                                        GLint level,
                                        TexInternalFormat internalformat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
@@ -4365,87 +3508,16 @@ WebGLContext::RestoreContext()
     // restoreContext().
 
     if (!mAllowContextRestore)
         return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
 
     ForceRestoreContext();
 }
 
-bool
-BaseTypeAndSizeFromUniformType(GLenum uType, GLenum* baseType, GLint* unitSize)
-{
-    switch (uType) {
-        case LOCAL_GL_INT:
-        case LOCAL_GL_INT_VEC2:
-        case LOCAL_GL_INT_VEC3:
-        case LOCAL_GL_INT_VEC4:
-        case LOCAL_GL_SAMPLER_2D:
-        case LOCAL_GL_SAMPLER_CUBE:
-            *baseType = LOCAL_GL_INT;
-            break;
-        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_MAT3:
-        case LOCAL_GL_FLOAT_MAT4:
-            *baseType = LOCAL_GL_FLOAT;
-            break;
-        case LOCAL_GL_BOOL:
-        case LOCAL_GL_BOOL_VEC2:
-        case LOCAL_GL_BOOL_VEC3:
-        case LOCAL_GL_BOOL_VEC4:
-            *baseType = LOCAL_GL_BOOL; // pretend these are int
-            break;
-        default:
-            return false;
-    }
-
-    switch (uType) {
-        case LOCAL_GL_INT:
-        case LOCAL_GL_FLOAT:
-        case LOCAL_GL_BOOL:
-        case LOCAL_GL_SAMPLER_2D:
-        case LOCAL_GL_SAMPLER_CUBE:
-            *unitSize = 1;
-            break;
-        case LOCAL_GL_INT_VEC2:
-        case LOCAL_GL_FLOAT_VEC2:
-        case LOCAL_GL_BOOL_VEC2:
-            *unitSize = 2;
-            break;
-        case LOCAL_GL_INT_VEC3:
-        case LOCAL_GL_FLOAT_VEC3:
-        case LOCAL_GL_BOOL_VEC3:
-            *unitSize = 3;
-            break;
-        case LOCAL_GL_INT_VEC4:
-        case LOCAL_GL_FLOAT_VEC4:
-        case LOCAL_GL_BOOL_VEC4:
-            *unitSize = 4;
-            break;
-        case LOCAL_GL_FLOAT_MAT2:
-            *unitSize = 4;
-            break;
-        case LOCAL_GL_FLOAT_MAT3:
-            *unitSize = 9;
-            break;
-        case LOCAL_GL_FLOAT_MAT4:
-            *unitSize = 16;
-            break;
-        default:
-            return false;
-    }
-
-    return true;
-}
-
-
 WebGLTexelFormat
 mozilla::GetWebGLTexelFormat(TexInternalFormat effectiveInternalFormat)
 {
     switch (effectiveInternalFormat.get()) {
         case LOCAL_GL_RGBA8:                  return WebGLTexelFormat::RGBA8;
         case LOCAL_GL_SRGB8_ALPHA8:           return WebGLTexelFormat::RGBA8;
         case LOCAL_GL_RGB8:                   return WebGLTexelFormat::RGB8;
         case LOCAL_GL_SRGB8:                  return WebGLTexelFormat::RGB8;
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -1038,17 +1038,17 @@ WebGLContext::AssertCachedBindings()
         AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound);
     } else {
         MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
         GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName()
                                              : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
     }
 
-    GLuint bound = mCurrentProgram ? mCurrentProgram->GLName() : 0;
+    GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound);
 
     // Textures
     GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0;
     AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture);
 
     WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D);
     bound = curTex ? curTex->GLName() : 0;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -17,16 +17,17 @@
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLUniformLocation.h"
+#include "WebGLValidateStrings.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #if defined(MOZ_WIDGET_COCOA)
 #include "nsCocoaFeatures.h"
 #endif
 
 namespace mozilla {
@@ -362,60 +363,16 @@ WebGLContext::ValidateDrawModeEnum(GLenu
         return true;
 
     default:
         ErrorInvalidEnumInfo(info, mode);
         return false;
     }
 }
 
-bool
-WebGLContext::ValidateGLSLVariableName(const nsAString& name, const char* info)
-{
-    if (name.IsEmpty())
-        return false;
-
-    const uint32_t maxSize = 256;
-    if (name.Length() > maxSize) {
-        ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the"
-                          " maximum allowed length of %d characters.", info,
-                          name.Length(), maxSize);
-        return false;
-    }
-
-    if (!ValidateGLSLString(name, info))
-        return false;
-
-    nsString prefix1 = NS_LITERAL_STRING("webgl_");
-    nsString prefix2 = NS_LITERAL_STRING("_webgl_");
-
-    if (Substring(name, 0, prefix1.Length()).Equals(prefix1) ||
-        Substring(name, 0, prefix2.Length()).Equals(prefix2))
-    {
-        ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.",
-                              info);
-        return false;
-    }
-
-    return true;
-}
-
-bool WebGLContext::ValidateGLSLString(const nsAString& string, const char* info)
-{
-    for (uint32_t i = 0; i < string.Length(); ++i) {
-        if (!ValidateGLSLCharacter(string.CharAt(i))) {
-             ErrorInvalidValue("%s: String contains the illegal character"
-                               " '%d'.", info, string.CharAt(i));
-             return false;
-        }
-    }
-
-    return true;
-}
-
 /**
  * Return true if the framebuffer attachment is valid. Attachment must
  * be one of depth/stencil/depth_stencil/color attachment.
  */
 bool
 WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
                                             const char* funcName)
 {
@@ -1507,261 +1464,129 @@ WebGLContext::ValidateTexImage(TexImageT
         return false;
     }
 
     // Parameters are OK
     return true;
 }
 
 bool
-WebGLContext::ValidateUniformLocation(const char* info,
-                                      WebGLUniformLocation* loc)
+WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
 {
-    if (!ValidateObjectAllowNull(info, loc))
-        return false;
-
+    /* GLES 2.0.25, p38:
+     *   If the value of location is -1, the Uniform* commands will silently
+     *   ignore the data passed in, and the current uniform values will not be
+     *   changed.
+     */
     if (!loc)
         return false;
 
-    // The need to check specifically for !mCurrentProgram here is explained in
-    // bug 657556.
-    if (!mCurrentProgram) {
-        ErrorInvalidOperation("%s: No program is currently bound.", info);
+    if (!ValidateObject(funcName, loc))
         return false;
-    }
 
-    if (mCurrentProgram != loc->Program()) {
-        ErrorInvalidOperation("%s: This uniform location doesn't correspond to"
-                              " the current program.", info);
+    if (!mCurrentProgram) {
+        ErrorInvalidOperation("%s: No program is currently bound.", funcName);
         return false;
     }
 
-    if (mCurrentProgram->Generation() != loc->ProgramGeneration()) {
-        ErrorInvalidOperation("%s: This uniform location is obsolete since the"
-                              " program has been relinked.", info);
-        return false;
-    }
-
-    return true;
+    return loc->ValidateForProgram(mCurrentProgram, this, funcName);
 }
 
 bool
-WebGLContext::ValidateSamplerUniformSetter(const char* info,
-                                           WebGLUniformLocation* loc,
-                                           GLint value)
-{
-    if (loc->Info().type != LOCAL_GL_SAMPLER_2D &&
-        loc->Info().type != LOCAL_GL_SAMPLER_CUBE)
-    {
-        return true;
-    }
-
-    if (value >= 0 && value < mGLMaxTextureUnits)
-        return true;
-
-    ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a"
-                      " valid texture unit.", info, value);
-    return false;
-}
-
-bool
-WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t cnt,
+WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize,
                                         uint32_t arrayLength)
 {
     if (IsContextLost())
         return false;
 
-    if (arrayLength < cnt) {
-        ErrorInvalidOperation("%s: Array must be >= %d elements.", name, cnt);
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
-{
-    switch (uniformType) {
-    case LOCAL_GL_BOOL:
-    case LOCAL_GL_BOOL_VEC2:
-    case LOCAL_GL_BOOL_VEC3:
-    case LOCAL_GL_BOOL_VEC4:
-        return true; // GLfloat(0.0) sets a bool to false.
-
-    case LOCAL_GL_INT:
-    case LOCAL_GL_INT_SAMPLER_2D:
-    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
-    case LOCAL_GL_INT_SAMPLER_3D:
-    case LOCAL_GL_INT_SAMPLER_CUBE:
-    case LOCAL_GL_INT_VEC2:
-    case LOCAL_GL_INT_VEC3:
-    case LOCAL_GL_INT_VEC4:
-    case LOCAL_GL_SAMPLER_2D:
-    case LOCAL_GL_SAMPLER_2D_ARRAY:
-    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
-    case LOCAL_GL_SAMPLER_2D_SHADOW:
-    case LOCAL_GL_SAMPLER_CUBE:
-    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
-        return setterType == LOCAL_GL_INT;
-
-    case LOCAL_GL_FLOAT:
-    case LOCAL_GL_FLOAT_MAT2:
-    case LOCAL_GL_FLOAT_MAT2x3:
-    case LOCAL_GL_FLOAT_MAT2x4:
-    case LOCAL_GL_FLOAT_MAT3:
-    case LOCAL_GL_FLOAT_MAT3x2:
-    case LOCAL_GL_FLOAT_MAT3x4:
-    case LOCAL_GL_FLOAT_MAT4:
-    case LOCAL_GL_FLOAT_MAT4x2:
-    case LOCAL_GL_FLOAT_MAT4x3:
-    case LOCAL_GL_FLOAT_VEC2:
-    case LOCAL_GL_FLOAT_VEC3:
-    case LOCAL_GL_FLOAT_VEC4:
-        return setterType == LOCAL_GL_FLOAT;
-
-    default:
-        MOZ_ASSERT(false); // should never get here
-        return false;
-    }
-}
-
-static bool
-CheckUniformSizeAndType(WebGLContext& webgl, WebGLUniformLocation* loc,
-                        uint8_t setterElemSize, GLenum setterType,
-                        const char* info)
-{
-    if (setterElemSize != loc->ElementSize()) {
-        webgl.ErrorInvalidOperation("%s: Bad uniform size: %i", info,
-                                    loc->ElementSize());
-        return false;
-    }
-
-    if (!IsUniformSetterTypeValid(setterType, loc->Info().type)) {
-        webgl.ErrorInvalidOperation("%s: Bad uniform type: %i", info,
-                                    loc->Info().type);
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc,
-                        uint8_t setterElemSize, size_t setterArraySize,
-                        const char* info)
-{
-    if (setterArraySize == 0 ||
-        setterArraySize % setterElemSize)
-    {
-        webgl.ErrorInvalidValue("%s: expected an array of length a multiple of"
-                                " %d, got an array of length %d.", info,
-                                setterElemSize, setterArraySize);
-        return false;
-    }
-
-    if (!loc->Info().isArray &&
-        setterArraySize != setterElemSize)
-    {
-        webgl.ErrorInvalidOperation("%s: expected an array of length exactly %d"
-                                    " (since this uniform is not an array"
-                                    " uniform), got an array of length %d.",
-                                    info, setterElemSize, setterArraySize);
+    if (arrayLength < setterElemSize) {
+        ErrorInvalidOperation("%s: Array must have >= %d elements.", name,
+                              setterElemSize);
         return false;
     }
 
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
-                                    const char* info, GLuint* out_rawLoc)
+                                    const char* funcName, GLuint* out_rawLoc)
 {
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(info, loc))
+    if (!ValidateUniformLocation(loc, funcName))
         return false;
 
-    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
         return false;
 
-    *out_rawLoc = loc->Location();
+    *out_rawLoc = loc->mLoc;
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                          uint8_t setterElemSize,
                                          GLenum setterType,
                                          size_t setterArraySize,
-                                         const char* info,
+                                         const char* funcName,
                                          GLuint* const out_rawLoc,
                                          GLsizei* const out_numElementsToUpload)
 {
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(info, loc))
+    if (!ValidateUniformLocation(loc, funcName))
         return false;
 
-    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
         return false;
 
-    if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
-                                 info))
-    {
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
         return false;
-    }
 
-    *out_rawLoc = loc->Location();
-    *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
+    *out_rawLoc = loc->mLoc;
+    *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
                                         setterArraySize / setterElemSize);
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                                uint8_t setterDims,
                                                GLenum setterType,
                                                size_t setterArraySize,
                                                bool setterTranspose,
-                                               const char* info,
+                                               const char* funcName,
                                                GLuint* const out_rawLoc,
                                                GLsizei* const out_numElementsToUpload)
 {
     uint8_t setterElemSize = setterDims * setterDims;
 
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(info, loc))
+    if (!ValidateUniformLocation(loc, funcName))
+        return false;
+
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
         return false;
 
-    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
         return false;
 
-    if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
-                                 info))
-    {
+    if (setterTranspose) {
+        ErrorInvalidValue("%s: `transpose` must be false.", funcName);
         return false;
     }
 
-    if (setterTranspose) {
-        ErrorInvalidValue("%s: `transpose` must be false.", info);
-        return false;
-    }
-
-    *out_rawLoc = loc->Location();
-    *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
+    *out_rawLoc = loc->mLoc;
+    *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
                                         setterArraySize / setterElemSize);
     return true;
 }
 
 bool
 WebGLContext::ValidateAttribIndex(GLuint index, const char* info)
 {
     bool valid = (index < MaxVertexAttribs());
@@ -2090,25 +1915,23 @@ WebGLContext::InitAndValidateGL()
         gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
                              LOCAL_GL_LOWER_LEFT);
     }
 #endif
 
     // Check the shader validator pref
     NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
 
-    mShaderValidation = Preferences::GetBool("webgl.shader_validator",
-                                             mShaderValidation);
+    mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
+                                                   mBypassShaderValidation);
 
     // initialize shader translator
-    if (mShaderValidation) {
-        if (!ShInitialize()) {
-            GenerateWarning("GLSL translator initialization failed!");
-            return false;
-        }
+    if (!ShInitialize()) {
+        GenerateWarning("GLSL translator initialization failed!");
+        return false;
     }
 
     // Mesa can only be detected with the GL_VERSION string, of the form
     // "2.1 Mesa 7.11.0"
     const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
     mIsMesa = strstr(versionStr, "Mesa");
 
     // Notice that the point of calling fGetError here is not only to check for
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -8,17 +8,16 @@
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "WebGLBuffer.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
-#include "WebGLUniformInfo.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 using namespace mozilla;
 using namespace dom;
 
 void
 WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -2,371 +2,697 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLProgram.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
-#include "MurmurHash3.h"
 #include "WebGLContext.h"
 #include "WebGLShader.h"
+#include "WebGLUniformLocation.h"
+#include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
-/** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]"
-  * in bracketPart.
-  *
-  * \param string input/output: The string to split, becomes the string without
-  *                             the bracket part.
-  * \param bracketPart output: Gets the bracket part.
-  *
-  * Notice that if there are multiple brackets like "foo[i].bar[j]", only the
-  * last bracket is split.
-  */
+/* If `name`: "foo[3]"
+ * Then returns true, with
+ *     `out_baseName`: "foo"
+ *     `out_isArray`: true
+ *     `out_index`: 3
+ *
+ * If `name`: "foo"
+ * Then returns true, with
+ *     `out_baseName`: "foo"
+ *     `out_isArray`: false
+ *     `out_index`: <not written>
+ */
 static bool
-SplitLastSquareBracket(nsACString& string, nsCString& bracketPart)
+ParseName(const nsCString& name, nsCString* const out_baseName,
+          bool* const out_isArray, size_t* const out_arrayIndex)
 {
-    MOZ_ASSERT(bracketPart.IsEmpty(),
-               "SplitLastSquareBracket must be called with empty bracketPart"
-               " string.");
+    int32_t indexEnd = name.RFind("]");
+    if (indexEnd == -1 ||
+        (uint32_t)indexEnd != name.Length() - 1)
+    {
+        *out_baseName = name;
+        *out_isArray = false;
+        return true;
+    }
 
-    if (string.IsEmpty())
+    int32_t indexOpenBracket = name.RFind("[");
+    if (indexOpenBracket == -1)
         return false;
 
-    char* string_start = string.BeginWriting();
-    char* s = string_start + string.Length() - 1;
-
-    if (*s != ']')
+    uint32_t indexStart = indexOpenBracket + 1;
+    uint32_t indexLen = indexEnd - indexStart;
+    if (indexLen == 0)
         return false;
 
-    while (*s != '[' && s != string_start)
-        s--;
+    const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
 
-    if (*s != '[')
+    nsresult errorcode;
+    int32_t indexNum = indexStr.ToInteger(&errorcode);
+    if (NS_FAILED(errorcode))
         return false;
 
-    bracketPart.Assign(s);
-    *s = 0;
-    string.EndWriting();
-    string.SetLength(s - string_start);
+    if (indexNum < 0)
+        return false;
+
+    *out_baseName = StringHead(name, indexOpenBracket);
+    *out_isArray = true;
+    *out_arrayIndex = indexNum;
     return true;
 }
 
-JSObject*
-WebGLProgram::WrapObject(JSContext* cx) {
-    return dom::WebGLProgramBinding::Wrap(cx, this);
+static void
+AddActiveInfo(GLint elemCount, GLenum elemType, bool isArray,
+              const nsACString& baseUserName, const nsACString& baseMappedName,
+              std::vector<nsRefPtr<WebGLActiveInfo>>* activeInfoList,
+              std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
+{
+    nsRefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(elemCount, elemType, isArray,
+                                                         baseUserName, baseMappedName);
+    activeInfoList->push_back(info);
+
+    infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
+}
+
+//#define DUMP_SHADERVAR_MAPPINGS
+
+static TemporaryRef<const webgl::LinkedProgramInfo>
+QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
+{
+    RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
+
+    GLuint maxAttribLenWithNull = 0;
+    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
+                      (GLint*)&maxAttribLenWithNull);
+    if (maxAttribLenWithNull < 1)
+        maxAttribLenWithNull = 1;
+
+    GLuint maxUniformLenWithNull = 0;
+    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
+                      (GLint*)&maxUniformLenWithNull);
+    if (maxUniformLenWithNull < 1)
+        maxUniformLenWithNull = 1;
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+    printf_stderr("maxAttribLenWithNull: %d\n", maxAttribLenWithNull);
+    printf_stderr("maxUniformLenWithNull: %d\n", maxUniformLenWithNull);
+#endif
+
+    // Attribs
+
+    GLuint numActiveAttribs = 0;
+    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
+                      (GLint*)&numActiveAttribs);
+
+    for (GLuint i = 0; i < numActiveAttribs; i++) {
+        nsAutoCString mappedName;
+        mappedName.SetLength(maxAttribLenWithNull - 1);
+
+        GLsizei lengthWithoutNull = 0;
+        GLint elemCount = 0; // `size`
+        GLenum elemType = 0; // `type`
+        gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
+                             &elemCount, &elemType, mappedName.BeginWriting());
+
+        mappedName.SetLength(lengthWithoutNull);
+
+        // Collect ActiveInfos:
+
+        // Attribs can't be arrays, so we can skip some of the mess we have in the Uniform
+        // path.
+        nsDependentCString userName;
+        if (!prog->FindAttribUserNameByMappedName(mappedName, &userName))
+            userName.Rebind(mappedName, 0);
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+        printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(),
+                      userName.BeginReading());
+        printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
+#endif
+
+        const bool isArray = false;
+        AddActiveInfo(elemCount, elemType, isArray, userName, mappedName,
+                      &info->activeAttribs, &info->attribMap);
+
+        // Collect active locations:
+        GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
+        if (loc == -1)
+            MOZ_CRASH("Active attrib has no location.");
+
+        info->activeAttribLocs.insert(loc);
+    }
+
+    // Uniforms
+
+    const bool needsCheckForArrays = true;
+
+    GLuint numActiveUniforms = 0;
+    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
+                      (GLint*)&numActiveUniforms);
+
+    for (GLuint i = 0; i < numActiveUniforms; i++) {
+        nsAutoCString mappedName;
+        mappedName.SetLength(maxUniformLenWithNull - 1);
+
+        GLsizei lengthWithoutNull = 0;
+        GLint elemCount = 0; // `size`
+        GLenum elemType = 0; // `type`
+        gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
+                              &elemCount, &elemType, mappedName.BeginWriting());
+
+        mappedName.SetLength(lengthWithoutNull);
+
+        nsAutoCString baseMappedName;
+        bool isArray;
+        size_t arrayIndex;
+        if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
+            MOZ_CRASH("Failed to parse `mappedName` received from driver.");
+
+        // Note that for good drivers, `isArray` should already be correct.
+        // However, if FindUniform succeeds, it will be validator-guaranteed correct.
+
+        nsAutoCString baseUserName;
+        if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
+            baseUserName = baseMappedName;
+
+            if (needsCheckForArrays && !isArray) {
+                // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
+                // not an array. Our current linux Try slaves return the location of `foo`
+                // anyways, though.
+                std::string mappedName = baseMappedName.BeginReading();
+                mappedName += "[0]";
+
+                GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedName.c_str());
+                if (loc != -1)
+                    isArray = true;
+            }
+        }
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+        printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
+                      (int)isArray, baseMappedName.BeginReading(),
+                      baseUserName.BeginReading());
+        printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
+        printf_stderr("    isArray: %d\n", (int)isArray);
+#endif
+
+        AddActiveInfo(elemCount, elemType, isArray, baseUserName, baseMappedName,
+                      &info->activeUniforms, &info->uniformMap);
+    }
+
+    return info.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg)
+    : prog(aProg)
+{ }
+
+////////////////////////////////////////////////////////////////////////////////
+// WebGLProgram
+
+static GLuint
+CreateProgram(gl::GLContext* gl)
+{
+    gl->MakeCurrent();
+    return gl->fCreateProgram();
 }
 
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
-    , mLinkStatus(false)
-    , mGeneration(0)
-    , mIdentifierMap(new CStringMap)
-    , mIdentifierReverseMap(new CStringMap)
-    , mUniformInfoMap(new CStringToUniformInfoMap)
-    , mAttribMaxNameLength(0)
+    , mGLName(CreateProgram(webgl->GL()))
 {
-    mContext->MakeContextCurrent();
-    mGLName = mContext->gl->fCreateProgram();
     mContext->mPrograms.insertBack(this);
 }
 
 void
 WebGLProgram::Delete()
 {
-    DetachShaders();
+    gl::GLContext* gl = mContext->GL();
+
+    gl->MakeCurrent();
+    gl->fDeleteProgram(mGLName);
+
+    mVertShader = nullptr;
+    mFragShader = nullptr;
+
+    mMostRecentLinkInfo = nullptr;
+
+    LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GL funcs
+
+void
+WebGLProgram::AttachShader(WebGLShader* shader)
+{
+    WebGLRefPtr<WebGLShader>* shaderSlot;
+    switch (shader->mType) {
+    case LOCAL_GL_VERTEX_SHADER:
+        shaderSlot = &mVertShader;
+        break;
+    case LOCAL_GL_FRAGMENT_SHADER:
+        shaderSlot = &mFragShader;
+        break;
+    default:
+        mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
+        return;
+    }
+
+    if (*shaderSlot) {
+        if (shader == *shaderSlot) {
+            mContext->ErrorInvalidOperation("attachShader: `shader` is already attached.");
+        } else {
+            mContext->ErrorInvalidOperation("attachShader: Only one of each type of"
+                                            " shader may be attached to a program.");
+        }
+        return;
+    }
+
+    *shaderSlot = shader;
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fAttachShader(mGLName, shader->mGLName);
+}
+
+void
+WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
+{
+    if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation"))
+        return;
+
+    if (loc >= mContext->MaxVertexAttribs()) {
+        mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than"
+                                    " MAX_VERTEX_ATTRIBS.");
+        return;
+    }
+
+    if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
+        mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the  location of a"
+                                        " name that starts with 'gl_'.");
+        return;
+    }
+
+    NS_LossyConvertUTF16toASCII asciiName(name);
+
+    auto res = mBoundAttribLocs.insert(std::pair<nsCString, GLuint>(asciiName, loc));
+
+    const bool wasInserted = res.second;
+    if (!wasInserted) {
+        auto itr = res.first;
+        itr->second = loc;
+    }
+}
+
+void
+WebGLProgram::DetachShader(WebGLShader* shader)
+{
+    MOZ_ASSERT(shader);
+
+    WebGLRefPtr<WebGLShader>* shaderSlot;
+    switch (shader->mType) {
+    case LOCAL_GL_VERTEX_SHADER:
+        shaderSlot = &mVertShader;
+        break;
+    case LOCAL_GL_FRAGMENT_SHADER:
+        shaderSlot = &mFragShader;
+        break;
+    default:
+        mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
+        return;
+    }
+
+    if (*shaderSlot != shader) {
+        mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
+        return;
+    }
+
+    *shaderSlot = nullptr;
+
     mContext->MakeContextCurrent();
-    mContext->gl->fDeleteProgram(mGLName);
-    LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
+    mContext->gl->fDetachShader(mGLName, shader->mGLName);
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetActiveAttrib(GLuint index) const
+{
+    if (!mMostRecentLinkInfo) {
+        nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid();
+        return ret.forget();
+    }
+
+    const auto& activeList = mMostRecentLinkInfo->activeAttribs;
+
+    if (index >= activeList.size()) {
+        mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
+                                    index, "ACTIVE_ATTRIBS", activeList.size());
+        return nullptr;
+    }
+
+    nsRefPtr<WebGLActiveInfo> ret =  activeList[index];
+    return ret.forget();
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetActiveUniform(GLuint index) const
+{
+    if (!mMostRecentLinkInfo) {
+        nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid();
+        return ret.forget();
+    }
+
+    const auto& activeList = mMostRecentLinkInfo->activeUniforms;
+
+    if (index >= activeList.size()) {
+        mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
+                                    index, "ACTIVE_UNIFORMS", activeList.size());
+        return nullptr;
+    }
+
+    nsRefPtr<WebGLActiveInfo> ret = activeList[index];
+    return ret.forget();
+}
+
+void
+WebGLProgram::GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const
+{
+    out->TruncateLength(0);
+
+    if (mVertShader)
+        out->AppendElement(mVertShader);
+
+    if (mFragShader)
+        out->AppendElement(mFragShader);
+}
+
+GLint
+WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const
+{
+    if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation"))
+        return -1;
+
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
+        return -1;
+    }
+
+    const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+    const WebGLActiveInfo* info;
+    if (!LinkInfo()->FindAttrib(userName, &info))
+        return -1;
+
+    const nsCString& mappedName = info->mBaseMappedName;
+
+    gl::GLContext* gl = mContext->GL();
+    gl->MakeCurrent();
+
+    return gl->fGetAttribLocation(mGLName, mappedName.BeginReading());
+}
+
+void
+WebGLProgram::GetProgramInfoLog(nsAString* const out) const
+{
+    CopyASCIItoUTF16(mLinkLog, *out);
+}
+
+static GLint
+GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
+{
+    GLint ret = 0;
+    gl->fGetProgramiv(program, pname, &ret);
+    return ret;
+}
+
+JS::Value
+WebGLProgram::GetProgramParameter(GLenum pname) const
+{
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
+
+    if (mContext->IsWebGL2()) {
+        switch (pname) {
+        case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
+            return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
+        }
+    }
+
+
+    switch (pname) {
+    case LOCAL_GL_ATTACHED_SHADERS:
+    case LOCAL_GL_ACTIVE_UNIFORMS:
+    case LOCAL_GL_ACTIVE_ATTRIBUTES:
+        return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
+
+    case LOCAL_GL_DELETE_STATUS:
+        return JS::BooleanValue(IsDeleteRequested());
+
+    case LOCAL_GL_LINK_STATUS:
+        return JS::BooleanValue(IsLinked());
+
+    case LOCAL_GL_VALIDATE_STATUS:
+#ifdef XP_MACOSX
+        // See comment in ValidateProgram.
+        if (gl->WorkAroundDriverBugs())
+            return JS::BooleanValue(true);
+#endif
+        return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
+
+    default:
+        mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
+                                       pname);
+        return JS::NullValue();
+    }
+}
+
+already_AddRefed<WebGLUniformLocation>
+WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
+{
+    if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
+        return nullptr;
+
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
+        return nullptr;
+    }
+
+    const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+    nsDependentCString baseUserName;
+    bool isArray;
+    size_t arrayIndex;
+    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+        return nullptr;
+
+    const WebGLActiveInfo* activeInfo;
+    if (!LinkInfo()->FindUniform(baseUserName, &activeInfo))
+        return nullptr;
+
+    const nsCString& baseMappedName = activeInfo->mBaseMappedName;
+
+    nsAutoCString mappedName(baseMappedName);
+    if (isArray) {
+        mappedName.AppendLiteral("[");
+        mappedName.AppendInt(uint32_t(arrayIndex));
+        mappedName.AppendLiteral("]");
+    }
+
+    gl::GLContext* gl = mContext->GL();
+    gl->MakeCurrent();
+
+    GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
+    if (loc == -1)
+        return nullptr;
+
+    nsRefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
+                                                                     loc, activeInfo);
+    return locObj.forget();
 }
 
 bool
-WebGLProgram::AttachShader(WebGLShader* shader)
-{
-    if (ContainsShader(shader))
-        return false;
-
-    mAttachedShaders.AppendElement(shader);
-
-    mContext->MakeContextCurrent();
-    mContext->gl->fAttachShader(GLName(), shader->GLName());
-
-    return true;
-}
-
-bool
-WebGLProgram::DetachShader(WebGLShader* shader)
+WebGLProgram::LinkProgram()
 {
-    if (!mAttachedShaders.RemoveElement(shader))
+    mContext->InvalidateBufferFetching(); // we do it early in this function
+    // as some of the validation below changes program state
+
+    mLinkLog.Truncate();
+    mMostRecentLinkInfo = nullptr;
+
+    if (!mVertShader || !mVertShader->IsCompiled()) {
+        mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
+        mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
         return false;
+    }
 
-    mContext->MakeContextCurrent();
-    mContext->gl->fDetachShader(GLName(), shader->GLName());
+    if (!mFragShader || !mFragShader->IsCompiled()) {
+        mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
+        mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
+        return false;
+    }
+
+    if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) {
+        mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
+        return false;
+    }
+
+    gl::GLContext* gl = mContext->gl;
+    gl->MakeCurrent();
 
-    return true;
-}
+    // Bug 777028: Mesa can't handle more than 16 samplers per program,
+    // counting each array entry.
+    size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
+                                           mFragShader->CalcNumSamplerUniforms();
+    if (gl->WorkAroundDriverBugs() &&
+        mContext->mIsMesa &&
+        numSamplerUniforms_upperBound > 16)
+    {
+        mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
+                               " Mesa drivers to avoid crashing.");
+        mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
+        return false;
+    }
 
-bool
-WebGLProgram::HasAttachedShaderOfType(GLenum shaderType)
-{
-    for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
-        if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType)
-            return true;
+    // Bind the attrib locations.
+    // This can't be done trivially, because we have to deal with mapped attrib names.
+    for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) {
+        const nsCString& name = itr->first;
+        GLuint index = itr->second;
+
+        mVertShader->BindAttribLocation(mGLName, name, index);
+    }
+
+    if (LinkAndUpdate())
+        return true;
+
+    // Failed link.
+    if (mContext->ShouldGenerateWarnings()) {
+        // report shader/program infoLogs as warnings.
+        // note that shader compilation errors can be deferred to linkProgram,
+        // which is why we can't do anything in compileShader. In practice we could
+        // report in compileShader the translation errors generated by ANGLE,
+        // but it seems saner to keep a single way of obtaining shader infologs.
+        if (!mLinkLog.IsEmpty()) {
+            mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
+                                      " log:\n%s\n",
+                                      mLinkLog.BeginReading());
+        }
     }
 
     return false;
 }
 
 bool
-WebGLProgram::HasBadShaderAttached()
+WebGLProgram::UseProgram() const
+{
+    if (!mMostRecentLinkInfo) {
+        mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
+                                        " linked.");
+        return false;
+    }
+
+    mContext->MakeContextCurrent();
+
+    mContext->InvalidateBufferFetching();
+
+    mContext->gl->fUseProgram(mGLName);
+    return true;
+}
+
+void
+WebGLProgram::ValidateProgram() const
 {
-    for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
-        if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus())
-            return true;
+    mContext->MakeContextCurrent();
+    gl::GLContext* gl = mContext->gl;
+
+#ifdef XP_MACOSX
+    // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
+    // with Mac OS 10.6.7.
+    if (gl->WorkAroundDriverBugs()) {
+        mContext->GenerateWarning("validateProgram: Implemented as a no-op on"
+                                  " Mac to work around crashes.");
+        return;
     }
+#endif
+
+    gl->fValidateProgram(mGLName);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool
+WebGLProgram::LinkAndUpdate()
+{
+    mMostRecentLinkInfo = nullptr;
+
+    gl::GLContext* gl = mContext->gl;
+    gl->fLinkProgram(mGLName);
+
+    // Grab the program log.
+    GLuint logLenWithNull = 0;
+    gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
+    if (logLenWithNull > 1) {
+        mLinkLog.SetLength(logLenWithNull - 1);
+        gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
+    } else {
+        mLinkLog.SetLength(0);
+    }
+
+    GLint ok = 0;
+    gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
+    if (!ok)
+        return false;
+
+    mMostRecentLinkInfo = QueryProgramInfo(this, gl);
+
+    MOZ_ASSERT(mMostRecentLinkInfo);
+    if (!mMostRecentLinkInfo)
+        mLinkLog.AssignLiteral("Failed to gather program info.");
+
+    return mMostRecentLinkInfo;
+}
+
+bool
+WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
+                                             nsDependentCString* const out_userName) const
+{
+    if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
+        return true;
 
     return false;
 }
 
-size_t
-WebGLProgram::UpperBoundNumSamplerUniforms()
+bool
+WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
+                                      nsCString* const out_userName,
+                                      bool* const out_isArray) const
 {
-    size_t numSamplerUniforms = 0;
-
-    for (size_t i = 0; i < mAttachedShaders.Length(); ++i) {
-        const WebGLShader* shader = mAttachedShaders[i];
-        if (!shader)
-            continue;
-
-        for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) {
-            WebGLUniformInfo u = shader->mUniformInfos[j];
-            if (u.type == LOCAL_GL_SAMPLER_2D ||
-                u.type == LOCAL_GL_SAMPLER_CUBE)
-            {
-                numSamplerUniforms += u.arraySize;
-            }
-        }
-    }
-
-    return numSamplerUniforms;
-}
-
-void
-WebGLProgram::MapIdentifier(const nsACString& name,
-                            nsCString* const out_mappedName)
-{
-    MOZ_ASSERT(mIdentifierMap);
-
-    nsCString mutableName(name);
-    nsCString bracketPart;
-    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-    if (hadBracketPart)
-        mutableName.AppendLiteral("[0]");
-
-    if (mIdentifierMap->Get(mutableName, out_mappedName)) {
-        if (hadBracketPart) {
-            nsCString mappedBracketPart;
-            bool mappedHadBracketPart = SplitLastSquareBracket(*out_mappedName,
-                                                               mappedBracketPart);
-            if (mappedHadBracketPart)
-                out_mappedName->Append(bracketPart);
-        }
-        return;
-    }
-
-    // Not found? We might be in the situation we have a uniform array name and
-    // the GL's glGetActiveUniform returned its name without [0], as is allowed
-    // by desktop GL but not in ES. Let's then try with [0].
-    mutableName.AppendLiteral("[0]");
-    if (mIdentifierMap->Get(mutableName, out_mappedName))
-        return;
+    if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
+        return true;
 
-    /* Not found? Return name unchanged. This case happens e.g. on bad user
-     * input, or when we're not using identifier mapping, or if we didn't store
-     * an identifier in the map because e.g. its mapping is trivial. (as happens
-     * for short identifiers)
-     */
-    out_mappedName->Assign(name);
-}
-
-void
-WebGLProgram::ReverseMapIdentifier(const nsACString& name,
-                                   nsCString* const out_reverseMappedName)
-{
-    MOZ_ASSERT(mIdentifierReverseMap);
-
-    nsCString mutableName(name);
-    nsCString bracketPart;
-    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-    if (hadBracketPart)
-        mutableName.AppendLiteral("[0]");
-
-    if (mIdentifierReverseMap->Get(mutableName, out_reverseMappedName)) {
-        if (hadBracketPart) {
-            nsCString reverseMappedBracketPart;
-            bool reverseMappedHadBracketPart = SplitLastSquareBracket(*out_reverseMappedName,
-                                                                      reverseMappedBracketPart);
-            if (reverseMappedHadBracketPart)
-                out_reverseMappedName->Append(bracketPart);
-        }
-        return;
-    }
+    if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
+        return true;
 
-    // Not found? We might be in the situation we have a uniform array name and
-    // the GL's glGetActiveUniform returned its name without [0], as is allowed
-    // by desktop GL but not in ES. Let's then try with [0].
-    mutableName.AppendLiteral("[0]");
-    if (mIdentifierReverseMap->Get(mutableName, out_reverseMappedName))
-        return;
-
-    /* Not found? Return name unchanged. This case happens e.g. on bad user
-     * input, or when we're not using identifier mapping, or if we didn't store
-     * an identifier in the map because e.g. its mapping is trivial. (as happens
-     * for short identifiers)
-     */
-    out_reverseMappedName->Assign(name);
-}
-
-WebGLUniformInfo
-WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name)
-{
-    MOZ_ASSERT(mUniformInfoMap);
-
-    nsCString mutableName(name);
-    nsCString bracketPart;
-    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-    // If there is a bracket, we're either an array or an entry in an array.
-    if (hadBracketPart)
-        mutableName.AppendLiteral("[0]");
-
-    WebGLUniformInfo info;
-    mUniformInfoMap->Get(mutableName, &info);
-    // We don't check if that Get failed, as if it did, it left info with
-    // default values.
-
-    return info;
+    return false;
 }
 
-bool
-WebGLProgram::UpdateInfo()
-{
-    mAttribMaxNameLength = 0;
-    for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
-        mAttribMaxNameLength = std::max(mAttribMaxNameLength,
-                                        mAttachedShaders[i]->mAttribMaxNameLength);
-    }
-
-    GLint attribCount;
-    mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount);
-
-    if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) {
-        mContext->ErrorOutOfMemory("updateInfo: Out of memory to allocate %d"
-                                   " attribs.", mContext->mGLMaxVertexAttribs);
-        return false;
-    }
-
-    for (size_t i = 0; i < mAttribsInUse.Length(); i++)
-        mAttribsInUse[i] = false;
-
-    nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]);
-
-    for (int i = 0; i < attribCount; ++i) {
-        GLint attrnamelen;
-        GLint attrsize;
-        GLenum attrtype;
-        mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength,
-                                       &attrnamelen, &attrsize, &attrtype,
-                                       nameBuf);
-        if (attrnamelen > 0) {
-            GLint loc = mContext->gl->fGetAttribLocation(mGLName, nameBuf);
-            MOZ_ASSERT(loc >= 0, "Major oops in managing the attributes of a"
-                                 " WebGL program.");
-            if (loc < mContext->mGLMaxVertexAttribs) {
-                mAttribsInUse[loc] = true;
-            } else {
-                mContext->GenerateWarning("Program exceeds MAX_VERTEX_ATTRIBS.");
-                return false;
-            }
-        }
-    }
+////////////////////////////////////////////////////////////////////////////////
 
-    // nsAutoPtr will delete old version first
-    mIdentifierMap = new CStringMap;
-    mIdentifierReverseMap = new CStringMap;
-    mUniformInfoMap = new CStringToUniformInfoMap;
-    for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
-        // Loop through ATTRIBUTES
-        for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
-            const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
-
-            // FORWARD MAPPING
-            mIdentifierMap->Put(attrib.original, attrib.mapped);
-            // REVERSE MAPPING
-            mIdentifierReverseMap->Put(attrib.mapped, attrib.original);
-        }
-
-        // Loop through UNIFORMS
-        for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
-            // Add the uniforms name mapping to mIdentifier[Reverse]Map
-            const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
-
-            // FOWARD MAPPING
-            mIdentifierMap->Put(uniform.original, uniform.mapped);
-            // REVERSE MAPPING
-            mIdentifierReverseMap->Put(uniform.mapped, uniform.original);
-
-            // Add uniform info to mUniformInfoMap
-            const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
-            mUniformInfoMap->Put(uniform.mapped, info);
-        }
-    }
-
-    mActiveAttribMap.clear();
-
-    GLint numActiveAttrs = 0;
-    mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs);
-
-    // Spec says the maximum attrib name length is 256 chars, so this is
-    // sufficient to hold any attrib name.
-    char attrName[257];
-
-    GLint dummySize;
-    GLenum dummyType;
-    for (GLint i = 0; i < numActiveAttrs; i++) {
-        mContext->gl->fGetActiveAttrib(mGLName, i, 257, nullptr, &dummySize,
-                                       &dummyType, attrName);
-        GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName);
-        MOZ_ASSERT(attrLoc >= 0);
-        mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName)));
-    }
-
-    return true;
+JSObject*
+WebGLProgram::WrapObject(JSContext* js)
+{
+    return dom::WebGLProgramBinding::Wrap(js, this);
 }
 
-/*static*/ uint64_t
-WebGLProgram::IdentifierHashFunction(const char* ident, size_t size)
-{
-    uint64_t outhash[2];
-    // NB: we use the x86 function everywhere, even though it's suboptimal perf
-    // on x64.  They return different results; not sure if that's a requirement.
-    MurmurHash3_x86_128(ident, size, 0, &outhash[0]);
-    return outhash[0];
-}
-
-/*static*/ void
-WebGLProgram::HashMapIdentifier(const nsACString& name,
-                                nsCString* const out_hashedName)
-{
-    uint64_t hash = IdentifierHashFunction(name.BeginReading(), name.Length());
-    out_hashedName->Truncate();
-    // This MUST MATCH HASHED_NAME_PREFIX from
-    // angle/src/compiler/translator/HashNames.h .
-    out_hashedName->AppendPrintf("webgl_%llx", hash);
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mAttachedShaders)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -4,135 +4,147 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_PROGRAM_H_
 #define WEBGL_PROGRAM_H_
 
 #include <map>
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
+#include "nsString.h"
 #include "nsWrapperCache.h"
+#include <set>
+#include <vector>
 #include "WebGLObjectModel.h"
 #include "WebGLShader.h"
-#include "WebGLUniformInfo.h"
 
 namespace mozilla {
 
+class WebGLActiveInfo;
+class WebGLProgram;
+class WebGLUniformLocation;
+
+namespace webgl {
+
+struct LinkedProgramInfo MOZ_FINAL
+    : public RefCounted<LinkedProgramInfo>
+    , public SupportsWeakPtr<LinkedProgramInfo>
+{
+    MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
+
+    WebGLProgram* const prog;
+    std::vector<nsRefPtr<WebGLActiveInfo>> activeAttribs;
+    std::vector<nsRefPtr<WebGLActiveInfo>> activeUniforms;
+
+    // Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped
+    // user-facing `GLActiveInfo::name`s, without any final "[0]".
+    std::map<nsCString, const WebGLActiveInfo*> attribMap;
+    std::map<nsCString, const WebGLActiveInfo*> uniformMap;
+
+    // Needed for draw call validation.
+    std::set<GLuint> activeAttribLocs;
+
+    explicit LinkedProgramInfo(WebGLProgram* aProg);
+
+    bool FindAttrib(const nsCString& baseUserName,
+                    const WebGLActiveInfo** const out_activeInfo) const
+    {
+        auto itr = attribMap.find(baseUserName);
+        if (itr == attribMap.end())
+            return false;
+
+        *out_activeInfo = itr->second;
+        return true;
+    }
+
+    bool FindUniform(const nsCString& baseUserName,
+                     const WebGLActiveInfo** const out_activeInfo) const
+    {
+        auto itr = uniformMap.find(baseUserName);
+        if (itr == uniformMap.end())
+            return false;
+
+        *out_activeInfo = itr->second;
+        return true;
+    }
+
+    bool HasActiveAttrib(GLuint loc) const {
+        auto itr = activeAttribLocs.find(loc);
+        return itr != activeAttribLocs.end();
+    }
+};
+
+} // namespace webgl
+
 class WebGLShader;
-struct WebGLUniformInfo;
 
 typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap;
-typedef nsDataHashtable<nsCStringHashKey,
-                        WebGLUniformInfo> CStringToUniformInfoMap;
 
 class WebGLProgram MOZ_FINAL
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
     , public WebGLContextBoundObject
 {
 public:
+    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
+
     explicit WebGLProgram(WebGLContext* webgl);
 
     void Delete();
 
-    void DetachShaders() {
-        mAttachedShaders.Clear();
-    }
-
-    GLuint GLName() { return mGLName; }
-    const nsTArray<WebGLRefPtr<WebGLShader> >& AttachedShaders() const {
-        return mAttachedShaders;
-    }
-    bool LinkStatus() { return mLinkStatus; }
-    uint32_t Generation() const { return mGeneration.value(); }
-    void SetLinkStatus(bool val) { mLinkStatus = val; }
-
-    bool ContainsShader(WebGLShader* shader) {
-        return mAttachedShaders.Contains(shader);
-    }
-
-    // return true if the shader wasn't already attached
-    bool AttachShader(WebGLShader* shader);
-
-    // return true if the shader was found and removed
-    bool DetachShader(WebGLShader* shader);
-
-    bool HasAttachedShaderOfType(GLenum shaderType);
-
-    bool HasBothShaderTypesAttached() {
-        return HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) &&
-               HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER);
-    }
-
-    bool HasBadShaderAttached();
+    // GL funcs
+    void AttachShader(WebGLShader* shader);
+    void BindAttribLocation(GLuint index, const nsAString& name);
+    void DetachShader(WebGLShader* shader);
+    already_AddRefed<WebGLActiveInfo> GetActiveAttrib(GLuint index) const;
+    already_AddRefed<WebGLActiveInfo> GetActiveUniform(GLuint index) const;
+    void GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const;
+    GLint GetAttribLocation(const nsAString& name) const;
+    void GetProgramInfoLog(nsAString* const out) const;
+    JS::Value GetProgramParameter(GLenum pname) const;
+    already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const;
+    bool LinkProgram();
+    bool UseProgram() const;
+    void ValidateProgram() const;
 
-    size_t UpperBoundNumSamplerUniforms();
-
-    bool NextGeneration() {
-        if (!(mGeneration + 1).isValid())
-            return false; // must exit without changing mGeneration
-
-        ++mGeneration;
-        return true;
-    }
-
-    // Called only after LinkProgram
-    bool UpdateInfo();
-
-    // Getters for cached program info
-    bool IsAttribInUse(uint32_t i) const { return mAttribsInUse[i]; }
+    ////////////////
 
-    // Maps identifier |name| to the mapped identifier |*mappedName|
-    // Both are ASCII strings.
-    void MapIdentifier(const nsACString& name, nsCString* out_mappedName);
+    bool FindAttribUserNameByMappedName(const nsACString& mappedName,
+                                        nsDependentCString* const out_userName) const;
+    bool FindUniformByMappedName(const nsACString& mappedName,
+                                 nsCString* const out_userName,
+                                 bool* const out_isArray) const;
 
-    // Un-maps mapped identifier |name| to the original identifier
-    // |*reverseMappedName|.
-    // Both are ASCII strings.
-    void ReverseMapIdentifier(const nsACString& name,
-                              nsCString* out_reverseMappedName);
+    bool IsLinked() const { return mMostRecentLinkInfo; }
 
-    /* Returns the uniform array size (or 1 if the uniform is not an array) of
-     * the uniform with given mapped identifier.
-     *
-     * Note: The input string |name| is the mapped identifier, not the original
-     * identifier.
-     */
-    WebGLUniformInfo GetUniformInfoForMappedIdentifier(const nsACString& name);
+    const webgl::LinkedProgramInfo* LinkInfo() const {
+        return mMostRecentLinkInfo.get();
+    }
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
-
-    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
+    virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
 
-    // public post-link data
-    std::map<GLint, nsCString> mActiveAttribMap;
-
-    static uint64_t IdentifierHashFunction(const char* ident, size_t size);
-    static void HashMapIdentifier(const nsACString& name,
-                                  nsCString* const out_hashedName);
-
-protected:
+private:
     ~WebGLProgram() {
         DeleteOnce();
     }
 
-    GLuint mGLName;
-    bool mLinkStatus;
-    // attached shaders of the program object
-    nsTArray<WebGLRefPtr<WebGLShader> > mAttachedShaders;
-    CheckedUint32 mGeneration;
+    bool LinkAndUpdate();
+
+public:
+    const GLuint mGLName;
 
-    // post-link data
-    FallibleTArray<bool> mAttribsInUse;
-    nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap;
-    nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap;
-    int mAttribMaxNameLength;
+private:
+    WebGLRefPtr<WebGLShader> mVertShader;
+    WebGLRefPtr<WebGLShader> mFragShader;
+    std::map<nsCString, GLuint> mBoundAttribLocs;
+    nsCString mLinkLog;
+    RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_PROGRAM_H_
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -4,61 +4,384 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLShader.h"
 
 #include "angle/ShaderLang.h"
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/MemoryReporting.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
 #include "WebGLContext.h"
 #include "WebGLObjectModel.h"
+#include "WebGLShaderValidator.h"
+#include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLShader::WrapObject(JSContext* cx) {
-    return dom::WebGLShaderBinding::Wrap(cx, this);
+// On success, writes to out_validator and out_translatedSource.
+// On failure, writes to out_translationLog.
+static bool
+Translate(const nsACString& source, webgl::ShaderValidator* validator,
+          nsACString* const out_translationLog, nsACString* const out_translatedSource)
+{
+    if (!validator->ValidateAndTranslate(source.BeginReading())) {
+        validator->GetInfoLog(out_translationLog);
+        return false;
+    }
+
+    // Success
+    validator->GetOutput(out_translatedSource);
+    return true;
+}
+
+template<size_t N>
+static bool
+SubstringStartsWith(const std::string& testStr, size_t offset, const char (& refStr)[N])
+{
+    for (size_t i = 0; i < N-1; i++) {
+        if (testStr[offset + i] != refStr[i])
+            return false;
+    }
+    return true;
+}
+
+/* On success, writes to out_translatedSource.
+ * On failure, writes to out_translationLog.
+ *
+ * Requirements:
+ *   #version is either omitted, `#version 100`, or `version 300 es`.
+ */
+static bool
+TranslateWithoutValidation(const nsACString& sourceNS, bool isWebGL2,
+                           nsACString* const out_translationLog,
+                           nsACString* const out_translatedSource)
+{
+    std::string source = sourceNS.BeginReading();
+
+    size_t versionStrStart = source.find("#version");
+    size_t versionStrLen;
+    uint32_t glesslVersion;
+
+    if (versionStrStart != std::string::npos) {
+        static const char versionStr100[] = "#version 100\n";
+        static const char versionStr300es[] = "#version 300 es\n";
+
+        if (isWebGL2 && SubstringStartsWith(source, versionStrStart, versionStr300es)) {
+            glesslVersion = 300;
+            versionStrLen = strlen(versionStr300es);
+
+        } else if (SubstringStartsWith(source, versionStrStart, versionStr100)) {
+            glesslVersion = 100;
+            versionStrLen = strlen(versionStr100);
+
+        } else {
+            nsPrintfCString error("#version, if declared, must be %s.",
+                                  isWebGL2 ? "`100` or `300 es`"
+                                           : "`100`");
+            *out_translationLog = error;
+            return false;
+        }
+    } else {
+        versionStrStart = 0;
+        versionStrLen = 0;
+        glesslVersion = 100;
+    }
+
+    std::string reversionedSource = source;
+    reversionedSource.erase(versionStrStart, versionStrLen);
+
+    switch (glesslVersion) {
+    case 100:
+        break;
+    case 300:
+        reversionedSource.insert(versionStrStart, "#version 330\n");
+        break;
+    default:
+        MOZ_CRASH("Bad `glesslVersion`.");
+    }
+
+    out_translatedSource->Assign(reversionedSource.c_str(),
+                                 reversionedSource.length());
+    return true;
+}
+
+static void
+GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader, bool* const out_success,
+                           nsACString* const out_log)
+{
+    GLint compileStatus = LOCAL_GL_FALSE;
+    gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus);
+
+    // It's simpler if we always get the log.
+    GLint lenWithNull = 0;
+    gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull);
+
+    if (lenWithNull > 1) {
+        // SetLength takes the length without the null.
+        out_log->SetLength(lenWithNull - 1);
+        gl->fGetShaderInfoLog(shader, lenWithNull, nullptr, out_log->BeginWriting());
+    } else {
+        out_log->SetLength(0);
+    }
+
+    *out_success = (compileStatus == LOCAL_GL_TRUE);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static GLuint
+CreateShader(gl::GLContext* gl, GLenum type)
+{
+    gl->MakeCurrent();
+    return gl->fCreateShader(type);
 }
 
 WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
     : WebGLContextBoundObject(webgl)
+    , mGLName(CreateShader(webgl->GL(), type))
     , mType(type)
-    , mNeedsTranslation(true)
-    , mAttribMaxNameLength(0)
-    , mCompileStatus(false)
+{
+    mContext->mShaders.insertBack(this);
+}
+
+WebGLShader::~WebGLShader()
+{
+    DeleteOnce();
+}
+
+void
+WebGLShader::ShaderSource(const nsAString& source)
+{
+    StripComments stripComments(source);
+    const nsAString& cleanSource = Substring(stripComments.result().Elements(),
+                                             stripComments.length());
+    if (!ValidateGLSLString(cleanSource, mContext, "shaderSource"))
+        return;
+
+    // We checked that the source stripped of comments is in the
+    // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
+    NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
+
+    if (mContext->gl->WorkAroundDriverBugs()) {
+        const size_t maxSourceLength = 0x3ffff;
+        if (sourceCString.Length() > maxSourceLength) {
+            mContext->ErrorInvalidValue("shaderSource: Source has more than %d"
+                                        " characters. (Driver workaround)",
+                                        maxSourceLength);
+            return;
+        }
+    }
+
+    // HACK - dump shader source
+    {
+/*
+        printf_stderr("//-*- glsl -*-\n");
+        // Wow - Roll Your Own For Each Lines because printf_stderr has a hard-coded internal size, so long strings are truncated.
+        const nsString& src = shader->Source();
+        int32_t start = 0;
+        int32_t end = src.Find("\n", false, start, -1);
+        while (end > -1) {
+            printf_stderr("%s\n", NS_ConvertUTF16toUTF8(nsDependentSubstring(src, start, end - start)).get());
+            start = end + 1;
+            end = src.Find("\n", false, start, -1);
+        }
+        printf_stderr("//\n");
+*/
+    }
+    // HACK
+
+    mSource = source;
+    mCleanSource = sourceCString;
+}
+
+void
+WebGLShader::CompileShader()
+{
+    mValidator = nullptr;
+    mTranslationSuccessful = false;
+    mCompilationSuccessful = false;
+
+    gl::GLContext* gl = mContext->gl;
+
+    mValidator.reset(mContext->CreateShaderValidator(mType));
+
+    bool success;
+    if (mValidator) {
+        success = Translate(mCleanSource, mValidator.get(), &mValidationLog,
+                            &mTranslatedSource);
+    } else {
+        success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
+                                             &mValidationLog, &mTranslatedSource);
+    }
+
+    if (!success)
+        return;
+
+    mTranslationSuccessful = true;
+
+    gl->MakeCurrent();
+
+    const char* const parts[] = {
+        mTranslatedSource.BeginReading()
+    };
+    gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr);
+
+    gl->fCompileShader(mGLName);
+
+    GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful, &mCompilationLog);
+}
+
+void
+WebGLShader::GetShaderInfoLog(nsAString* out) const
+{
+    const nsCString& log = !mTranslationSuccessful ? mValidationLog
+                                                   : mCompilationLog;
+    CopyASCIItoUTF16(log, *out);
+}
+
+JS::Value
+WebGLShader::GetShaderParameter(GLenum pname) const
 {
-    mContext->MakeContextCurrent();
-    mGLName = mContext->gl->fCreateShader(mType);
-    mContext->mShaders.insertBack(this);
+    switch (pname) {
+    case LOCAL_GL_SHADER_TYPE:
+        return JS::NumberValue(mType);
+
+    case LOCAL_GL_DELETE_STATUS:
+        return JS::BooleanValue(IsDeleteRequested());
+
+    case LOCAL_GL_COMPILE_STATUS:
+        return JS::BooleanValue(mCompilationSuccessful);
+
+    default:
+        mContext->ErrorInvalidEnumInfo("getShaderParameter: `pname`", pname);
+        return JS::NullValue();
+    }
+}
+
+void
+WebGLShader::GetShaderSource(nsAString* out) const
+{
+    out->SetIsVoid(false);
+    *out = mSource;
+}
+
+void
+WebGLShader::GetShaderTranslatedSource(nsAString* out) const
+{
+    if (!mCompilationSuccessful) {
+        mContext->ErrorInvalidOperation("getShaderTranslatedSource: Shader has"
+                                        " not been successfully compiled.");
+        return;
+    }
+
+    out->SetIsVoid(false);
+    CopyASCIItoUTF16(mTranslatedSource, *out);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool
+WebGLShader::CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const
+{
+    if (!mValidator)
+        return true;
+
+    return mValidator->CanLinkTo(prev->mValidator.get(), out_log);
+}
+
+size_t
+WebGLShader::CalcNumSamplerUniforms() const
+{
+    if (mValidator)
+        return mValidator->CalcNumSamplerUniforms();
+
+    // TODO
+    return 0;
+}
+
+void
+WebGLShader::BindAttribLocation(GLuint prog, const nsCString& userName,
+                                GLuint index) const
+{
+    std::string userNameStr(userName.BeginReading());
+
+    const std::string* mappedNameStr = &userNameStr;
+    if (mValidator)
+        mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
+
+    mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str());
+}
+
+bool
+WebGLShader::FindAttribUserNameByMappedName(const nsACString& mappedName,
+                                            nsDependentCString* const out_userName) const
+{
+    if (!mValidator)
+        return false;
+
+    const std::string mappedNameStr(mappedName.BeginReading());
+    const std::string* userNameStr;
+    if (!mValidator->FindAttribUserNameByMappedName(mappedNameStr, &userNameStr))
+        return false;
+
+    out_userName->Rebind(userNameStr->c_str());
+    return true;
+}
+
+bool
+WebGLShader::FindUniformByMappedName(const nsACString& mappedName,
+                                     nsCString* const out_userName,
+                                     bool* const out_isArray) const
+{
+    if (!mValidator)
+        return false;
+
+    const std::string mappedNameStr(mappedName.BeginReading(), mappedName.Length());
+    std::string userNameStr;
+    if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr, out_isArray))
+        return false;
+
+    *out_userName = userNameStr.c_str();
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Boilerplate
+
+JSObject*
+WebGLShader::WrapObject(JSContext* js)
+{
+    return dom::WebGLShaderBinding::Wrap(js, this);
+}
+
+size_t
+WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
+{
+    size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get())
+                                      : 0;
+    return mallocSizeOf(this) +
+           mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
+           mCleanSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
+           validatorSize +
+           mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
+           mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
+           mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
 }
 
 void
 WebGLShader::Delete()
 {
-    mSource.Truncate();
-    mTranslationLog.Truncate();
-    mContext->MakeContextCurrent();
-    mContext->gl->fDeleteShader(mGLName);
-    LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
-}
+    gl::GLContext* gl = mContext->GL();
 
-size_t
-WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return mallocSizeOf(this) +
-           mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
-           mTranslationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
-}
+    gl->MakeCurrent();
+    gl->fDeleteShader(mGLName);
 
-void
-WebGLShader::SetTranslationSuccess()
-{
-    mTranslationLog.SetIsVoid(true);
-    mNeedsTranslation = false;
+    LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLShader, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLShader, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -1,106 +1,88 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_SHADER_H_
 #define WEBGL_SHADER_H_
 
+#include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
-#include "WebGLUniformInfo.h"
 
 namespace mozilla {
 
-struct WebGLMappedIdentifier
-{
-    // ASCII strings
-    nsCString original;
-    nsCString mapped;
-
-    WebGLMappedIdentifier(const nsACString& o, const nsACString& m)
-        : original(o)
-        , mapped(m)
-    {}
-};
+namespace webgl {
+class ShaderValidator;
+} // namespace webgl
 
 class WebGLShader MOZ_FINAL
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLShader>
     , public LinkedListElement<WebGLShader>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGLProgram;
 
 public:
     WebGLShader(WebGLContext* webgl, GLenum type);
 
-    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+protected:
+    ~WebGLShader();
 
-    GLuint GLName() { return mGLName; }
-    sh::GLenum ShaderType() { return mType; }
+public:
+    // GL funcs
+    void CompileShader();
+    JS::Value GetShaderParameter(GLenum pname) const;
+    void GetShaderInfoLog(nsAString* out) const;
+    void GetShaderSource(nsAString* out) const;
+    void GetShaderTranslatedSource(nsAString* out) const;
+    void ShaderSource(const nsAString& source);
 
-    void SetSource(const nsAString& src) {
-        // TODO: Do some quick gzip here maybe? Getting this will be very rare,
-        // and we have to keep it forever.
-        mSource.Assign(src);
-    }
+    // Util funcs
+    bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
+    size_t CalcNumSamplerUniforms() const;
+    void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
+    bool FindAttribUserNameByMappedName(const nsACString& mappedName,
+                                        nsDependentCString* const out_userName) const;
+    bool FindUniformByMappedName(const nsACString& mappedName,
+                                 nsCString* const out_userName,
+                                 bool* const out_isArray) const;
 
-    const nsString& Source() const { return mSource; }
-
-    void SetNeedsTranslation() { mNeedsTranslation = true; }
-    bool NeedsTranslation() const { return mNeedsTranslation; }
-
-    void SetCompileStatus (bool status) {
-        mCompileStatus = status;
+    bool IsCompiled() const {
+        return mTranslationSuccessful && mCompilationSuccessful;
     }
 
+    // Other funcs
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     void Delete();
 
-    bool CompileStatus() const {
-        return mCompileStatus;
-    }
-
-    void SetTranslationSuccess();
-
-    void SetTranslationFailure(const nsCString& msg) {
-        mTranslationLog.Assign(msg);
-    }
+    WebGLContext* GetParentObject() const { return Context(); }
 
-    const nsCString& TranslationLog() const { return mTranslationLog; }
-
-    const nsString& TranslatedSource() const { return mTranslatedSource; }
-
-    WebGLContext* GetParentObject() const {
-        return Context();
-    }
-
-    virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
+    virtual JSObject* WrapObject(JSContext* js) MOZ_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:
-    ~WebGLShader() {
-        DeleteOnce();
-    }
-
-    GLuint mGLName;
-    GLenum mType;
     nsString mSource;
-    nsString mTranslatedSource;
-    nsCString mTranslationLog; // The translation log should contain only ASCII characters
-    bool mNeedsTranslation;
-    nsTArray<WebGLMappedIdentifier> mAttributes;
-    nsTArray<WebGLMappedIdentifier> mUniforms;
-    nsTArray<WebGLUniformInfo> mUniformInfos;
-    int mAttribMaxNameLength;
-    bool mCompileStatus;
+    nsCString mCleanSource;
+
+    UniquePtr<webgl::ShaderValidator> mValidator;
+    nsCString mValidationLog;
+    bool mTranslationSuccessful;
+    nsCString mTranslatedSource;
+
+    bool mCompilationSuccessful;
+    nsCString mCompilationLog;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_SHADER_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLShaderValidator.h"
+
+#include "angle/ShaderLang.h"
+#include "GLContext.h"
+#include "MurmurHash3.h"
+#include "nsPrintfCString.h"
+#include <string>
+#include <vector>
+#include "WebGLContext.h"
+
+namespace mozilla {
+namespace webgl {
+
+uint64_t
+IdentifierHashFunc(const char* name, size_t len)
+{
+    // NB: we use the x86 function everywhere, even though it's suboptimal perf
+    // on x64.  They return different results; not sure if that's a requirement.
+    uint64_t hash[2];
+    MurmurHash3_x86_128(name, len, 0, hash);
+    return hash[0];
+}
+
+static int
+ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
+                              const mozilla::gl::GLContext* gl)
+{
+    int options = SH_VARIABLES |
+                  SH_ENFORCE_PACKING_RESTRICTIONS |
+                  SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
+                  SH_OBJECT_CODE |
+                  SH_LIMIT_CALL_STACK_DEPTH;
+
+    if (resources.MaxExpressionComplexity > 0) {
+        options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
+    }
+
+#ifndef XP_MACOSX
+    // We want to do this everywhere, but to do this on Mac, we need
+    // to do it only on Mac OSX > 10.6 as this causes the shader
+    // compiler in 10.6 to crash
+    options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+#endif
+
+#ifdef XP_MACOSX
+    if (gl->WorkAroundDriverBugs()) {
+        // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
+        // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
+        options |= SH_UNFOLD_SHORT_CIRCUIT;
+
+        // Work around bug 665578 and bug 769810
+        if (gl->Vendor() == gl::GLVendor::ATI) {
+            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+        }
+
+        // Work around bug 735560
+        if (gl->Vendor() == gl::GLVendor::Intel) {
+            options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+        }
+
+        // Work around bug 636926
+        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
+            options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
+        }
+    }
+#endif
+
+    return options;
+}
+
+} // namespace webgl
+
+////////////////////////////////////////
+
+webgl::ShaderValidator*
+WebGLContext::CreateShaderValidator(GLenum shaderType) const
+{
+    if (mBypassShaderValidation)
+        return nullptr;
+
+    ShShaderSpec spec = SH_WEBGL_SPEC;
+    ShShaderOutput outputLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT
+                                                 : SH_GLSL_OUTPUT;
+
+    ShBuiltInResources resources;
+    memset(&resources, 0, sizeof(resources));
+    ShInitBuiltInResources(&resources);
+
+    resources.HashFunction = webgl::IdentifierHashFunc;
+
+    resources.MaxVertexAttribs = mGLMaxVertexAttribs;
+    resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
+    resources.MaxVaryingVectors = mGLMaxVaryingVectors;
+    resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
+    resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
+    resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
+    resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
+    resources.MaxDrawBuffers = mGLMaxDrawBuffers;
+
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
+        resources.EXT_frag_depth = 1;
+
+    if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
+        resources.OES_standard_derivatives = 1;
+
+    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
+        resources.EXT_draw_buffers = 1;
+
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
+        resources.EXT_shader_texture_lod = 1;
+
+    // Tell ANGLE to allow highp in frag shaders. (unless disabled)
+    // If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
+    resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
+
+    if (gl->WorkAroundDriverBugs()) {
+#ifdef XP_MACOSX
+        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
+            // Work around bug 890432
+            resources.MaxExpressionComplexity = 1000;
+        }
+#endif
+    }
+
+    int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
+
+    return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources,
+                                          compileOptions);
+}
+
+////////////////////////////////////////
+
+namespace webgl {
+
+/*static*/ ShaderValidator*
+ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec,
+                        ShShaderOutput outputLanguage,
+                        const ShBuiltInResources& resources, int compileOptions)
+{
+    ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources);
+    if (!handle)
+        return nullptr;
+
+    return new ShaderValidator(handle, compileOptions);
+}
+
+ShaderValidator::~ShaderValidator()
+{
+    ShDestruct(mHandle);
+}
+
+bool
+ShaderValidator::ValidateAndTranslate(const char* source)
+{
+    MOZ_ASSERT(!mHasRun);
+    mHasRun = true;
+
+    const char* const parts[] = {
+        source
+    };
+    return ShCompile(mHandle, parts, ArrayLength(parts), mCompileOptions);
+}
+
+void
+ShaderValidator::GetInfo(ShShaderInfo pname, size_t* params) const
+{
+    MOZ_ASSERT(mHasRun);
+
+    ShGetInfo(mHandle, pname, params);
+}
+
+void
+ShaderValidator::GetInfoLog(nsACString* out) const
+{
+    MOZ_ASSERT(mHasRun);
+
+    size_t lenWithNull = 0;
+    GetInfo(SH_INFO_LOG_LENGTH, &lenWithNull);
+    MOZ_ASSERT(lenWithNull >= 1);
+
+    // SetLength allocates len+1, for the null-term.
+    out->SetLength(lenWithNull - 1);
+
+    ShGetInfoLog(mHandle, out->BeginWriting());
+}
+
+void
+ShaderValidator::GetOutput(nsACString* out) const
+{
+    MOZ_ASSERT(mHasRun);
+
+    size_t lenWithNull = 0;
+    GetInfo(SH_OBJECT_CODE_LENGTH, &lenWithNull);
+    MOZ_ASSERT(lenWithNull >= 1);
+
+    // SetLength allocates len+1, for the null-term.
+    out->SetLength(lenWithNull - 1);
+
+    ShGetObjectCode(mHandle, out->BeginWriting());
+}
+
+template<size_t N>
+static bool
+StartsWith(const std::string& haystack, const char (&needle)[N])
+{
+    return haystack.compare(0, N - 1, needle) == 0;
+}
+
+bool
+ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const
+{
+    {
+        const std::vector<sh::Uniform>& vertList = *ShGetUniforms(prev->mHandle);
+        const std::vector<sh::Uniform>& fragList = *ShGetUniforms(mHandle);
+
+        for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
+            for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
+                if (itrVert->name != itrFrag->name)
+                    continue;
+
+                if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) {
+                    nsPrintfCString error("Uniform `%s`is not linkable between"
+                                          " attached shaders.",
+                                          itrFrag->name.c_str());
+                    *out_log = error;
+                    return false;
+                }
+
+                break;
+            }
+        }
+    }
+    {
+        const std::vector<sh::Varying>& vertList = *ShGetVaryings(prev->mHandle);
+        const std::vector<sh::Varying>& fragList = *ShGetVaryings(mHandle);
+
+        for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
+            static const char prefix[] = "gl_";
+            if (StartsWith(itrFrag->name, prefix))
+                continue;
+
+            bool definedInVertShader = false;
+
+            for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
+                if (itrVert->name != itrFrag->name)
+                    continue;
+
+                if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) {
+                    nsPrintfCString error("Varying `%s`is not linkable between"
+                                          " attached shaders.",
+                                          itrFrag->name.c_str());
+                    *out_log = error;
+                    return false;
+                }
+
+                definedInVertShader = true;
+                break;
+            }
+
+            if (!definedInVertShader && itrFrag->staticUse) {
+                nsPrintfCString error("Varying `%s` has static-use in the frag"
+                                      " shader, but is undeclared in the vert"
+                                      " shader.", itrFrag->name.c_str());
+                *out_log = error;
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+size_t
+ShaderValidator::CalcNumSamplerUniforms() const
+{
+    size_t accum = 0;
+
+    const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle);
+
+    for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) {
+        GLenum type = itr->type;
+        if (type == LOCAL_GL_SAMPLER_2D ||
+            type == LOCAL_GL_SAMPLER_CUBE)
+        {
+            accum += itr->arraySize;
+        }
+    }
+
+    return accum;
+}
+
+// Attribs cannot be structs or arrays, and neither can vertex inputs in ES3.
+// Therefore, attrib names are always simple.
+bool
+ShaderValidator::FindAttribUserNameByMappedName(const std::string& mappedName,
+                                                const std::string** const out_userName) const
+{
+    const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle);
+    for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
+        if (itr->mappedName == mappedName) {
+            *out_userName = &(itr->name);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool
+ShaderValidator::FindAttribMappedNameByUserName(const std::string& userName,
+                                                const std::string** const out_mappedName) const
+{
+    const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle);
+    for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
+        if (itr->name == userName) {
+            *out_mappedName = &(itr->mappedName);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+// This must handle names like "foo.bar[0]".
+bool
+ShaderValidator::FindUniformByMappedName(const std::string& mappedName,
+                                         std::string* const out_userName,
+                                         bool* const out_isArray) const
+{
+    const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle);
+    for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) {
+        const sh::ShaderVariable* found;
+        if (!itr->findInfoByMappedName(mappedName, &found, out_userName))
+            continue;
+
+        *out_isArray = found->isArray();
+        return true;
+    }
+
+    return false;
+}
+
+} // namespace webgl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLShaderValidator.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGL_SHADER_VALIDATOR_H_
+#define WEBGL_SHADER_VALIDATOR_H_
+
+#include "angle/ShaderLang.h"
+#include "GLDefs.h"
+#include "nsString.h"
+#include <string>
+
+namespace mozilla {
+namespace webgl {
+
+class ShaderValidator MOZ_FINAL
+{
+    const ShHandle mHandle;
+    const int mCompileOptions;
+    bool mHasRun;
+
+public:
+    static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec,
+                                   ShShaderOutput outputLanguage,
+                                   const ShBuiltInResources& resources,
+                                   int compileOptions);
+
+private:
+    ShaderValidator(ShHandle handle, int compileOptions)
+        : mHandle(handle)
+        , mCompileOptions(compileOptions)
+        , mHasRun(false)
+    { }
+
+public:
+    ~ShaderValidator();
+
+    bool ValidateAndTranslate(const char* source);
+    void GetInfo(ShShaderInfo pname, size_t* params) const;
+    void GetInfoLog(nsACString* out) const;
+    void GetOutput(nsACString* out) const;
+    bool CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const;
+    size_t CalcNumSamplerUniforms() const;
+
+    bool FindAttribUserNameByMappedName(const std::string& mappedName,
+                                        const std::string** const out_userName) const;
+
+    bool FindAttribMappedNameByUserName(const std::string& userName,
+                                        const std::string** const out_mappedName) const;
+
+    bool FindUniformByMappedName(const std::string& mappedName,
+                                 std::string* const out_userName,
+                                 bool* const out_isArray) const;
+};
+
+} // namespace webgl
+} // namespace mozilla
+
+#endif // WEBGL_SHADER_VALIDATOR_H_
deleted file mode 100644
--- a/dom/canvas/WebGLUniformInfo.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef WEBGLUNIFORMINFO_H_
-#define WEBGLUNIFORMINFO_H_
-
-#include "GLDefs.h"
-#include "angle/ShaderLang.h"
-
-namespace mozilla {
-
-struct WebGLUniformInfo {
-    uint32_t arraySize;
-    bool isArray;
-    sh::GLenum type;
-
-    explicit WebGLUniformInfo(uint32_t s = 0, bool a = false, sh::GLenum t = LOCAL_GL_NONE)
-        : arraySize(s), isArray(a), type(t) {}
-
-    int ElementSize() const {
-        switch (type) {
-            case LOCAL_GL_FLOAT:
-            case LOCAL_GL_INT:
-            case LOCAL_GL_UNSIGNED_INT:
-            case LOCAL_GL_BOOL:
-            case LOCAL_GL_SAMPLER_2D:
-            case LOCAL_GL_SAMPLER_3D:
-            case LOCAL_GL_SAMPLER_CUBE:
-            case LOCAL_GL_SAMPLER_2D_SHADOW:
-            case LOCAL_GL_SAMPLER_2D_ARRAY:
-            case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
-            case LOCAL_GL_SAMPLER_CUBE_SHADOW:
-            case LOCAL_GL_INT_SAMPLER_2D:
-            case LOCAL_GL_INT_SAMPLER_3D:
-            case LOCAL_GL_INT_SAMPLER_CUBE:
-            case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
-            case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
-            case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
-            case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
-            case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
-                return 1;
-            case LOCAL_GL_FLOAT_VEC2:
-            case LOCAL_GL_INT_VEC2:
-            case LOCAL_GL_UNSIGNED_INT_VEC2:
-            case LOCAL_GL_BOOL_VEC2:
-                return 2;
-            case LOCAL_GL_FLOAT_VEC3:
-            case LOCAL_GL_INT_VEC3:
-            case LOCAL_GL_UNSIGNED_INT_VEC3:
-            case LOCAL_GL_BOOL_VEC3:
-                return 3;
-            case LOCAL_GL_FLOAT_VEC4:
-            case LOCAL_GL_INT_VEC4:
-            case LOCAL_GL_UNSIGNED_INT_VEC4:
-            case LOCAL_GL_BOOL_VEC4:
-            case LOCAL_GL_FLOAT_MAT2:
-                return 4;
-            case LOCAL_GL_FLOAT_MAT2x3:
-            case LOCAL_GL_FLOAT_MAT3x2:
-                return 6;
-            case LOCAL_GL_FLOAT_MAT2x4:
-            case LOCAL_GL_FLOAT_MAT4x2:
-                return 8;
-            case LOCAL_GL_FLOAT_MAT3:
-                return 9;
-            case LOCAL_GL_FLOAT_MAT3x4:
-            case LOCAL_GL_FLOAT_MAT4x3:
-                return 12;
-            case LOCAL_GL_FLOAT_MAT4:
-                return 16;
-            default:
-                MOZ_ASSERT(false); // should never get here
-                return 0;
-        }
-    }
-};
-
-} // namespace mozilla
-
-#endif
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -1,39 +1,278 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLUniformLocation.h"
 
+#include "GLContext.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "WebGLActiveInfo.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
-#include "WebGLShader.h"
 
 namespace mozilla {
 
-JSObject*
-WebGLUniformLocation::WrapObject(JSContext* cx)
+WebGLUniformLocation::WebGLUniformLocation(WebGLContext* webgl,
+                                           const webgl::LinkedProgramInfo* linkInfo,
+                                           GLuint loc, const WebGLActiveInfo* activeInfo)
+    : WebGLContextBoundObject(webgl)
+    , mLinkInfo(linkInfo)
+    , mLoc(loc)
+    , mActiveInfo(activeInfo)
+{ }
+
+WebGLUniformLocation::~WebGLUniformLocation()
+{ }
+
+bool
+WebGLUniformLocation::ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
+                                         const char* funcName) const
+{
+    // Check the weak-pointer.
+    if (!mLinkInfo) {
+        webgl->ErrorInvalidOperation("%s: This uniform location is obsolete because its"
+                                     " program has been successfully relinked.",
+                                     funcName);
+        return false;
+    }
+
+    if (mLinkInfo->prog != prog) {
+        webgl->ErrorInvalidOperation("%s: This uniform location corresponds to a"
+                                     " different program.", funcName);
+        return false;
+    }
+
+    return true;
+}
+
+static bool
+IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
 {
-    return dom::WebGLUniformLocationBinding::Wrap(cx, this);
+    switch (uniformType) {
+    case LOCAL_GL_BOOL:
+    case LOCAL_GL_BOOL_VEC2:
+    case LOCAL_GL_BOOL_VEC3:
+    case LOCAL_GL_BOOL_VEC4:
+        return setterType == LOCAL_GL_INT ||
+               setterType == LOCAL_GL_FLOAT; // GLfloat(0.0) sets a bool to false.
+
+    case LOCAL_GL_INT:
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_INT_VEC2:
+    case LOCAL_GL_INT_VEC3:
+    case LOCAL_GL_INT_VEC4:
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+        return setterType == LOCAL_GL_INT;
+
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_MAT2x3:
+    case LOCAL_GL_FLOAT_MAT2x4:
+    case LOCAL_GL_FLOAT_MAT3:
+    case LOCAL_GL_FLOAT_MAT3x2:
+    case LOCAL_GL_FLOAT_MAT3x4:
+    case LOCAL_GL_FLOAT_MAT4:
+    case LOCAL_GL_FLOAT_MAT4x2:
+    case LOCAL_GL_FLOAT_MAT4x3:
+    case LOCAL_GL_FLOAT_VEC2:
+    case LOCAL_GL_FLOAT_VEC3:
+    case LOCAL_GL_FLOAT_VEC4:
+        return setterType == LOCAL_GL_FLOAT;
+
+    default:
+        MOZ_CRASH("Bad `uniformType`.");
+    }
+}
+
+bool
+WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
+                                          WebGLContext* webgl, const char* funcName) const
+{
+    MOZ_ASSERT(mLinkInfo);
+
+    if (setterElemSize != mActiveInfo->mElemSize) {
+        webgl->ErrorInvalidOperation("%s: Bad uniform size: %i", funcName,
+                                     mActiveInfo->mElemSize);
+        return false;
+    }
+
+    if (!IsUniformSetterTypeValid(setterType, mActiveInfo->mElemType)) {
+        webgl->ErrorInvalidOperation("%s: Bad uniform type: %i", funcName,
+                                     mActiveInfo->mElemSize);
+        return false;
+    }
+
+    return true;
 }
 
-WebGLUniformLocation::WebGLUniformLocation(WebGLContext* context,
-                                           WebGLProgram* program,
-                                           GLint location,
-                                           const WebGLUniformInfo& info)
-    : WebGLContextBoundObject(context)
-    , mProgram(program)
-    , mProgramGeneration(program->Generation())
-    , mLocation(location)
-    , mInfo(info)
+bool
+WebGLUniformLocation::ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
+                                          WebGLContext* webgl, const char* funcName) const
 {
-    mElementSize = info.ElementSize();
+    MOZ_ASSERT(mLinkInfo);
+
+    if (setterArraySize == 0 ||
+        setterArraySize % setterElemSize)
+    {
+        webgl->ErrorInvalidValue("%s: expected an array of length a multiple of"
+                                 " %d, got an array of length %d.",
+                                 funcName, setterElemSize, setterArraySize);
+        return false;
+    }
+
+    /* GLES 2.0.25, Section 2.10, p38
+     *   When loading `N` elements starting at an arbitrary position `k` in a uniform
+     *   declared as an array, elements `k` through `k + N - 1` in the array will be
+     *   replaced with the new values. Values for any array element that exceeds the
+     *   highest array element index used, as reported by `GetActiveUniform`, will be
+     *   ignored by GL.
+     */
+    if (!mActiveInfo->mIsArray &&
+        setterArraySize != setterElemSize)
+    {
+        webgl->ErrorInvalidOperation("%s: expected an array of length exactly %d"
+                                     " (since this uniform is not an array"
+                                     " uniform), got an array of length %d.",
+                                     funcName, setterElemSize, setterArraySize);
+        return false;
+    }
+
+    return true;
+}
+
+bool
+WebGLUniformLocation::ValidateSamplerSetter(GLint value, WebGLContext* webgl,
+                                            const char* funcName) const
+{
+    MOZ_ASSERT(mLinkInfo);
+
+    if (mActiveInfo->mElemType != LOCAL_GL_SAMPLER_2D &&
+        mActiveInfo->mElemType != LOCAL_GL_SAMPLER_CUBE)
+    {
+        return true;
+    }
+
+    if (value >= 0 && value < (GLint)webgl->GLMaxTextureUnits())
+        return true;
+
+    webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d is not a"
+                             " valid texture unit.",
+                             funcName, value);
+    return false;
 }
 
-NS_IMPL_CYCLE_COLLECTION(WebGLUniformLocation, mProgram)
+JS::Value
+WebGLUniformLocation::GetUniform(JSContext* js, WebGLContext* webgl) const
+{
+    MOZ_ASSERT(mLinkInfo);
+
+    uint8_t elemSize = mActiveInfo->mElemSize;
+    static const uint8_t kMaxElemSize = 16;
+    MOZ_ASSERT(elemSize <= kMaxElemSize);
+
+    GLuint prog = mLinkInfo->prog->mGLName;
+
+    gl::GLContext* gl = webgl->GL();
+    gl->MakeCurrent();
+
+    switch (mActiveInfo->mElemType) {
+    case LOCAL_GL_INT:
+    case LOCAL_GL_INT_VEC2:
+    case LOCAL_GL_INT_VEC3:
+    case LOCAL_GL_INT_VEC4:
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_CUBE:
+        {
+            GLint buffer[kMaxElemSize] = {0};
+            gl->fGetUniformiv(prog, mLoc, buffer);
+
+            if (elemSize == 1)
+                return JS::Int32Value(buffer[0]);
+
+            JSObject* obj = dom::Int32Array::Create(js, webgl, elemSize, buffer);
+            if (!obj) {
+                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
+            }
+            return JS::ObjectOrNullValue(obj);
+        }
+
+    case LOCAL_GL_BOOL:
+    case LOCAL_GL_BOOL_VEC2:
+    case LOCAL_GL_BOOL_VEC3:
+    case LOCAL_GL_BOOL_VEC4:
+        {
+            GLint buffer[kMaxElemSize] = {0};
+            gl->fGetUniformiv(prog, mLoc, buffer);
+
+            if (elemSize == 1)
+                return JS::BooleanValue(buffer[0]);
+
+            bool boolBuffer[kMaxElemSize];
+            for (uint8_t i = 0; i < kMaxElemSize; i++)
+                boolBuffer[i] = buffer[i];
+
+            JS::RootedValue val(js);
+            // Be careful: we don't want to convert all of |uv|!
+            if (!dom::ToJSValue(js, boolBuffer, elemSize, &val)) {
+                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
+            }
+            return val;
+        }
+
+    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_MAT3:
+    case LOCAL_GL_FLOAT_MAT4:
+        {
+            GLfloat buffer[16] = {0.0f};
+            gl->fGetUniformfv(prog, mLoc, buffer);
+
+            if (elemSize == 1)
+                return JS::DoubleValue(buffer[0]);
+
+            JSObject* obj = dom::Float32Array::Create(js, webgl, elemSize, buffer);
+            if (!obj) {
+                webgl->ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
+            }
+            return JS::ObjectOrNullValue(obj);
+        }
+
+    default:
+        MOZ_CRASH("Invalid elemType.");
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JSObject*
+WebGLUniformLocation::WrapObject(JSContext* js)
+{
+    return dom::WebGLUniformLocationBinding::Wrap(js, this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocation)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLUniformLocation, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLUniformLocation, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -1,55 +1,63 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_UNIFORM_LOCATION_H_
 #define WEBGL_UNIFORM_LOCATION_H_
 
+#include "GLDefs.h"
+#include "mozilla/WeakPtr.h"
+#include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
-#include "WebGLUniformInfo.h"
+
+struct JSContext;
 
 namespace mozilla {
+class WebGLActiveInfo;
+class WebGLContext;
+class WebGLProgram;
 
-class WebGLProgram;
+namespace webgl {
+struct LinkedProgramInfo;
+}
 
 class WebGLUniformLocation MOZ_FINAL
-    : public WebGLContextBoundObject
+    : public nsWrapperCache
+    , public WebGLContextBoundObject
 {
 public:
-    WebGLUniformLocation(WebGLContext* context, WebGLProgram* program,
-                         GLint location, const WebGLUniformInfo& info);
+    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLUniformLocation)
+
+    const WeakPtr<const webgl::LinkedProgramInfo> mLinkInfo;
+    const GLuint mLoc;
+    const WebGLActiveInfo* const mActiveInfo;
+
+    WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo,
+                         GLuint loc, const WebGLActiveInfo* activeInfo);
 
-    // needed for certain helper functions like ValidateObject.
-    // WebGLUniformLocation's can't be 'Deleted' in the WebGL sense.
+    bool ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
+                            const char* funcName) const;
+    bool ValidateSamplerSetter(GLint value, WebGLContext* webgl,
+                               const char* funcName) const;
+    bool ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
+                             WebGLContext* webgl, const char* funcName) const;
+    bool ValidateArrayLength(uint8_t setterElemSize, size_t setterArraySize,
+                             WebGLContext* webgl, const char* funcName) const;
+
+    JS::Value GetUniform(JSContext* js, WebGLContext* webgl) const;
+
+    // Needed for certain helper functions like ValidateObject.
+    // `WebGLUniformLocation`s can't be 'Deleted' in the WebGL sense.
     bool IsDeleted() const { return false; }
 
-    const WebGLUniformInfo& Info() const { return mInfo; }
-
-    WebGLProgram* Program() const { return mProgram; }
-    GLint Location() const { return mLocation; }
-    uint32_t ProgramGeneration() const { return mProgramGeneration; }
-    int ElementSize() const { return mElementSize; }
-
-    JSObject* WrapObject(JSContext* cx);
-
-    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
-    NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(WebGLUniformLocation)
+    virtual JSObject* WrapObject(JSContext* js) MOZ_OVERRIDE;
 
 protected:
-    ~WebGLUniformLocation() { }
-
-    // nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted.
-    // we just want to avoid having a dangling pointer.
-    nsRefPtr<WebGLProgram> mProgram;
-
-    uint32_t mProgramGeneration;
-    GLint mLocation;
-    WebGLUniformInfo mInfo;
-    int mElementSize;
-    friend class WebGLProgram;
+    ~WebGLUniformLocation();
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_UNIFORM_LOCATION_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLValidateStrings.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLValidateStrings.h"
+
+#include "nsString.h"
+#include "WebGLContext.h"
+
+namespace mozilla {
+// The following code was taken from the WebKit WebGL implementation,
+// which can be found here:
+// http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121
+// Note that some modifications were done to adapt it to Mozilla.
+/****** BEGIN CODE TAKEN FROM WEBKIT ******/
+bool IsValidGLSLCharacter(char16_t c)
+{
+    // Printing characters are valid except " $ ` @ \ ' DEL.
+    if (c >= 32 && c <= 126 &&
+        c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'')
+    {
+         return true;
+    }
+
+    // Horizontal tab, line feed, vertical tab, form feed, carriage return
+    // are also valid.
+    if (c >= 9 && c <= 13) {
+         return true;
+    }
+
+    return false;
+}
+
+void StripComments::process(char16_t c)
+{
+    if (isNewline(c)) {
+        // No matter what state we are in, pass through newlines
+        // so we preserve line numbers.
+        emit(c);
+
+        if (m_parseState != InMultiLineComment)
+            m_parseState = BeginningOfLine;
+
+        return;
+    }
+
+    char16_t temp = 0;
+    switch (m_parseState) {
+    case BeginningOfLine:
+        // If it's an ASCII space.
+        if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) {
+            emit(c);
+            break;
+        }
+
+        if (c == '#') {
+            m_parseState = InPreprocessorDirective;
+            emit(c);
+            break;
+        }
+
+        // Transition to normal state and re-handle character.
+        m_parseState = MiddleOfLine;
+        process(c);
+        break;
+
+    case MiddleOfLine:
+        if (c == '/' && peek(temp)) {
+            if (temp == '/') {
+                m_parseState = InSingleLineComment;
+                emit(' ');
+                advance();
+                break;
+            }
+
+            if (temp == '*') {
+                m_parseState = InMultiLineComment;
+                // Emit the comment start in case the user has
+                // an unclosed comment and we want to later
+                // signal an error.
+                emit('/');
+                emit('*');
+                advance();
+                break;
+            }
+        }
+
+        emit(c);
+        break;
+
+    case InPreprocessorDirective:
+        // No matter what the character is, just pass it
+        // through. Do not parse comments in this state. This
+        // might not be the right thing to do long term, but it
+        // should handle the #error preprocessor directive.
+        emit(c);
+        break;
+
+    case InSingleLineComment:
+        // The newline code at the top of this function takes care
+        // of resetting our state when we get out of the
+        // single-line comment. Swallow all other characters.
+        break;
+
+    case InMultiLineComment:
+        if (c == '*' && peek(temp) && temp == '/') {
+            emit('*');
+            emit('/');
+            m_parseState = MiddleOfLine;
+            advance();
+            break;
+        }
+
+        // Swallow all other characters. Unclear whether we may
+        // want or need to just emit a space per character to try
+        // to preserve column numbers for debugging purposes.
+        break;
+    }
+}
+
+/****** END CODE TAKEN FROM WEBKIT ******/
+
+bool
+ValidateGLSLString(const nsAString& string, WebGLContext* webgl, const char* funcName)
+{
+    for (size_t i = 0; i < string.Length(); ++i) {
+        if (!IsValidGLSLCharacter(string.CharAt(i))) {
+           webgl->ErrorInvalidValue("%s: String contains the illegal character '%d'",
+                                    funcName, string.CharAt(i));
+           return false;
+        }
+    }
+
+    return true;
+}
+
+bool
+ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl, const char* funcName)
+{
+    if (name.IsEmpty())
+        return false;
+
+    const uint32_t maxSize = 256;
+    if (name.Length() > maxSize) {
+        webgl->ErrorInvalidValue("%s: Identifier is %d characters long, exceeds the"
+                                 " maximum allowed length of %d characters.",
+                                 funcName, name.Length(), maxSize);
+        return false;
+    }
+
+    if (!ValidateGLSLString(name, webgl, funcName))
+        return false;
+
+    nsString prefix1 = NS_LITERAL_STRING("webgl_");
+    nsString prefix2 = NS_LITERAL_STRING("_webgl_");
+
+    if (Substring(name, 0, prefix1.Length()).Equals(prefix1) ||
+        Substring(name, 0, prefix2.Length()).Equals(prefix2))
+    {
+        webgl->ErrorInvalidOperation("%s: String contains a reserved GLSL prefix.",
+                                     funcName);
+        return false;
+    }
+
+    return true;
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGLValidateStrings.h
+++ b/dom/canvas/WebGLValidateStrings.h
@@ -22,231 +22,135 @@
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef WEBGL_VALIDATE_STRINGS_H_
 #define WEBGL_VALIDATE_STRINGS_H_
 
-#include "WebGLContext.h"
+#include "nsString.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 
+class WebGLContext;
+
 // The following code was taken from the WebKit WebGL implementation,
 // which can be found here:
 // http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121
 // Note that some modifications were done to adapt it to Mozilla.
 /****** BEGIN CODE TAKEN FROM WEBKIT ******/
-    bool WebGLContext::ValidateGLSLCharacter(char16_t c)
+// Strips comments from shader text. This allows non-ASCII characters
+// to be used in comments without potentially breaking OpenGL
+// implementations not expecting characters outside the GLSL ES set.
+class StripComments {
+public:
+    explicit StripComments(const nsAString& str)
+        : m_parseState(BeginningOfLine)
+        , m_end(str.EndReading())
+        , m_current(str.BeginReading())
+        , m_position(0)
     {
-        // Printing characters are valid except " $ ` @ \ ' DEL.
-        if (c >= 32 && c <= 126 &&
-            c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'')
-        {
-             return true;
-        }
-
-        // Horizontal tab, line feed, vertical tab, form feed, carriage return
-        // are also valid.
-        if (c >= 9 && c <= 13) {
-             return true;
-        }
-
-        return false;
+        m_result.SetLength(str.Length());
+        parse();
     }
 
-    // Strips comments from shader text. This allows non-ASCII characters
-    // to be used in comments without potentially breaking OpenGL
-    // implementations not expecting characters outside the GLSL ES set.
-    class StripComments {
-    public:
-        explicit StripComments(const nsAString& str)
-            : m_parseState(BeginningOfLine)
-            , m_end(str.EndReading())
-            , m_current(str.BeginReading())
-            , m_position(0)
-        {
-            m_result.SetLength(str.Length());
-            parse();
-        }
-
-        const nsTArray<char16_t>& result()
-        {
-            return m_result;
-        }
-
-        size_t length()
-        {
-            return m_position;
-        }
-
-    private:
-        bool hasMoreCharacters()
-        {
-            return (m_current < m_end);
-        }
-
-        void parse()
-        {
-            while (hasMoreCharacters()) {
-                process(current());
-                // process() might advance the position.
-                if (hasMoreCharacters())
-                    advance();
-            }
-        }
-
-        void process(char16_t);
+    const nsTArray<char16_t>& result()
+    {
+        return m_result;
+    }
 
-        bool peek(char16_t& character)
-        {
-            if (m_current + 1 >= m_end)
-                return false;
-            character = *(m_current + 1);
-            return true;
-        }
-
-        char16_t current()
-        {
-            //ASSERT(m_position < m_length);
-            return *m_current;
-        }
-
-        void advance()
-        {
-            ++m_current;
-        }
-
-        bool isNewline(char16_t character)
-        {
-            // Don't attempt to canonicalize newline related characters.
-            return (character == '\n' || character == '\r');
-        }
-
-        void emit(char16_t character)
-        {
-            m_result[m_position++] = character;
-        }
-
-        enum ParseState {
-            // Have not seen an ASCII non-whitespace character yet on
-            // this line. Possible that we might see a preprocessor
-            // directive.
-            BeginningOfLine,
-
-            // Have seen at least one ASCII non-whitespace character
-            // on this line.
-            MiddleOfLine,
-
-            // Handling a preprocessor directive. Passes through all
-            // characters up to the end of the line. Disables comment
-            // processing.
-            InPreprocessorDirective,
-
-            // Handling a single-line comment. The comment text is
-            // replaced with a single space.
-            InSingleLineComment,
+    size_t length()
+    {
+        return m_position;
+    }
 
-            // Handling a multi-line comment. Newlines are passed
-            // through to preserve line numbers.
-            InMultiLineComment
-        };
-
-        ParseState m_parseState;
-        const char16_t* m_end;
-        const char16_t* m_current;
-        size_t m_position;
-        nsTArray<char16_t> m_result;
-    };
-
-    void StripComments::process(char16_t c)
+private:
+    bool hasMoreCharacters()
     {
-        if (isNewline(c)) {
-            // No matter what state we are in, pass through newlines
-            // so we preserve line numbers.
-            emit(c);
-
-            if (m_parseState != InMultiLineComment)
-                m_parseState = BeginningOfLine;
-
-            return;
-        }
-
-        char16_t temp = 0;
-        switch (m_parseState) {
-        case BeginningOfLine:
-            // If it's an ASCII space.
-            if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) {
-                emit(c);
-                break;
-            }
-
-            if (c == '#') {
-                m_parseState = InPreprocessorDirective;
-                emit(c);
-                break;
-            }
-
-            // Transition to normal state and re-handle character.
-            m_parseState = MiddleOfLine;
-            process(c);
-            break;
+        return (m_current < m_end);
+    }
 
-        case MiddleOfLine:
-            if (c == '/' && peek(temp)) {
-                if (temp == '/') {
-                    m_parseState = InSingleLineComment;
-                    emit(' ');
-                    advance();
-                    break;
-                }
-
-                if (temp == '*') {
-                    m_parseState = InMultiLineComment;
-                    // Emit the comment start in case the user has
-                    // an unclosed comment and we want to later
-                    // signal an error.
-                    emit('/');
-                    emit('*');
-                    advance();
-                    break;
-                }
-            }
-
-            emit(c);
-            break;
-
-        case InPreprocessorDirective:
-            // No matter what the character is, just pass it
-            // through. Do not parse comments in this state. This
-            // might not be the right thing to do long term, but it
-            // should handle the #error preprocessor directive.
-            emit(c);
-            break;
-
-        case InSingleLineComment:
-            // The newline code at the top of this function takes care
-            // of resetting our state when we get out of the
-            // single-line comment. Swallow all other characters.
-            break;
-
-        case InMultiLineComment:
-            if (c == '*' && peek(temp) && temp == '/') {
-                emit('*');
-                emit('/');
-                m_parseState = MiddleOfLine;
+    void parse()
+    {
+        while (hasMoreCharacters()) {
+            process(current());
+            // process() might advance the position.
+            if (hasMoreCharacters())
                 advance();
-                break;
-            }
-
-            // Swallow all other characters. Unclear whether we may
-            // want or need to just emit a space per character to try
-            // to preserve column numbers for debugging purposes.
-            break;
         }
     }
 
+    void process(char16_t);
+
+    bool peek(char16_t& character)
+    {
+        if (m_current + 1 >= m_end)
+            return false;
+        character = *(m_current + 1);
+        return true;
+    }
+
+    char16_t current()
+    {
+        //ASSERT(m_position < m_length);
+        return *m_current;
+    }
+
+    void advance()
+    {
+        ++m_current;
+    }
+
+    bool isNewline(char16_t character)
+    {
+        // Don't attempt to canonicalize newline related characters.
+        return (character == '\n' || character == '\r');
+    }
+
+    void emit(char16_t character)
+    {
+        m_result[m_position++] = character;
+    }
+
+    enum ParseState {
+        // Have not seen an ASCII non-whitespace character yet on
+        // this line. Possible that we might see a preprocessor
+        // directive.
+        BeginningOfLine,
+
+        // Have seen at least one ASCII non-whitespace character
+        // on this line.
+        MiddleOfLine,
+
+        // Handling a preprocessor directive. Passes through all
+        // characters up to the end of the line. Disables comment
+        // processing.
+        InPreprocessorDirective,
+
+        // Handling a single-line comment. The comment text is
+        // replaced with a single space.
+        InSingleLineComment,
+
+        // Handling a multi-line comment. Newlines are passed
+        // through to preserve line numbers.
+        InMultiLineComment
+    };
+
+    ParseState m_parseState;
+    const char16_t* m_end;
+    const char16_t* m_current;
+    size_t m_position;
+    nsTArray<char16_t> m_result;
+};
+
 /****** END CODE TAKEN FROM WEBKIT ******/
 
+bool ValidateGLSLString(const nsAString& string, WebGLContext* webgl,
+                        const char* funcName);
+
+bool ValidateGLSLVariableName(const nsAString& name, WebGLContext* webgl,
+                              const char* funcName);
+
 } // namespace mozilla
 
 #endif // WEBGL_VALIDATE_STRINGS_H_
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -103,21 +103,23 @@ UNIFIED_SOURCES += [
     'WebGLFramebufferAttachable.cpp',
     'WebGLObjectModel.cpp',
     'WebGLProgram.cpp',
     'WebGLQuery.cpp',
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
+    'WebGLShaderValidator.cpp',
     'WebGLSync.cpp',
     'WebGLTexelConversions.cpp',
     'WebGLTexture.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
+    'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
 ]
 LOCAL_INCLUDES += [
     '/js/xpconnect/wrappers',
 ]
 
--- a/dom/canvas/test/webgl-conformance/conformance/misc/bad-arguments-test.html
+++ b/dom/canvas/test/webgl-conformance/conformance/misc/bad-arguments-test.html
@@ -63,25 +63,23 @@ var arguments = [
 
 var argument;
 
 function shouldBeEmptyString(command) {
   shouldBe(command, "''");
 }
 
 for (var i = 0; i < arguments.length; ++i) {
-  var func, func2, func3;
+  var func, func2;
   if (arguments[i].throws) {
     func = shouldThrow;
     func2 = shouldThrow;
-    func3 = shouldThrow;
   } else {
     func = shouldBeUndefined;
     func2 = shouldBeNull;
-    func3 = shouldBeEmptyString;
   }
   argument = arguments[i].value;
   func("context.compileShader(argument)");
   func("context.linkProgram(argument)");
   func("context.attachShader(program, argument)");
   func("context.attachShader(argument, shader)");
   func("context.detachShader(program, argument)");
   func("context.detachShader(argument, shader)");
@@ -93,25 +91,24 @@ for (var i = 0; i < arguments.length; ++
   func("context.bindRenderbuffer(context.RENDERBUFFER, argument)");
   func("context.bindTexture(context.TEXTURE_2D, argument)");
   func("context.framebufferRenderbuffer(context.FRAMEBUFFER, context.DEPTH_ATTACHMENT, context.RENDERBUFFER, argument)");
   func("context.framebufferTexture2D(context.FRAMEBUFFER, context.COLOR_ATTACHMENT0, context.TEXTURE_2D, argument, 0)");
   func("context.uniform2fv(argument, new Float32Array([0.0, 0.0]))");
   func("context.uniform2iv(argument, new Int32Array([0, 0]))");
   func("context.uniformMatrix2fv(argument, false, new Float32Array([0.0, 0.0, 0.0, 0.0]))");
 
+  func2("context.getProgramInfoLog(argument)");
   func2("context.getProgramParameter(argument, 0)");
+  func2("context.getShaderInfoLog(argument)");
   func2("context.getShaderParameter(argument, 0)");
+  func2("context.getShaderSource(argument)");
   func2("context.getUniform(argument, loc)");
   func2("context.getUniform(program, argument)");
   func2("context.getUniformLocation(argument, 'u_modelViewProjMatrix')");
-
-  func3("context.getProgramInfoLog(argument)");
-  func3("context.getShaderInfoLog(argument)");
-  func3("context.getShaderSource(argument)");
 }
 
 successfullyParsed = true;
 </script>
 
 <script>finishTest();</script>
 </body>
 </html>
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -2010,17 +2010,17 @@ public:
     }
 
     void fGetShaderSource(GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source) {
         BEFORE_GL_CALL;
         mSymbols.fGetShaderSource(obj, maxLength, length, source);
         AFTER_GL_CALL;
     }
 
-    void fShaderSource(GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths) {
+    void fShaderSource(GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths) {
         BEFORE_GL_CALL;
         mSymbols.fShaderSource(shader, count, strings, lengths);
         AFTER_GL_CALL;
     }
 
 private:
     void raw_fBindFramebuffer(GLenum target, GLuint framebuffer) {
         BEFORE_GL_CALL;
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -303,17 +303,17 @@ struct GLContextSymbols
     typedef void (GLAPIENTRY * PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint* param);
     PFNGLGETSHADERIVPROC fGetShaderiv;
     typedef void (GLAPIENTRY * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
     PFNGLGETSHADERINFOLOGPROC fGetShaderInfoLog;
     typedef void (GLAPIENTRY * PFNGETSHADERPRECISIONFORMAT) (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
     PFNGETSHADERPRECISIONFORMAT fGetShaderPrecisionFormat;
     typedef void (GLAPIENTRY * PFNGLGETSHADERSOURCEPROC) (GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source);
     PFNGLGETSHADERSOURCEPROC fGetShaderSource;
-    typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths);
+    typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const* strings, const GLint* lengths);
     PFNGLSHADERSOURCEPROC fShaderSource;
 
     typedef void (GLAPIENTRY * PFNGLBINDFRAMEBUFFER) (GLenum target, GLuint framebuffer);
     PFNGLBINDFRAMEBUFFER fBindFramebuffer;
     typedef void (GLAPIENTRY * PFNGLBINDRENDERBUFFER) (GLenum target, GLuint renderbuffer);
     PFNGLBINDRENDERBUFFER fBindRenderbuffer;
     typedef GLenum (GLAPIENTRY * PFNGLCHECKFRAMEBUFFERSTATUS) (GLenum target);
     PFNGLCHECKFRAMEBUFFERSTATUS fCheckFramebufferStatus;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3836,30 +3836,30 @@ pref("image.onload.decode.limit", 0);
 #ifdef ANDROID
 // Disable MSAA on mobile.
 pref("gl.msaa-level", 0);
 #else
 pref("gl.msaa-level", 2);
 #endif
 pref("webgl.force-enabled", false);
 pref("webgl.disabled", false);
-pref("webgl.shader_validator", true);
 pref("webgl.disable-angle", false);
 pref("webgl.min_capability_mode", false);
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
 pref("webgl.force-layers-readback", false);
 pref("webgl.lose-context-on-memory-pressure", false);
 pref("webgl.can-lose-context-in-foreground", true);
 pref("webgl.restore-context-when-visible", true);
 pref("webgl.max-warnings-per-context", 32);
 pref("webgl.enable-draft-extensions", false);
 pref("webgl.enable-privileged-extensions", false);
+pref("webgl.bypass-shader-validation", false);
 #ifdef XP_WIN
 pref("webgl.angle.try-d3d11", true);
 pref("webgl.angle.force-d3d11", false);
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 pref("gfx.gralloc.fence-with-readpixels", false);
 #endif