Bug 1109945 - Rewrite shader/program handling. - r=kamidphish
☠☠ backed out by 6dab2820469a ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Fri, 09 Jan 2015 18:40:56 -0800
changeset 239839 ca411b1cf0019393548598a66af9e08a711cd6f8
parent 239838 203e26771e770b3d072e2e3f2e5bb7ca2ca93555
child 239840 d4be320ebecbc710f8f19950a4d7cf0e69075d92
push id7472
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 20:36:27 +0000
treeherdermozilla-aurora@300ca104f8fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs1109945
milestone37.0a1
Bug 1109945 - Rewrite shader/program handling. - r=kamidphish
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/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;
     mBoundFramebuffer = 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();
@@ -369,17 +374,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,
@@ -830,18 +837,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);
@@ -1111,18 +1120,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;
@@ -1137,16 +1147,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
@@ -1218,20 +1232,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,
@@ -1268,16 +1278,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.
@@ -1396,16 +1410,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;
 
     WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback;
     WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;
 
@@ -1585,17 +1600,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);
 
 const WebGLRectangleObject*
 WebGLContext::CurValidFBRectObject() const
 {
     const WebGLRectangleObject* rect = nullptr;
 
     if (mBoundFramebuffer) {
         // We don't really need to ask the driver.
@@ -104,63 +101,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;
 
@@ -775,24 +743,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;
 
@@ -880,60 +849,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;
@@ -989,119 +914,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();
 
@@ -1435,110 +1309,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)
@@ -1736,181 +1533,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;
 
@@ -1980,175 +1640,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;
 
@@ -2777,17 +2299,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)
@@ -2880,17 +2402,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,
@@ -2900,18 +2422,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);
 }
 
@@ -2923,19 +2445,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);
 }
 
@@ -2947,20 +2469,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);
 }
 
@@ -3089,54 +2611,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;
 
@@ -3179,315 +2688,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)
@@ -3609,91 +2820,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;
 
@@ -3737,61 +2890,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,
@@ -4259,87 +3402,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
@@ -1026,17 +1026,17 @@ WebGLContext::AssertCachedBindings()
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
     // Bound object state
     GLuint bound = mBoundFramebuffer ? mBoundFramebuffer->GLName() : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
 
-    bound = mCurrentProgram ? mCurrentProgram->GLName() : 0;
+    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(GLenum attachment,
                                             const char* funcName)
 {
@@ -1508,261 +1465,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
@@ -3823,30 +3823,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