Simplify shader validation.
authorJeff Gilbert <jgilbert@mozilla.com>
Fri, 10 Mar 2017 21:23:39 -0800
changeset 1064480 c7fc66c947aa87e9f0f1c35ae04b618b71a25d4f
parent 1058372 3d341b9ba5353b6b8ab45b6ca03dcb1b2d789faa
child 1064481 7534722e4c5cf7bfb6e97abb72f25ae86c17a00e
push id176034
push userjgilbert@mozilla.com
push dateMon, 13 Mar 2017 23:57:56 +0000
treeherdertry@7534722e4c5c [default view] [failures only]
milestone55.0a1
Simplify shader validation. MozReview-Commit-ID: ExiwlrUCtrA
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLExtensionDrawBuffers.cpp
dom/canvas/WebGLExtensionFragDepth.cpp
dom/canvas/WebGLExtensionShaderTextureLod.cpp
dom/canvas/WebGLExtensionStandardDerivatives.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
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -66,16 +66,17 @@
 #include "WebGLExtensions.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLMemoryTracker.h"
 #include "WebGLObjectModel.h"
 #include "WebGLProgram.h"
 #include "WebGLQuery.h"
 #include "WebGLSampler.h"
 #include "WebGLShader.h"
+#include "WebGLShaderValidator.h"
 #include "WebGLSync.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #ifdef MOZ_WIDGET_COCOA
 #include "nsCocoaFeatures.h"
 #endif
@@ -2407,16 +2408,34 @@ WebGLContext::ValidateArrayBufferView(co
         elemCount = elemCountOverride;
     }
 
     *out_bytes = bytes + (elemOffset * elemSize);
     *out_byteLen = elemCount * elemSize;
     return true;
 }
 
+////
+
+void
+WebGLContext::ResetShaderValidator() const
+{
+    mShaderValidator = nullptr;
+}
+
+const webgl::ShaderValidator*
+WebGLContext::ShaderValidator() const
+{
+    if (mBypassShaderValidation)
+        return nullptr;
+
+    mShaderValidator.reset(new webgl::ShaderValidator(this));
+    return mShaderValidator.get();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags)
 {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -118,16 +118,17 @@ template<typename> struct Nullable;
 
 namespace gfx {
 class SourceSurface;
 class VRLayerChild;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
+struct ShaderInfo;
 class ShaderValidator;
 class TexUnpackBlob;
 struct UniformInfo;
 struct UniformBlockInfo;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
@@ -311,16 +312,18 @@ class WebGLContext
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
+    friend struct webgl::ShaderInfo;
+    friend class webgl::ShaderValidator;
     friend struct webgl::UniformBlockInfo;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
         BROWSER_DEFAULT_WEBGL = 0x9244,
@@ -1442,18 +1445,22 @@ protected:
     // glGetError sources:
     bool mEmitContextLostErrorOnce;
     GLenum mWebGLError;
     GLenum mUnderlyingGLError;
     GLenum GetAndFlushUnderlyingGLErrors();
 
     bool mBypassShaderValidation;
 
-    webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
+    mutable UniquePtr<const webgl::ShaderValidator> mShaderValidator;
+public:
+    void ResetShaderValidator() const;
+    const webgl::ShaderValidator* ShaderValidator() const;
 
+protected:
     // some GL constants
     uint32_t mGLMaxVertexAttribs;
     int32_t mGLMaxTextureUnits;
     int32_t mGLMaxTextureImageUnits;
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
--- a/dom/canvas/WebGLExtensionDrawBuffers.cpp
+++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp
@@ -21,16 +21,18 @@ WebGLExtensionDrawBuffers::WebGLExtensio
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
     // WEBGL_draw_buffers:
     // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
     //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
     webgl->mImplMaxColorAttachments = webgl->mGLMaxColorAttachments;
     webgl->mImplMaxDrawBuffers = std::min(webgl->mGLMaxDrawBuffers,
                                           webgl->mImplMaxColorAttachments);
+
+    webgl->ResetShaderValidator();
 }
 
 WebGLExtensionDrawBuffers::~WebGLExtensionDrawBuffers()
 {
 }
 
 void
 WebGLExtensionDrawBuffers::DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers)
--- a/dom/canvas/WebGLExtensionFragDepth.cpp
+++ b/dom/canvas/WebGLExtensionFragDepth.cpp
@@ -10,16 +10,17 @@
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionFragDepth::WebGLExtensionFragDepth(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
+    webgl->ResetShaderValidator();
 }
 
 WebGLExtensionFragDepth::~WebGLExtensionFragDepth()
 {
 }
 
 bool
 WebGLExtensionFragDepth::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionShaderTextureLod.cpp
+++ b/dom/canvas/WebGLExtensionShaderTextureLod.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionShaderTextureLod::WebGLExtensionShaderTextureLod(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
+    webgl->ResetShaderValidator();
 }
 
 WebGLExtensionShaderTextureLod::~WebGLExtensionShaderTextureLod()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionShaderTextureLod, EXT_shader_texture_lod)
 
--- a/dom/canvas/WebGLExtensionStandardDerivatives.cpp
+++ b/dom/canvas/WebGLExtensionStandardDerivatives.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionStandardDerivatives::WebGLExtensionStandardDerivatives(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
+    webgl->ResetShaderValidator();
 }
 
 WebGLExtensionStandardDerivatives::~WebGLExtensionStandardDerivatives()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionStandardDerivatives, OES_standard_derivatives)
 
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "nsPrintfCString.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLContext.h"
 #include "WebGLShader.h"
+#include "WebGLShaderValidator.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
 /* If `name`: "foo[3]"
  * Then returns true, with
@@ -170,283 +171,258 @@ webgl::UniformInfo::UniformInfo(WebGLAct
         mSamplerValues.assign(mActiveInfo->mElemCount, 0);
     }
 }
 
 //////////
 
 //#define DUMP_SHADERVAR_MAPPINGS
 
-static already_AddRefed<const webgl::LinkedProgramInfo>
-QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
+RefPtr<const webgl::LinkedProgramInfo>
+WebGLProgram::GatherLinkInfo() const
 {
-    WebGLContext* const webgl = prog->mContext;
-
-    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;
+    const auto& gl = mContext->gl;
+    RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(this));
 
-    GLuint maxUniformLenWithNull = 0;
-    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
-                      (GLint*)&maxUniformLenWithNull);
-    if (maxUniformLenWithNull < 1)
-        maxUniformLenWithNull = 1;
+    const auto& vertInfo = mVertShader->mCompileInfo;
+    const auto& fragInfo = mFragShader->mCompileInfo;
 
-    GLuint maxUniformBlockLenWithNull = 0;
-    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
-        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
-                          (GLint*)&maxUniformBlockLenWithNull);
-        if (maxUniformBlockLenWithNull < 1)
-            maxUniformBlockLenWithNull = 1;
+    std::map<std::string, const std::string> unmapName;
+    if (vertInfo) {
+        MOZ_ASSERT(fragInfo);
+        unmapName = vertInfo->unmapName;
+        for (const auto& pair : fragInfo->unmapName) {
+            unmapName.insert(pair);
+        }
     }
 
-    GLuint maxTransformFeedbackVaryingLenWithNull = 0;
+    const auto fnUnmapName = [&](const std::string& mappedName) {
+        return webgl::ShaderInfo::MapNameWith(mappedName, unmapName);
+    };
+
+    const auto fnGetProgInt = [&](GLenum pname) {
+        GLuint cur = 0;
+        gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, (GLint*)&cur);
+        return cur;
+    };
+
+    ////
+
+    std::vector<char> nameBuffer;
+    nameBuffer.reserve(256);
+
+    nameBuffer.reserve(fnGetProgInt(LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH));
+    nameBuffer.reserve(fnGetProgInt(LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH));
+
+    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
+        nameBuffer.reserve(fnGetProgInt(LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH));
+    }
+
     if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
-        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
-                          (GLint*)&maxTransformFeedbackVaryingLenWithNull);
-        if (maxTransformFeedbackVaryingLenWithNull < 1)
-            maxTransformFeedbackVaryingLenWithNull = 1;
+        nameBuffer.reserve(fnGetProgInt(LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH));
     }
 
     // Attribs (can't be arrays)
 
-    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);
-
+    auto numActive = fnGetProgInt(LOCAL_GL_ACTIVE_ATTRIBUTES);
+    for (GLuint i = 0; i < numActive; i++) {
         GLsizei lengthWithoutNull = 0;
         GLint elemCount = 0; // `size`
         GLenum elemType = 0; // `type`
-        gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
-                             &elemCount, &elemType, mappedName.BeginWriting());
-        GLenum error = gl->fGetError();
-        if (error != LOCAL_GL_NO_ERROR) {
-            gfxCriticalNote << "Failed to do glGetActiveAttrib: " << error;
-        }
+        gl->fGetActiveAttrib(mGLName, i, nameBuffer.capacity(), &lengthWithoutNull,
+                             &elemCount, &elemType, nameBuffer.data());
 
-        mappedName.SetLength(lengthWithoutNull);
+        const std::string mappedName(nameBuffer.data(), lengthWithoutNull);
+        const auto userName = fnUnmapName(mappedName);
 
         ////
 
-        nsCString userName;
-        if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) {
-            userName = mappedName;
-        }
-
-        ///////
-
-        GLint loc = gl->fGetAttribLocation(prog->mGLName,
-                                           mappedName.BeginReading());
+        auto loc = gl->fGetAttribLocation(mGLName, mappedName.c_str());
         if (gl->WorkAroundDriverBugs() &&
-            mappedName.EqualsIgnoreCase("gl_", 3))
+            mappedName.find("gl_") == 0)
         {
             // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or Win+GL.
             loc = -1;
         }
 #ifdef DUMP_SHADERVAR_MAPPINGS
-        printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActiveAttribs, loc,
-                      userName.BeginReading(), mappedName.BeginReading());
+        printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActive, loc, userName.c_str(),
+                      mappedName.c_str());
 #endif
-        MOZ_ASSERT_IF(mappedName.EqualsIgnoreCase("gl_", 3), loc == -1);
 
         ///////
 
         const bool isArray = false;
-        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
-                                                                       elemType, isArray,
-                                                                       userName,
-                                                                       mappedName);
+        const RefPtr<WebGLActiveInfo> activeInfo(new WebGLActiveInfo( mContext, elemCount,
+                                                                      elemType, isArray,
+                                                                      nsCString(userName.c_str()),
+                                                                      nsCString(mappedName.c_str()) ));
         const GLenum baseType = AttribBaseType(elemType);
         const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
         info->attribs.push_back(attrib);
     }
 
     // Uniforms (can be basically anything)
 
     const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
 
-    GLuint numActiveUniforms = 0;
-    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
-                      (GLint*)&numActiveUniforms);
+    const auto fnIsArrayName = [&](const std::string& name) {
+        if (name.size() < 3)
+            return false;
+        return (name.substr(name.size()-3) == "[0]");
+    };
 
-    for (GLuint i = 0; i < numActiveUniforms; i++) {
-        nsAutoCString mappedName;
-        mappedName.SetLength(maxUniformLenWithNull - 1);
-
+    numActive = fnGetProgInt(LOCAL_GL_ACTIVE_UNIFORMS);
+    for (GLuint i = 0; i < numActive; i++) {
         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);
-
-        ///////
+        gl->fGetActiveUniform(mGLName, i, nameBuffer.capacity(), &lengthWithoutNull,
+                              &elemCount, &elemType, nameBuffer.data());
 
-        nsAutoCString baseMappedName;
-        bool isArray;
-        size_t arrayIndex;
-        if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
-            MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
+        std::string mappedName(nameBuffer.data(), lengthWithoutNull);
 
-        // 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)) {
-            // Validator likely missing.
-            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 mappedNameStr = baseMappedName.BeginReading();
-                mappedNameStr += "[0]";
-
-                GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
-                if (loc != -1)
-                    isArray = true;
+        bool isArray = fnIsArrayName(mappedName);
+        if (!isArray && needsCheckForArrays) {
+            const std::string mappedNameIfArr = mappedName + "[0]";
+            const auto loc = gl->fGetUniformLocation(mGLName, mappedNameIfArr.c_str());
+            if (loc != -1) {
+                isArray = true;
+                mappedName = mappedNameIfArr;
             }
         }
 
+        const auto userName = fnUnmapName(mappedName);
+
         ///////
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
-        printf_stderr("[uniform %u/%u] %s->%s\n", i, numActiveUniforms,
-                      baseUserName.BeginReading(), mappedName.BeginReading());
+        printf_stderr("[uniform %u/%u] %s->%s\n", i, numActive, userName.c_str(),
+                      mappedName.c_str());
 #endif
 
         ///////
 
-        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
-                                                                       elemType, isArray,
-                                                                       baseUserName,
-                                                                       baseMappedName);
+        auto baseUserName = userName;
+        auto baseMappedName = mappedName;
+        if (isArray) {
+            // Remove the trailing "[0]".
+            baseUserName.resize(baseUserName.size() - 3);
+            baseMappedName.resize(baseMappedName.size() - 3);
+        }
+
+        const RefPtr<WebGLActiveInfo> activeInfo(new WebGLActiveInfo( mContext, elemCount,
+                                                                      elemType, isArray,
+                                                                      nsCString(baseUserName.c_str()),
+                                                                      nsCString(baseMappedName.c_str()) ));
 
         auto* uniform = new webgl::UniformInfo(activeInfo);
         info->uniforms.push_back(uniform);
 
         if (uniform->mSamplerTexList) {
             info->uniformSamplers.push_back(uniform);
         }
     }
 
     // Uniform Blocks (can be arrays, but can't contain sampler types)
 
     if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
-        GLuint numActiveUniformBlocks = 0;
-        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
-                          (GLint*)&numActiveUniformBlocks);
-
-        for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
-            nsAutoCString mappedName;
-            mappedName.SetLength(maxUniformBlockLenWithNull - 1);
-
+        numActive = fnGetProgInt(LOCAL_GL_ACTIVE_UNIFORM_BLOCKS);
+        for (GLuint i = 0; i < numActive; i++) {
             GLint lengthWithoutNull;
-            gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
-            gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
-            mappedName.SetLength(lengthWithoutNull);
+            gl->fGetActiveUniformBlockName(mGLName, i, nameBuffer.capacity(),
+                                           &lengthWithoutNull, nameBuffer.data());
 
-            ////
-
-            nsCString userName;
-            if (!prog->UnmapUniformBlockName(mappedName, &userName))
-                continue;
+            const std::string mappedName(nameBuffer.data(), lengthWithoutNull);
+            const auto userName = fnUnmapName(mappedName);
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
-            printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActiveUniformBlocks,
-                          userName.BeginReading(), mappedName.BeginReading());
+            printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActive,
+                          userName.c_str(), mappedName.c_str());
 #endif
 
             ////
 
             GLuint dataSize = 0;
-            gl->fGetActiveUniformBlockiv(prog->mGLName, i,
-                                         LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
+            gl->fGetActiveUniformBlockiv(mGLName, i, LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
                                          (GLint*)&dataSize);
 
 
-            auto* block = new webgl::UniformBlockInfo(webgl, userName, mappedName,
+            auto* block = new webgl::UniformBlockInfo(mContext,
+                                                      nsCString(userName.c_str()),
+                                                      nsCString(mappedName.c_str()),
                                                       dataSize);
             info->uniformBlocks.push_back(block);
         }
     }
 
     // Transform feedback varyings (can be arrays)
 
     if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
-        GLuint numTransformFeedbackVaryings = 0;
-        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
-                          (GLint*)&numTransformFeedbackVaryings);
-
-        for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
-            nsAutoCString mappedName;
-            mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
-
+        numActive = fnGetProgInt(LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS);
+        for (GLuint i = 0; i < numActive; i++) {
             GLint lengthWithoutNull;
             GLsizei elemCount;
             GLenum elemType;
-            gl->fGetTransformFeedbackVarying(prog->mGLName, i,
-                                             maxTransformFeedbackVaryingLenWithNull,
+            gl->fGetTransformFeedbackVarying(mGLName, i, nameBuffer.capacity(),
                                              &lengthWithoutNull, &elemCount, &elemType,
-                                             mappedName.BeginWriting());
-            mappedName.SetLength(lengthWithoutNull);
+                                             nameBuffer.data());
+
+            const std::string mappedName(nameBuffer.data(), lengthWithoutNull);
+            const auto userName = fnUnmapName(mappedName);
 
             ////
 
-            nsAutoCString baseMappedName;
-            bool isArray;
-            size_t arrayIndex;
-            if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
-                MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
-
-            nsAutoCString baseUserName;
-            if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
-                baseUserName = baseMappedName;
+            auto baseUserName = userName;
+            auto baseMappedName = mappedName;
+            const bool isArray = fnIsArrayName(mappedName);
+            if (isArray) {
+                MOZ_ASSERT(fnIsArrayName(userName));
+                // Remove the trailing "[0]".
+                baseUserName.resize(baseUserName.size() - 3);
+                baseMappedName.resize(baseMappedName.size() - 3);
             }
 
             ////
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
-            printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i,
-                          numTransformFeedbackVaryings, baseUserName.BeginReading(),
-                          mappedName.BeginReading());
+            printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i, numActive,
+                          userName.c_str(), mappedName.c_str());
 #endif
 
-            const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl,
-                                                                           elemCount,
-                                                                           elemType,
-                                                                           isArray,
-                                                                           baseUserName,
-                                                                           mappedName);
+            const RefPtr<WebGLActiveInfo> activeInfo(new WebGLActiveInfo( mContext, elemCount,
+                                                                          elemType, isArray,
+                                                                          nsCString(baseUserName.c_str()),
+                                                                          nsCString(baseMappedName.c_str()) ));
             info->transformFeedbackVaryings.push_back(activeInfo);
         }
     }
 
     // Frag outputs
 
-    prog->EnumerateFragOutputs(info->fragDataMap);
+    if (fragInfo) {
+        GLuint i = 0;
+        for (const auto& cur : fragInfo->outputs) {
+            info->fragDataMap.insert({ nsCString(cur.name.c_str()),
+                                       nsCString(cur.mappedName.c_str()) });
+#ifdef DUMP_SHADERVAR_MAPPINGS
+            printf_stderr("[frag data %u/%u] %s->%s\n", i, fragInfo->outputs.size(),
+                          cur.name.c_str(), cur.mappedName.c_str());
+#endif
+            ++i;
+        }
+    }
 
-    return info.forget();
+    return info;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
+webgl::LinkedProgramInfo::LinkedProgramInfo(const WebGLProgram* prog)
     : prog(prog)
     , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
 { }
 
 webgl::LinkedProgramInfo::~LinkedProgramInfo()
 {
     for (auto& cur : uniforms) {
         delete cur;
@@ -650,18 +626,26 @@ WebGLProgram::GetAttribLocation(const ns
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
         return -1;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
 
-    const webgl::AttribInfo* info;
-    if (!LinkInfo()->FindAttrib(userName, &info))
+    // VS inputs cannot be arrays or structures.
+    // `userName` is thus always `baseUserName`.
+    const webgl::AttribInfo* info = nullptr;
+    for (const auto& attrib : LinkInfo()->attribs) {
+        if (attrib.mActiveInfo->mBaseUserName == userName) {
+            info = &attrib;
+            break;
+        }
+    }
+    if (!info)
         return -1;
 
     return GLint(info->mLoc);
 }
 
 static GLint
 GetFragDataByUserName(const WebGLProgram* prog,
                       const nsCString& userName)
@@ -1023,40 +1007,22 @@ WebGLProgram::ValidateForLink()
         return false;
     }
 
     if (!mFragShader || !mFragShader->IsCompiled()) {
         mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
         return false;
     }
 
-    if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog))
-        return false;
-
-    const auto& gl = mContext->gl;
-
-    if (gl->WorkAroundDriverBugs() &&
-        mContext->mIsMesa)
-    {
-        // 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 (numSamplerUniforms_upperBound > 16) {
-            mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
-                                   " Mesa drivers to avoid crashing.");
+    const auto& vertInfo = mVertShader->mCompileInfo;
+    const auto& fragInfo = mFragShader->mCompileInfo;
+    if (vertInfo) {
+        MOZ_ASSERT(fragInfo);
+        if (!fragInfo->CanLinkToVert(*vertInfo, mContext, &mLinkLog))
             return false;
-        }
-
-        // Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
-        if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
-            mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max"
-                                   " attribute count.");
-            return false;
-        }
     }
 
     return true;
 }
 
 void
 WebGLProgram::LinkProgram()
 {
@@ -1078,33 +1044,53 @@ WebGLProgram::LinkProgram()
 
     if (!ValidateForLink()) {
         mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
         return;
     }
 
     // Bind the attrib locations.
     // This can't be done trivially, because we have to deal with mapped attrib names.
-    for (const auto& pair : mNextLink_BoundAttribLocs) {
-        const auto& name = pair.first;
-        const auto& index = pair.second;
+    const auto& vertInfo = mVertShader->mCompileInfo;
+    if (vertInfo) {
+        for (const auto& cur : vertInfo->attribs) {
+            const auto itr = mNextLink_BoundAttribLocs.find(nsCString(cur.name.c_str()));
+            if (itr == mNextLink_BoundAttribLocs.end())
+                continue;
 
-        mVertShader->BindAttribLocation(mGLName, name, index);
+            const auto& index = itr->second;
+            mContext->gl->fBindAttribLocation(mGLName, index, cur.mappedName.c_str());
+        }
+    } else {
+        for (const auto& pair : mNextLink_BoundAttribLocs) {
+            const auto& name = pair.first;
+            const auto& index = pair.second;
+            mContext->gl->fBindAttribLocation(mGLName, index, name.BeginReading());
+        }
     }
 
     // Storage for transform feedback varyings before link.
     // (Work around for bug seen on nVidia drivers.)
     std::vector<std::string> scopedMappedTFVaryings;
 
     if (mContext->IsWebGL2()) {
-        mVertShader->MapTransformFeedbackVaryings(mNextLink_TransformFeedbackVaryings,
-                                                  &scopedMappedTFVaryings);
+        scopedMappedTFVaryings.reserve(mNextLink_TransformFeedbackVaryings.size());
+        for (const auto& name : mNextLink_TransformFeedbackVaryings) {
+            const std::string userName(name.BeginReading());
+            std::string mappedName;
+            if (vertInfo) {
+                mappedName = vertInfo->MapName(userName);
+            } else {
+                mappedName = userName;
+            }
+            scopedMappedTFVaryings.push_back(mappedName);
+        }
 
         std::vector<const char*> driverVaryings;
-        driverVaryings.reserve(scopedMappedTFVaryings.size());
+        driverVaryings.reserve(mNextLink_TransformFeedbackVaryings.size());
         for (const auto& cur : scopedMappedTFVaryings) {
             driverVaryings.push_back(cur.c_str());
         }
 
         mContext->gl->fTransformFeedbackVaryings(mGLName, driverVaryings.size(),
                                                  driverVaryings.data(),
                                                  mNextLink_TransformFeedbackBufferMode);
     }
@@ -1278,66 +1264,63 @@ WebGLProgram::ValidateAfterTentativeLink
             break;
 
         default:
             MOZ_CRASH("`bufferMode`");
         }
 
         std::vector<size_t> componentsPerVert;
         std::set<const WebGLActiveInfo*> alreadyUsed;
-        for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
+        for (const auto& userName : mNextLink_TransformFeedbackVaryings) {
             if (!componentsPerVert.size() ||
                 mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS)
             {
                 componentsPerVert.push_back(0);
             }
 
             ////
 
             const WebGLActiveInfo* curInfo = nullptr;
             for (const auto& info : linkInfo->transformFeedbackVaryings) {
-                const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
-                if (info_wideUserName == wideUserName) {
+                if (info->mBaseUserName == userName) {
                     curInfo = info.get();
                     break;
                 }
             }
 
             if (!curInfo) {
-                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
                 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\" not"
                                                " found.",
-                                               asciiUserName.BeginReading());
+                                               userName.BeginReading());
                 return false;
             }
 
             const auto insertResPair = alreadyUsed.insert(curInfo);
             const auto& didInsert = insertResPair.second;
+            MOZ_ALWAYS_TRUE(didInsert);
             if (!didInsert) {
-                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
                 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
                                                " specified twice.",
-                                               asciiUserName.BeginReading());
+                                               userName.BeginReading());
                 return false;
             }
 
             ////
 
             size_t varyingComponents = NumComponents(curInfo->mElemType);
             varyingComponents *= curInfo->mElemCount;
 
             auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
             totalComponentsForIndex += varyingComponents;
 
             if (totalComponentsForIndex > maxComponentsPerIndex) {
-                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
                 *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
                                                " pushed `componentsForIndex` over the"
                                                " limit of %u.",
-                                               asciiUserName.BeginReading(),
+                                               userName.BeginReading(),
                                                maxComponentsPerIndex);
                 return false;
             }
         }
 
         linkInfo->componentsPerTFVert.swap(componentsPerVert);
     }
 
@@ -1412,92 +1395,65 @@ WebGLProgram::LinkAndUpdate()
         mLinkLog.SetLength(0);
     }
 
     GLint ok = 0;
     gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
     if (!ok)
         return;
 
-    mMostRecentLinkInfo = QueryProgramInfo(this, gl);
+    mMostRecentLinkInfo = GatherLinkInfo();
     MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
 }
 
-bool
-WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
-                                             nsCString* const out_userName) const
-{
-    if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
-        return true;
-
-    return false;
-}
-
-bool
-WebGLProgram::FindVaryingByMappedName(const nsACString& mappedName,
-                                              nsCString* const out_userName,
-                                              bool* const out_isArray) const
-{
-    if (mVertShader->FindVaryingByMappedName(mappedName, out_userName, out_isArray))
-        return true;
-
-    return false;
-}
-
-
-bool
-WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
-                                      nsCString* const out_userName,
-                                      bool* const out_isArray) const
-{
-    if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
-        return true;
-
-    if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
-        return true;
-
-    return false;
-}
-
 void
-WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
+WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& wide_varyings,
                                         GLenum bufferMode)
 {
     const char funcName[] = "transformFeedbackVaryings";
 
     const auto& gl = mContext->gl;
     gl->MakeCurrent();
 
     switch (bufferMode) {
     case LOCAL_GL_INTERLEAVED_ATTRIBS:
         break;
 
     case LOCAL_GL_SEPARATE_ATTRIBS:
         {
             GLuint maxAttribs = 0;
             gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
                              &maxAttribs);
-            if (varyings.Length() > maxAttribs) {
+            if (wide_varyings.Length() > maxAttribs) {
                 mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
                                             funcName,
                                             "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
                 return;
             }
         }
         break;
 
     default:
         mContext->ErrorInvalidEnum("%s: Bad `bufferMode`: 0x%04x.", funcName, bufferMode);
         return;
     }
 
     ////
 
-    mNextLink_TransformFeedbackVaryings.assign(varyings.Elements(),
-                                               varyings.Elements() + varyings.Length());
+    std::vector<nsCString> varyings;
+    varyings.reserve(wide_varyings.Length());
+    for (const auto& cur : wide_varyings) {
+        if (!ValidateGLSLVariableName(cur, mContext, funcName))
+            return;
+        varyings.push_back(NS_LossyConvertUTF16toASCII(cur));
+    }
+
+    ////
+
+    mNextLink_TransformFeedbackVaryings = Move(varyings);
     mNextLink_TransformFeedbackBufferMode = bufferMode;
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLProgram::GetTransformFeedbackVarying(GLuint index) const
 {
     // No docs in the WebGL 2 spec for this function. Taking the language for
     // getActiveAttrib, which states that the function returns null on any error.
@@ -1512,73 +1468,19 @@ WebGLProgram::GetTransformFeedbackVaryin
                                     "equal to TRANSFORM_FEEDBACK_VARYINGS.");
         return nullptr;
     }
 
     RefPtr<WebGLActiveInfo> ret = LinkInfo()->transformFeedbackVaryings[index];
     return ret.forget();
 }
 
-bool
-WebGLProgram::UnmapUniformBlockName(const nsCString& mappedName,
-                                    nsCString* const out_userName) const
-{
-    nsCString baseMappedName;
-    bool isArray;
-    size_t arrayIndex;
-    if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
-        return false;
-
-    nsCString baseUserName;
-    if (!mVertShader->UnmapUniformBlockName(baseMappedName, &baseUserName) &&
-        !mFragShader->UnmapUniformBlockName(baseMappedName, &baseUserName))
-    {
-        return false;
-    }
-
-    AssembleName(baseUserName, isArray, arrayIndex, out_userName);
-    return true;
-}
-
-void
-WebGLProgram::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
-{
-    MOZ_ASSERT(mFragShader);
-
-    mFragShader->EnumerateFragOutputs(out_FragOutputs);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 bool
-IsBaseName(const nsCString& name)
-{
-    if (!name.Length())
-        return true;
-
-    return name[name.Length() - 1] != ']'; // Doesn't end in ']'.
-}
-
-bool
-webgl::LinkedProgramInfo::FindAttrib(const nsCString& userName,
-                                     const webgl::AttribInfo** const out) const
-{
-    // VS inputs cannot be arrays or structures.
-    // `userName` is thus always `baseUserName`.
-    for (const auto& attrib : attribs) {
-        if (attrib.mActiveInfo->mBaseUserName == userName) {
-            *out = &attrib;
-            return true;
-        }
-    }
-
-    return false;
-}
-
-bool
 webgl::LinkedProgramInfo::FindUniform(const nsCString& userName,
                                       nsCString* const out_mappedName,
                                       size_t* const out_arrayIndex,
                                       webgl::UniformInfo** const out_info) const
 {
     nsCString baseUserName;
     bool isArray;
     size_t arrayIndex;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -24,17 +24,16 @@ namespace mozilla {
 class ErrorResult;
 class WebGLActiveInfo;
 class WebGLProgram;
 class WebGLShader;
 class WebGLUniformLocation;
 
 namespace dom {
 template<typename> struct Nullable;
-class OwningUnsignedLongOrUint32ArrayOrBoolean;
 template<typename> class Sequence;
 } // namespace dom
 
 namespace webgl {
 
 struct AttribInfo final
 {
     const RefPtr<WebGLActiveInfo> mActiveInfo;
@@ -81,17 +80,17 @@ struct LinkedProgramInfo final
 {
     friend class WebGLProgram;
 
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
 
     //////
 
-    WebGLProgram* const prog;
+    const WebGLProgram* const prog;
     const GLenum transformFeedbackBufferMode;
 
     std::vector<AttribInfo> attribs;
     std::vector<UniformInfo*> uniforms; // Owns its contents.
     std::vector<UniformBlockInfo*> uniformBlocks; // Owns its contents.
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
     // Needed for draw call validation.
@@ -99,20 +98,19 @@ struct LinkedProgramInfo final
 
     mutable std::vector<size_t> componentsPerTFVert;
 
     //////
 
     // The maps for the frag data names to the translated names.
     std::map<nsCString, const nsCString> fragDataMap;
 
-    explicit LinkedProgramInfo(WebGLProgram* prog);
+    explicit LinkedProgramInfo(const WebGLProgram* prog);
     ~LinkedProgramInfo();
 
-    bool FindAttrib(const nsCString& userName, const AttribInfo** const out_info) const;
     bool FindUniform(const nsCString& userName, nsCString* const out_mappedName,
                      size_t* const out_arrayIndex, UniformInfo** const out_info) const;
     bool MapFragDataName(const nsCString& userName,
                          nsCString* const out_mappedName) const;
 };
 
 } // namespace webgl
 
@@ -154,33 +152,22 @@ public:
     void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const;
 
     void LinkProgram();
     bool UseProgram() const;
     void ValidateProgram() const;
 
     ////////////////
 
-    bool FindAttribUserNameByMappedName(const nsACString& mappedName,
-                                        nsCString* const out_userName) const;
-    bool FindVaryingByMappedName(const nsACString& mappedName,
-                                 nsCString* const out_userName,
-                                 bool* const out_isArray) const;
-    bool FindUniformByMappedName(const nsACString& mappedName,
-                                 nsCString* const out_userName,
-                                 bool* const out_isArray) const;
-    bool UnmapUniformBlockName(const nsCString& mappedName,
-                               nsCString* const out_userName) const;
+    RefPtr<const webgl::LinkedProgramInfo> GatherLinkInfo() const;
 
     void TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
                                    GLenum bufferMode);
     already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(GLuint index) const;
 
-    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
-
     bool IsLinked() const { return mMostRecentLinkInfo; }
 
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
         return mContext;
@@ -200,17 +187,17 @@ public:
 
 private:
     WebGLRefPtr<WebGLShader> mVertShader;
     WebGLRefPtr<WebGLShader> mFragShader;
     size_t mNumActiveTFOs;
 
     std::map<nsCString, GLuint> mNextLink_BoundAttribLocs;
 
-    std::vector<nsString> mNextLink_TransformFeedbackVaryings;
+    std::vector<nsCString> mNextLink_TransformFeedbackVaryings;
     GLenum mNextLink_TransformFeedbackBufferMode;
 
     nsCString mLinkLog;
     RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo;
 };
 
 } // namespace mozilla
 
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -15,32 +15,16 @@
 #include "prenv.h"
 #include "WebGLContext.h"
 #include "WebGLObjectModel.h"
 #include "WebGLShaderValidator.h"
 #include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
-// 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;
     }
@@ -203,36 +187,36 @@ WebGLShader::ShaderSource(const nsAStrin
 
     mSource = source;
     mCleanSource = cleanSource;
 }
 
 void
 WebGLShader::CompileShader()
 {
-    mValidator = nullptr;
+    mCompileInfo = 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);
+    const auto& shaderValidator = mContext->ShaderValidator();
+    if (shaderValidator) {
+        mCompileInfo = shaderValidator->Compile(mType, mCleanSource.BeginReading(),
+                                                &mValidationLog);
+        if (!mCompileInfo)
+            return;
+        mTranslatedSource = mCompileInfo->translatedSource.c_str();
     } else {
-        success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
-                                             &mValidationLog, &mTranslatedSource);
+        if (!TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
+                                        &mValidationLog, &mTranslatedSource))
+        {
+            return;
+        }
     }
-
-    if (!success)
-        return;
-
     mTranslationSuccessful = true;
 
     gl->MakeCurrent();
 
     const char* const parts[] = {
         mTranslatedSource.BeginReading()
     };
     gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr);
@@ -285,171 +269,31 @@ WebGLShader::GetShaderTranslatedSource(n
         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;
-}
-
-size_t
-WebGLShader::NumAttributes() const
-{
-    if (mValidator)
-        return mValidator->NumAttributes();
-
-    // 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,
-                                            nsCString* 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 = userNameStr->c_str();
-    return true;
-}
-
-bool
-WebGLShader::FindVaryingByMappedName(const nsACString& mappedName,
-                                     nsCString* const out_userName,
-                                     bool* const out_isArray) const
-{
-    if (!mValidator)
-        return false;
-
-    const std::string mappedNameStr(mappedName.BeginReading());
-    std::string userNameStr;
-    if (!mValidator->FindVaryingByMappedName(mappedNameStr, &userNameStr, out_isArray))
-        return false;
-
-    *out_userName = 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;
-}
-
-bool
-WebGLShader::UnmapUniformBlockName(const nsACString& baseMappedName,
-                                   nsCString* const out_baseUserName) const
-{
-    if (!mValidator) {
-        *out_baseUserName = baseMappedName;
-        return true;
-    }
-
-    return mValidator->UnmapUniformBlockName(baseMappedName, out_baseUserName);
-}
-
-void
-WebGLShader::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
-{
-    out_FragOutputs.clear();
-
-    if (!mValidator) {
-        return;
-    }
-    mValidator->EnumerateFragOutputs(out_FragOutputs);
-}
-
-void
-WebGLShader::MapTransformFeedbackVaryings(const std::vector<nsString>& varyings,
-                                          std::vector<std::string>* out_mappedVaryings) const
-{
-    MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
-    MOZ_ASSERT(out_mappedVaryings);
-
-    out_mappedVaryings->clear();
-    out_mappedVaryings->reserve(varyings.size());
-
-    for (const auto& wideUserName : varyings) {
-        const NS_LossyConvertUTF16toASCII mozUserName(wideUserName); // Don't validate here.
-        const std::string userName(mozUserName.BeginReading(), mozUserName.Length());
-        const std::string* pMappedName = &userName;
-        if (mValidator) {
-            mValidator->FindVaryingMappedNameByUserName(userName, &pMappedName);
-        }
-        out_mappedVaryings->push_back(*pMappedName);
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // Boilerplate
 
 JSObject*
 WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLShaderBinding::Wrap(js, this, givenProto);
 }
 
 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 +
+           (mCompileInfo ? mCompileInfo->MemSize() : 0) +
            mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
            mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
            mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
 }
 
 void
 WebGLShader::Delete()
 {
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -16,17 +16,17 @@
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 namespace webgl {
-class ShaderValidator;
+struct ShaderInfo;
 } // namespace webgl
 
 class WebGLShader final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLShader>
     , public LinkedListElement<WebGLShader>
 {
     friend class WebGLContext;
@@ -42,43 +42,22 @@ 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);
 
-    // Util funcs
-    bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
-    size_t CalcNumSamplerUniforms() const;
-    size_t NumAttributes() const;
-    bool FindAttribUserNameByMappedName(const nsACString& mappedName,
-                                        nsCString* const out_userName) const;
-    bool FindVaryingByMappedName(const nsACString& mappedName,
-                                 nsCString* const out_userName,
-                                 bool* const out_isArray) const;
-    bool FindUniformByMappedName(const nsACString& mappedName,
-                                 nsCString* const out_userName,
-                                 bool* const out_isArray) const;
-    bool UnmapUniformBlockName(const nsACString& baseMappedName,
-                               nsCString* const out_baseUserName) const;
-
-    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
+    ////
 
     bool IsCompiled() const {
         return mTranslationSuccessful && mCompilationSuccessful;
     }
 
-private:
-    void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
-    void MapTransformFeedbackVaryings(const std::vector<nsString>& varyings,
-                                      std::vector<std::string>* out_mappedVaryings) const;
-
-public:
     // Other funcs
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
     void Delete();
 
     WebGLContext* GetParentObject() const { return mContext; }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
@@ -88,17 +67,17 @@ public:
 public:
     const GLuint mGLName;
     const GLenum mType;
 
 protected:
     nsString mSource;
     nsCString mCleanSource;
 
-    UniquePtr<webgl::ShaderValidator> mValidator;
+    UniquePtr<const webgl::ShaderInfo> mCompileInfo;
     nsCString mValidationLog;
     bool mTranslationSuccessful;
     nsCString mTranslatedSource;
 
     bool mCompilationSuccessful;
     nsCString mCompilationLog;
 };
 
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -6,16 +6,17 @@
 #include "WebGLShaderValidator.h"
 
 #include "angle/ShaderLang.h"
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/Preferences.h"
 #include "MurmurHash3.h"
 #include "nsPrintfCString.h"
+#include <regex>
 #include <string>
 #include <vector>
 #include "WebGLContext.h"
 
 namespace mozilla {
 namespace webgl {
 
 uint64_t
@@ -84,18 +85,16 @@ ChooseValidatorCompileOptions(const ShBu
     }
     if (resources.MaxCallStackDepth > 0) {
         options |= SH_LIMIT_CALL_STACK_DEPTH;
     }
 
     return options;
 }
 
-} // namespace webgl
-
 ////////////////////////////////////////
 
 static ShShaderOutput
 ShaderOutput(gl::GLContext* gl)
 {
     if (gl->IsGLES()) {
         return SH_ESSL_OUTPUT;
     } else {
@@ -116,286 +115,254 @@ ShaderOutput(gl::GLContext* gl)
         default:
             MOZ_CRASH("GFX: Unexpected GLSL version.");
         }
     }
 
     return SH_GLSL_COMPATIBILITY_OUTPUT;
 }
 
-webgl::ShaderValidator*
-WebGLContext::CreateShaderValidator(GLenum shaderType) const
+/*static*/ void
+ShaderValidator::ChooseResources(const WebGLContext* webgl, ShBuiltInResources* res)
 {
-    if (mBypassShaderValidation)
-        return nullptr;
+    memset(res, 0, sizeof(*res));
+    sh::InitBuiltInResources(res);
 
-    const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
-    const auto outputLanguage = ShaderOutput(gl);
-
-    ShBuiltInResources resources;
-    memset(&resources, 0, sizeof(resources));
-    ShInitBuiltInResources(&resources);
-
-    resources.HashFunction = webgl::IdentifierHashFunc;
+    res->HashFunction = webgl::IdentifierHashFunc;
 
-    resources.MaxVertexAttribs = mGLMaxVertexAttribs;
-    resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
-    resources.MaxVaryingVectors = mGLMaxVaryingVectors;
-    resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
-    resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
-    resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
-    resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
-
-    const bool hasMRTs = (IsWebGL2() ||
-                          IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers));
-    resources.MaxDrawBuffers = (hasMRTs ? mGLMaxDrawBuffers : 1);
-
-    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;
+    res->MaxVertexAttribs             = webgl->mGLMaxVertexAttribs;
+    res->MaxVertexUniformVectors      = webgl->mGLMaxVertexUniformVectors;
+    res->MaxVaryingVectors            = webgl->mGLMaxVaryingVectors;
+    res->MaxVertexTextureImageUnits   = webgl->mGLMaxVertexTextureImageUnits;
+    res->MaxCombinedTextureImageUnits = webgl->mGLMaxTextureUnits;
+    res->MaxTextureImageUnits         = webgl->mGLMaxTextureImageUnits;
+    res->MaxFragmentUniformVectors    = webgl->mGLMaxFragmentUniformVectors;
+    res->MaxDrawBuffers               = webgl->mImplMaxDrawBuffers;
 
     // 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;
+    res->FragmentPrecisionHigh = 1;
+
+    if (webgl->mDisableFragHighP) {
+        res->FragmentPrecisionHigh = 0;
+    }
 
-    if (gl->WorkAroundDriverBugs()) {
+    res->EXT_frag_depth           = webgl->IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth);
+    res->OES_standard_derivatives = webgl->IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives);
+    res->EXT_draw_buffers         = webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
+    res->EXT_shader_texture_lod   = webgl->IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod);
+
+    if (webgl->gl->WorkAroundDriverBugs()) {
 #ifdef XP_MACOSX
-        if (gl->Vendor() == gl::GLVendor::NVIDIA) {
+        if (webgl->gl->Vendor() == gl::GLVendor::NVIDIA) {
             // Work around bug 890432
-            resources.MaxExpressionComplexity = 1000;
+            res->MaxExpressionComplexity = 1000;
         }
 #endif
     }
-
-    const auto compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
-    return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources,
-                                          compileOptions);
 }
 
-////////////////////////////////////////
+ShaderValidator::ShaderValidator(const WebGLContext* webgl)
+{
+    const auto spec = (webgl->IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC);
+    const auto outputLang = ShaderOutput(webgl->gl);
 
-namespace webgl {
+    ShBuiltInResources resources;
+    ChooseResources(webgl, &resources);
 
-/*static*/ ShaderValidator*
-ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec,
-                        ShShaderOutput outputLanguage,
-                        const ShBuiltInResources& resources,
-                        ShCompileOptions compileOptions)
-{
-    ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources);
-    if (!handle)
-        return nullptr;
+    mCompileOptions = ChooseValidatorCompileOptions(resources, webgl->gl);
+    mVertCompiler = sh::ConstructCompiler(LOCAL_GL_VERTEX_SHADER  , spec, outputLang, &resources);
+    mFragCompiler = sh::ConstructCompiler(LOCAL_GL_FRAGMENT_SHADER, spec, outputLang, &resources);
+    MOZ_RELEASE_ASSERT(mVertCompiler);
+    MOZ_RELEASE_ASSERT(mFragCompiler);
 
-    return new ShaderValidator(handle, compileOptions, resources.MaxVaryingVectors);
+#ifdef DEBUG
+    mWebGL = webgl;
+    mResources = resources;
+#endif
 }
 
 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);
+    ShDestruct(mVertCompiler);
+    ShDestruct(mFragCompiler);
 }
 
-void
-ShaderValidator::GetInfoLog(nsACString* out) const
+UniquePtr<const ShaderInfo>
+ShaderValidator::Compile(GLenum shaderType, const char* source,
+                         nsCString* const out_infoLog) const
 {
-    MOZ_ASSERT(mHasRun);
+#ifdef DEBUG
+    ShBuiltInResources resources;
+    ChooseResources(mWebGL, &resources);
+    MOZ_ASSERT(memcmp(&mResources, &resources, sizeof(resources)) == 0);
+#endif
+
+    const ShHandle* pCompiler;
+    switch (shaderType) {
+    case LOCAL_GL_VERTEX_SHADER:
+        pCompiler = &mVertCompiler;
+        break;
+
+    case LOCAL_GL_FRAGMENT_SHADER:
+        pCompiler = &mFragCompiler;
+        break;
+
+    default:
+        MOZ_CRASH();
+    }
+    const auto& compiler = *pCompiler;
 
-    const std::string &log = ShGetInfoLog(mHandle);
-    out->Assign(log.data(), log.length());
+    UniquePtr<ShaderInfo> info;
+    if (sh::Compile(compiler, &source, 1, mCompileOptions)) {
+        info.reset(new ShaderInfo);
+
+        info->shaderVersion = sh::GetShaderVersion(compiler);
+        info->translatedSource = sh::GetObjectCode(compiler);
+
+        info->uniforms = *sh::GetUniforms(compiler);
+        info->varyings = *sh::GetVaryings(compiler);
+        info->attribs  = *sh::GetAttributes(compiler);
+        info->outputs  = *sh::GetOutputVariables(compiler);
+        info->blocks   = *sh::GetInterfaceBlocks(compiler);
+
+        for (const auto& itr : *sh::GetNameHashingMap(compiler)) {
+            info->mapName.insert({itr.first, itr.second});
+            info->unmapName.insert({itr.second, itr.first});
+        }
+    }
+
+    *out_infoLog = sh::GetInfoLog(compiler).c_str();
+
+    sh::ClearResults(compiler);
+    return Move(info);
 }
 
-void
-ShaderValidator::GetOutput(nsACString* out) const
-{
-    MOZ_ASSERT(mHasRun);
-
-    const std::string &output = ShGetObjectCode(mHandle);
-    out->Assign(output.data(), output.length());
-}
+////////////////////////////////////////////////////////////////////////////////
 
 template<size_t N>
-static bool
+static inline 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
+ShaderInfo::CanLinkToVert(const ShaderInfo& vert, const WebGLContext* webgl,
+                          nsCString* const out_log) const
 {
-    if (!prev) {
-        nsPrintfCString error("Passed in NULL prev ShaderValidator.");
-        *out_log = error;
-        return false;
-    }
-
-    const auto shaderVersion = ShGetShaderVersion(mHandle);
-    if (ShGetShaderVersion(prev->mHandle) != shaderVersion) {
-        nsPrintfCString error("Vertex shader version %d does not match"
-                              " fragment shader version %d.",
-                              ShGetShaderVersion(prev->mHandle),
-                              ShGetShaderVersion(mHandle));
-        *out_log = error;
+    if (shaderVersion != vert.shaderVersion) {
+        *out_log = nsPrintfCString("Fragment shader version %u does not match vertex"
+                                   " shader version %u.",
+                                   shaderVersion, vert.shaderVersion);
         return false;
     }
 
-    {
-        const std::vector<sh::Uniform>* vertPtr = ShGetUniforms(prev->mHandle);
-        const std::vector<sh::Uniform>* fragPtr = ShGetUniforms(mHandle);
-        if (!vertPtr || !fragPtr) {
-            nsPrintfCString error("Could not create uniform list.");
-            *out_log = error;
-            return false;
-        }
+    for (const auto& fragVar : uniforms) {
+        for (const auto& vertVar : vert.uniforms) {
+            if (vertVar.name != fragVar.name)
+                continue;
 
-        for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
-            for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->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;
+            if (!fragVar.isSameUniformAtLinkTime(vertVar)) {
+                *out_log = nsPrintfCString("Uniform `%s` is not linkable between"
+                                           " attached shaders.",
+                                           fragVar.name.c_str());
+                return false;
             }
-        }
-    }
-    {
-        const auto vertVars = sh::GetInterfaceBlocks(prev->mHandle);
-        const auto fragVars = sh::GetInterfaceBlocks(mHandle);
-        if (!vertVars || !fragVars) {
-            nsPrintfCString error("Could not create uniform block list.");
-            *out_log = error;
-            return false;
-        }
-
-        for (const auto& fragVar : *fragVars) {
-            for (const auto& vertVar : *vertVars) {
-                if (vertVar.name != fragVar.name)
-                    continue;
-
-                if (!vertVar.isSameInterfaceBlockAtLinkTime(fragVar)) {
-                    nsPrintfCString error("Interface block `%s` is not linkable between"
-                                          " attached shaders.",
-                                          fragVar.name.c_str());
-                    *out_log = error;
-                    return false;
-                }
-
-                break;
-            }
+            break;
         }
     }
 
-    const auto& vertVaryings = ShGetVaryings(prev->mHandle);
-    const auto& fragVaryings = ShGetVaryings(mHandle);
-    if (!vertVaryings || !fragVaryings) {
-        nsPrintfCString error("Could not create varying list.");
-        *out_log = error;
-        return false;
+    for (const auto& fragVar : blocks) {
+        for (const auto& vertVar : vert.blocks) {
+            if (vertVar.name != fragVar.name)
+                continue;
+
+            if (!fragVar.isSameInterfaceBlockAtLinkTime(vertVar)) {
+                *out_log = nsPrintfCString("Interface block `%s` is not linkable between"
+                                           " attached shaders.",
+                                           fragVar.name.c_str());
+                return false;
+            }
+            break;
+        }
     }
 
     {
         std::vector<sh::ShaderVariable> staticUseVaryingList;
 
-        for (const auto& fragVarying : *fragVaryings) {
-            static const char prefix[] = "gl_";
-            if (StartsWith(fragVarying.name, prefix)) {
-                if (fragVarying.staticUse) {
-                    staticUseVaryingList.push_back(fragVarying);
+        for (const auto& fragVar : varyings) {
+            if (fragVar.isBuiltIn()) {
+                if (fragVar.staticUse) {
+                    staticUseVaryingList.push_back(fragVar);
                 }
                 continue;
             }
 
             bool definedInVertShader = false;
             bool staticVertUse = false;
 
-            for (const auto& vertVarying : *vertVaryings) {
-                if (vertVarying.name != fragVarying.name)
+            for (const auto& vertVar : vert.varyings) {
+                if (vertVar.name != fragVar.name)
                     continue;
 
-                if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, shaderVersion)) {
-                    nsPrintfCString error("Varying `%s`is not linkable between"
-                                          " attached shaders.",
-                                          fragVarying.name.c_str());
-                    *out_log = error;
+                if (!fragVar.isSameVaryingAtLinkTime(vertVar, shaderVersion)) {
+                    *out_log = nsPrintfCString("Varying `%s`is not linkable between"
+                                               " attached shaders.",
+                                               fragVar.name.c_str());
                     return false;
                 }
 
                 definedInVertShader = true;
-                staticVertUse = vertVarying.staticUse;
+                staticVertUse = vertVar.staticUse;
                 break;
             }
 
-            if (!definedInVertShader && fragVarying.staticUse) {
-                nsPrintfCString error("Varying `%s` has static-use in the frag"
-                                      " shader, but is undeclared in the vert"
-                                      " shader.", fragVarying.name.c_str());
-                *out_log = error;
+            if (!definedInVertShader && fragVar.staticUse) {
+                *out_log = nsPrintfCString("Varying `%s` has static-use in the frag"
+                                           " shader, but is undeclared in the vert"
+                                           " shader.",
+                                           fragVar.name.c_str());
                 return false;
             }
 
-            if (staticVertUse && fragVarying.staticUse) {
-                staticUseVaryingList.push_back(fragVarying);
+            if (staticVertUse && fragVar.staticUse) {
+                staticUseVaryingList.push_back(fragVar);
             }
         }
 
-        if (!ShCheckVariablesWithinPackingLimits(mMaxVaryingVectors,
+        if (!ShCheckVariablesWithinPackingLimits(webgl->mGLMaxVaryingVectors,
                                                  staticUseVaryingList))
         {
             *out_log = "Statically used varyings do not fit within packing limits. (see"
                        " GLSL ES Specification 1.0.17, p111)";
             return false;
         }
     }
 
     if (shaderVersion == 100) {
         // Enforce ESSL1 invariant linking rules.
         bool isInvariant_Position = false;
         bool isInvariant_PointSize = false;
         bool isInvariant_FragCoord = false;
         bool isInvariant_PointCoord = false;
 
-        for (const auto& varying : *vertVaryings) {
-            if (varying.name == "gl_Position") {
-                isInvariant_Position = varying.isInvariant;
-            } else if (varying.name == "gl_PointSize") {
-                isInvariant_PointSize = varying.isInvariant;
+        for (const auto& vertVar : vert.varyings) {
+            if (vertVar.name == "gl_Position") {
+                isInvariant_Position = vertVar.isInvariant;
+            } else if (vertVar.name == "gl_PointSize") {
+                isInvariant_PointSize = vertVar.isInvariant;
             }
         }
 
-        for (const auto& varying : *fragVaryings) {
-            if (varying.name == "gl_FragCoord") {
-                isInvariant_FragCoord = varying.isInvariant;
-            } else if (varying.name == "gl_PointCoord") {
-                isInvariant_PointCoord = varying.isInvariant;
+        for (const auto& fragVar : varyings) {
+            if (fragVar.name == "gl_FragCoord") {
+                isInvariant_FragCoord = fragVar.isInvariant;
+            } else if (fragVar.name == "gl_PointCoord") {
+                isInvariant_PointCoord = fragVar.isInvariant;
             }
         }
 
         ////
 
         const auto fnCanBuiltInsLink = [](bool vertIsInvariant, bool fragIsInvariant) {
             if (vertIsInvariant)
                 return true;
@@ -411,194 +378,167 @@ ShaderValidator::CanLinkTo(const ShaderV
 
         if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
             *out_log = "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
                        " Specification 1.0.17, p39)";
             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;
-}
-
-size_t
-ShaderValidator::NumAttributes() const
-{
-  return ShGetAttributes(mHandle)->size();
-}
-
-// 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;
-        }
-    }
+    const auto& gl = webgl->gl;
 
-    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;
+    if (gl->WorkAroundDriverBugs() &&
+        webgl->mIsMesa)
+    {
+        // Bug 777028: Mesa can't handle more than 16 samplers per program, counting each
+        // array entry.
+        const auto fnCalcSamplers = [&](const ShaderInfo& info) {
+            size_t accum = 0;
+            for (const auto& cur : info.uniforms) {
+                switch (cur.type) {
+                case LOCAL_GL_SAMPLER_2D:
+                case LOCAL_GL_SAMPLER_CUBE:
+                    accum += cur.arraySize;
+                    break;
+                }
+            }
+            return accum;
+        };
+        const auto numSamplers_upperBound = fnCalcSamplers(vert) + fnCalcSamplers(*this);
+        if (numSamplers_upperBound > 16) {
+            *out_log = "Programs with more than 16 samplers are disallowed on Mesa"
+                       " drivers to avoid crashing.";
+            return false;
         }
-    }
-
-    return false;
-}
 
-bool
-ShaderValidator::FindVaryingByMappedName(const std::string& mappedName,
-                                         std::string* const out_userName,
-                                         bool* const out_isArray) const
-{
-    const std::vector<sh::Varying>& varyings = *ShGetVaryings(mHandle);
-    for (auto itr = varyings.begin(); itr != varyings.end(); ++itr) {
-        const sh::ShaderVariable* found;
-        if (!itr->findInfoByMappedName(mappedName, &found, out_userName))
-            continue;
-
-        *out_isArray = found->isArray();
-        return true;
-    }
-
-    return false;
-}
-
-bool
-ShaderValidator::FindVaryingMappedNameByUserName(const std::string& userName,
-                                                 const std::string** const out_mappedName) const
-{
-    const std::vector<sh::Varying>& attribs = *ShGetVaryings(mHandle);
-    for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
-        if (itr->name == userName) {
-            *out_mappedName = &(itr->mappedName);
-            return true;
+        // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
+        // attribute count.
+        if (vert.attribs.size() > webgl->mGLMaxVertexAttribs) {
+            *out_log = "Number of attributes exceeds Mesa's reported max attribute"
+                       " count.";
+            return false;
         }
     }
 
-    return false;
+    return true;
 }
-// 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::string var("foo.bar[3].qux[10]")
+//const std::sregex_token_iterator itr(var.begin(), var.end(), kRegex_GLSLVar, {-1,0});
+//const std::vector<std::string> parts(itr, std::sregex_token_iterator());
+//  => ||foo|.|bar|[3].|qux|[10]|
+
+/*static*/ std::string
+ShaderInfo::MapNameWith(const std::string& srcName,
+                        const decltype(ShaderInfo::mapName)& map)
 {
-    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;
+    static const std::regex kRegex_GLSLVar("[a-zA-Z_][a-zA-Z_0-9]*");
+    const std::vector<std::string> srcParts(std::sregex_token_iterator(srcName.begin(),
+                                                                       srcName.end(),
+                                                                       kRegex_GLSLVar,
+                                                                       {-1,0}),
+                                            std::sregex_token_iterator());
+    std::vector<const std::string*> dstParts;
+    dstParts.reserve(srcParts.size());
+    size_t dstNameSize = 0;
+    for (const auto& src : srcParts) {
+        const auto itr = map.find(src);
+        const std::string* dst;
+        if (itr == map.end()) {
+            dst = &src;
+        } else {
+            dst = &(itr->second);
+        }
+        dstParts.push_back(dst);
+        dstNameSize += dst->size();
     }
 
-    const size_t dotPos = mappedName.find(".");
-
-    const std::vector<sh::InterfaceBlock>& interfaces = *ShGetInterfaceBlocks(mHandle);
-    for (const auto& interface : interfaces) {
-
-        std::string mappedFieldName;
-        const bool hasInstanceName = !interface.instanceName.empty();
+    std::string dstName;
+    dstName.reserve(dstNameSize);
+    for (const auto& dst : dstParts) {
+        dstName += *dst;
+    }
+    return dstName;
+}
 
-        // If the InterfaceBlock has an instanceName, all variables defined
-        // within the block are qualified with the block name, as opposed
-        // to being placed in the global scope.
-        if (hasInstanceName) {
+////
 
-            // If mappedName has no block name prefix, skip
-            if (std::string::npos == dotPos)
-                continue;
-
-            // If mappedName has a block name prefix that doesn't match, skip
-            const std::string mappedInterfaceBlockName = mappedName.substr(0, dotPos);
-            if (interface.mappedName != mappedInterfaceBlockName)
-                continue;
+template<typename T>
+size_t MemSize(const T& x)
+{
+    return sizeof(x) + IndirectMemSize(x);
+}
 
-            mappedFieldName = mappedName.substr(dotPos + 1);
-        } else {
-            mappedFieldName = mappedName;
-        }
-
-        for (const auto& field : interface.fields) {
-            const sh::ShaderVariable* found;
-
-            if (!field.findInfoByMappedName(mappedFieldName, &found, out_userName))
-                continue;
+size_t
+IndirectMemSize(int64_t)
+{
+    return 0;
+}
 
-            if (hasInstanceName) {
-                // Prepend the user name of the interface that matched
-                *out_userName = interface.name + "." + *out_userName;
-            }
+size_t
+IndirectMemSize(uint64_t)
+{
+    return 0;
+}
 
-            *out_isArray = found->isArray();
-            return true;
-        }
-    }
-
-    return false;
+size_t
+IndirectMemSize(const std::string& x)
+{
+    return x.size();
 }
 
-bool
-ShaderValidator::UnmapUniformBlockName(const nsACString& baseMappedName,
-                                       nsCString* const out_baseUserName) const
+template<typename K, typename V>
+size_t
+IndirectMemSize(const std::map<K, V>& x)
 {
-    const std::vector<sh::InterfaceBlock>& interfaces = *ShGetInterfaceBlocks(mHandle);
-    for (const auto& interface : interfaces) {
-        const nsDependentCString interfaceMappedName(interface.mappedName.data(),
-                                                     interface.mappedName.size());
-        if (baseMappedName == interfaceMappedName) {
-            *out_baseUserName = interface.name.data();
-            return true;
-        }
+    size_t ret = 0;
+    for (const auto& pair : x) {
+        ret += MemSize(pair.first);
+        ret += MemSize(pair.second);
     }
+    return ret;
+}
 
-    return false;
+template<typename T>
+size_t
+IndirectMemSize(const std::vector<T>& x)
+{
+    size_t ret = 0;
+    for (const auto& cur : x) {
+        ret += MemSize(cur);
+    }
+    return ret;
 }
 
-void
-ShaderValidator::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
+size_t
+IndirectMemSize(const sh::ShaderVariable& x)
 {
-    const auto* fragOutputs = ShGetOutputVariables(mHandle);
+    return IndirectMemSize(x.name) +
+           IndirectMemSize(x.mappedName) +
+           IndirectMemSize(x.fields) +
+           IndirectMemSize(x.structName);
+}
 
-    if (fragOutputs) {
-        for (const auto& fragOutput : *fragOutputs) {
-            out_FragOutputs.insert({nsCString(fragOutput.name.c_str()),
-                                    nsCString(fragOutput.mappedName.c_str())});
-        }
-    }
+size_t
+IndirectMemSize(const sh::InterfaceBlock& x)
+{
+    return IndirectMemSize(x.name) +
+           IndirectMemSize(x.mappedName) +
+           IndirectMemSize(x.instanceName) +
+           IndirectMemSize(x.fields);
+}
+
+size_t
+ShaderInfo::MemSize() const
+{
+    return sizeof(*this) +
+           IndirectMemSize(translatedSource) +
+           IndirectMemSize(uniforms) +
+           IndirectMemSize(varyings) +
+           IndirectMemSize(attribs) +
+           IndirectMemSize(outputs) +
+           IndirectMemSize(blocks) +
+           IndirectMemSize(mapName) +
+           IndirectMemSize(unmapName);
 }
 
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderValidator.h
+++ b/dom/canvas/WebGLShaderValidator.h
@@ -2,76 +2,75 @@
 /* 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 "mozilla/UniquePtr.h"
 #include "GLDefs.h"
 #include "nsString.h"
+#include <map>
 #include <string>
 
 namespace mozilla {
+class WebGLContext;
+
 namespace webgl {
+struct ShaderInfo;
 
 class ShaderValidator final
 {
-    const ShHandle mHandle;
-    const ShCompileOptions mCompileOptions;
-    const int mMaxVaryingVectors;
-    bool mHasRun;
+    ShCompileOptions mCompileOptions;
+    ShHandle mVertCompiler;
+    ShHandle mFragCompiler;
+
+#ifdef DEBUG
+    const WebGLContext* mWebGL;
+    ShBuiltInResources mResources;
+#endif
+
+    static void ChooseResources(const WebGLContext* webgl, ShBuiltInResources* res);
 
 public:
-    static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec,
-                                   ShShaderOutput outputLanguage,
-                                   const ShBuiltInResources& resources,
-                                   ShCompileOptions compileOptions);
-
-private:
-    ShaderValidator(ShHandle handle, ShCompileOptions compileOptions,
-                    int maxVaryingVectors)
-        : mHandle(handle)
-        , mCompileOptions(compileOptions)
-        , mMaxVaryingVectors(maxVaryingVectors)
-        , mHasRun(false)
-    { }
+    explicit ShaderValidator(const WebGLContext* webgl);
+    virtual ~ShaderValidator();
 
 public:
-    ~ShaderValidator();
+    UniquePtr<const ShaderInfo> Compile(GLenum shaderType, const char* source,
+                                        nsCString* const out_infoLog) const;
+};
 
-    bool ValidateAndTranslate(const char* source);
-    void GetInfoLog(nsACString* out) const;
-    void GetOutput(nsACString* out) const;
-    bool CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const;
-    size_t CalcNumSamplerUniforms() const;
-    size_t NumAttributes() 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;
+struct ShaderInfo final
+{
+    std::string translatedSource;
+    uint16_t shaderVersion;
+    std::vector<sh::Uniform> uniforms;
+    std::vector<sh::Varying> varyings;
+    std::vector<sh::Attribute> attribs;
+    std::vector<sh::OutputVariable> outputs;
+    std::vector<sh::InterfaceBlock> blocks;
 
-    bool FindVaryingMappedNameByUserName(const std::string& userName,
-                                         const std::string** const out_mappedName) const;
+    std::map<std::string, const std::string> mapName;
+    std::map<std::string, const std::string> unmapName;
+
+    bool CanLinkToVert(const ShaderInfo& vert, const WebGLContext* webgl,
+                       nsCString* const out_log) const;
+
+    static std::string MapNameWith(const std::string& srcName,
+                                   const decltype(mapName)& map);
 
-    bool FindVaryingByMappedName(const std::string& mappedName,
-                                 std::string* const out_userName,
-                                 bool* const out_isArray) const;
-    bool FindUniformByMappedName(const std::string& mappedName,
-                                 std::string* const out_userName,
-                                 bool* const out_isArray) const;
-    bool UnmapUniformBlockName(const nsACString& baseMappedName,
-                               nsCString* const out_baseUserName) const;
+    std::string MapName(const std::string& userName) const {
+        return MapNameWith(userName, mapName);
+    }
+    //std::string UnmapName(const std::string& mappedName) const {
+    //    return MapNameWith(mappedName, unmapName);
+    //}
 
-    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
-
-    bool ValidateTransformFeedback(const std::vector<nsString>& userNames,
-                                   uint32_t maxComponents, nsCString* const out_errorText,
-                                   std::vector<std::string>* const out_mappedNames);
+    size_t MemSize() const;
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // WEBGL_SHADER_VALIDATOR_H_