Bug 1478909 - Make funcName implicit for WebGL calls. - r=kvark
☠☠ backed out by c50b638b8e71 ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 26 Jul 2018 21:46:33 -0700
changeset 478969 e8023a02b6c2870e15bea85539ce9ba1563ffdc5
parent 478968 8489f76a1b4ddbef11e853fe7355cdfbf7e03689
child 478970 c50b638b8e7157661ce4ebb023c93969844eddbf
push id215
push userfmarier@mozilla.com
push dateFri, 10 Aug 2018 00:07:31 +0000
reviewerskvark
bugs1478909
milestone63.0a1
Bug 1478909 - Make funcName implicit for WebGL calls. - r=kvark MozReview-Commit-ID: Gv77SnHZcGb
dom/canvas/TexUnpackBlob.cpp
dom/canvas/TexUnpackBlob.h
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextBuffers.cpp
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGL2ContextMRTs.cpp
dom/canvas/WebGL2ContextPrograms.cpp
dom/canvas/WebGL2ContextQueries.cpp
dom/canvas/WebGL2ContextRenderbuffers.cpp
dom/canvas/WebGL2ContextSamplers.cpp
dom/canvas/WebGL2ContextState.cpp
dom/canvas/WebGL2ContextSync.cpp
dom/canvas/WebGL2ContextTextures.cpp
dom/canvas/WebGL2ContextTransformFeedback.cpp
dom/canvas/WebGL2ContextUniforms.cpp
dom/canvas/WebGLBuffer.cpp
dom/canvas/WebGLBuffer.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextExtensions.cpp
dom/canvas/WebGLContextFramebufferOperations.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextTextures.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLContextVertexArray.cpp
dom/canvas/WebGLContextVertices.cpp
dom/canvas/WebGLExtensionDebugShaders.cpp
dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
dom/canvas/WebGLExtensionMOZDebug.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLFramebufferAttachable.cpp
dom/canvas/WebGLFramebufferAttachable.h
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLQuery.cpp
dom/canvas/WebGLQuery.h
dom/canvas/WebGLRenderbuffer.cpp
dom/canvas/WebGLRenderbuffer.h
dom/canvas/WebGLSampler.cpp
dom/canvas/WebGLSampler.h
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTextureUpload.cpp
dom/canvas/WebGLTransformFeedback.cpp
dom/canvas/WebGLTransformFeedback.h
dom/canvas/WebGLUniformLocation.cpp
dom/canvas/WebGLUniformLocation.h
dom/canvas/WebGLValidateStrings.cpp
dom/canvas/WebGLValidateStrings.h
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -62,22 +62,20 @@ IsPIValidForDOM(const webgl::PackingInfo
     default:
         return false;
     }
 
     return true;
 }
 
 static bool
-ValidatePIForDOM(WebGLContext* webgl, const char* funcName,
-                 const webgl::PackingInfo& pi)
+ValidatePIForDOM(WebGLContext* webgl, const webgl::PackingInfo& pi)
 {
     if (!IsPIValidForDOM(pi)) {
-        webgl->ErrorInvalidOperation("%s: Format or type is invalid for DOM sources.",
-                                     funcName);
+        webgl->ErrorInvalidOperation("Format or type is invalid for DOM sources.");
         return false;
     }
     return true;
 }
 
 static WebGLTexelFormat
 FormatForPackingInfo(const PackingInfo& pi)
 {
@@ -174,91 +172,89 @@ FormatForPackingInfo(const PackingInfo& 
     }
 
     return WebGLTexelFormat::FormatNotSupportingAnyConversion;
 }
 
 ////////////////////
 
 static bool
-ValidateUnpackPixels(WebGLContext* webgl, const char* funcName, uint32_t fullRows,
+ValidateUnpackPixels(WebGLContext* webgl, uint32_t fullRows,
                      uint32_t tailPixels, webgl::TexUnpackBlob* blob)
 {
     if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
         return true;
 
     const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
     if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > blob->mRowLength) {
-        webgl->ErrorInvalidOperation("%s: UNPACK_SKIP_PIXELS + width >"
-                                     " UNPACK_ROW_LENGTH.",
-                                     funcName);
+        webgl->ErrorInvalidOperation("UNPACK_SKIP_PIXELS + width >"
+                                     " UNPACK_ROW_LENGTH.");
         return false;
     }
 
     if (blob->mHeight > blob->mImageHeight) {
-        webgl->ErrorInvalidOperation("%s: height > UNPACK_IMAGE_HEIGHT.", funcName);
+        webgl->ErrorInvalidOperation("height > UNPACK_IMAGE_HEIGHT.");
         return false;
     }
 
     //////
 
     // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
     auto skipFullRows = CheckedUint32(blob->mSkipImages) * blob->mImageHeight;
     skipFullRows += blob->mSkipRows;
 
     MOZ_ASSERT(blob->mDepth >= 1);
     MOZ_ASSERT(blob->mHeight >= 1);
     auto usedFullRows = CheckedUint32(blob->mDepth - 1) * blob->mImageHeight;
     usedFullRows += blob->mHeight - 1; // Full rows in the final image, excluding the tail.
 
     const auto fullRowsNeeded = skipFullRows + usedFullRows;
     if (!fullRowsNeeded.isValid()) {
-        webgl->ErrorOutOfMemory("%s: Invalid calculation for required row count.",
-                                funcName);
+        webgl->ErrorOutOfMemory("Invalid calculation for required row count.");
         return false;
     }
 
     if (fullRows > fullRowsNeeded.value())
         return true;
 
     if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) {
         blob->mNeedsExactUpload = true;
         return true;
     }
 
-    webgl->ErrorInvalidOperation("%s: Desired upload requires more data than is"
+    webgl->ErrorInvalidOperation("Desired upload requires more data than is"
                                  " available: (%u rows plus %u pixels needed, %u rows"
                                  " plus %u pixels available)",
-                                 funcName, fullRowsNeeded.value(),
+                                 fullRowsNeeded.value(),
                                  usedPixelsPerRow.value(), fullRows, tailPixels);
     return false;
 }
 
 static bool
-ValidateUnpackBytes(WebGLContext* webgl, const char* funcName,
+ValidateUnpackBytes(WebGLContext* webgl,
                     const webgl::PackingInfo& pi, size_t availByteCount,
                     webgl::TexUnpackBlob* blob)
 {
     if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
         return true;
 
     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
     const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
 
     const auto fullRows = availByteCount / rowStride;
     if (!fullRows.isValid()) {
-        webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.", funcName);
+        webgl->ErrorOutOfMemory("Unacceptable upload size calculated.");
         return false;
     }
 
     const auto bodyBytes = fullRows.value() * rowStride.value();
     const auto tailPixels = (availByteCount - bodyBytes) / bytesPerPixel;
 
-    return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob);
+    return ValidateUnpackPixels(webgl, fullRows.value(), tailPixels, blob);
 }
 
 ////////////////////
 
 static uint32_t
 ZeroOn2D(TexImageTarget target, uint32_t val)
 {
     return (IsTarget3D(target) ? val : 0);
@@ -308,17 +304,17 @@ HasColorAndAlpha(const WebGLTexelFormat 
     case WebGLTexelFormat::BGRA8:
         return true;
     default:
         return false;
     }
 }
 
 bool
-TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
+TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl,
                                const uint32_t rowLength, const uint32_t rowCount,
                                WebGLTexelFormat srcFormat,
                                const uint8_t* const srcBegin, const ptrdiff_t srcStride,
                                WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
                                const uint8_t** const out_begin,
                                UniqueBuffer* const out_anchoredBuffer) const
 {
     MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
@@ -341,57 +337,56 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
         return srcIsPremult != dstIsPremult;
     };
 
     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                      : gl::OriginPos::BottomLeft);
     const auto dstOrigin = gl::OriginPos::BottomLeft;
 
     if (srcFormat != dstFormat) {
-        webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting. (%u->%u)",
-                                   funcName, uint32_t(srcFormat),
+        webgl->GeneratePerfWarning("Conversion requires pixel reformatting. (%u->%u)",
+                                   uint32_t(srcFormat),
                                    uint32_t(dstFormat));
     } else if (fnHasPremultMismatch()) {
-        webgl->GeneratePerfWarning("%s: Conversion requires change in"
-                                   " alpha-premultiplication.",
-                                   funcName);
+        webgl->GeneratePerfWarning("Conversion requires change in"
+                                   " alpha-premultiplication.");
     } else if (srcOrigin != dstOrigin) {
-        webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName);
+        webgl->GeneratePerfWarning("Conversion requires y-flip.");
     } else if (srcStride != dstStride) {
-        webgl->GeneratePerfWarning("%s: Conversion requires change in stride. (%u->%u)",
-                                   funcName, uint32_t(srcStride), uint32_t(dstStride));
+        webgl->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
+                                   uint32_t(srcStride), uint32_t(dstStride));
     } else {
         return true;
     }
 
     ////
 
     const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
     if (!dstTotalBytes.isValid()) {
-        webgl->ErrorOutOfMemory("%s: Calculation failed.", funcName);
+        webgl->ErrorOutOfMemory("Calculation failed.");
         return false;
     }
 
     UniqueBuffer dstBuffer = calloc(1, dstTotalBytes.value());
     if (!dstBuffer.get()) {
-        webgl->ErrorOutOfMemory("%s: Failed to allocate dest buffer.", funcName);
+        webgl->ErrorOutOfMemory("Failed to allocate dest buffer.");
         return false;
     }
     const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
 
     ////
 
     // And go!:
     bool wasTrivial;
     if (!ConvertImage(rowLength, rowCount,
                       srcBegin, srcStride, srcOrigin, srcFormat, srcIsPremult,
                       dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult,
                       &wasTrivial))
     {
-        webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName);
+        webgl->ErrorImplementationBug("ConvertImage failed.");
         return false;
     }
 
     *out_begin = dstBegin;
     *out_anchoredBuffer = std::move(dstBuffer);
     return true;
 }
 
@@ -418,27 +413,26 @@ TexUnpackBytes::TexUnpackBytes(const Web
                     FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width),
                     width, height, depth, gfxAlphaType::NonPremult)
     , mIsClientData(isClientData)
     , mPtr(ptr)
     , mAvailBytes(availBytes)
 { }
 
 bool
-TexUnpackBytes::Validate(WebGLContext* webgl, const char* funcName,
-                         const webgl::PackingInfo& pi)
+TexUnpackBytes::Validate(WebGLContext* webgl, const webgl::PackingInfo& pi)
 {
     if (mIsClientData && !mPtr)
         return true;
 
-    return ValidateUnpackBytes(webgl, funcName, pi, mAvailBytes, this);
+    return ValidateUnpackBytes(webgl, pi, mAvailBytes, this);
 }
 
 bool
-TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
                               GLenum* const out_error) const
 {
     WebGLContext* webgl = tex->mContext;
 
     const auto format = FormatForPackingInfo(pi);
@@ -458,46 +452,43 @@ TexUnpackBytes::TexOrSubImage(bool isSub
         }
 
         if (webgl->mPixelStore_UnpackImageHeight ||
             webgl->mPixelStore_UnpackSkipImages ||
             webgl->mPixelStore_UnpackRowLength ||
             webgl->mPixelStore_UnpackSkipRows ||
             webgl->mPixelStore_UnpackSkipPixels)
         {
-            webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult"
-                                         " or y-flip do not support subrect selection.",
-                                         funcName);
+            webgl->ErrorInvalidOperation("Non-DOM-Element uploads with alpha-premult"
+                                         " or y-flip do not support subrect selection.");
             return false;
         }
 
-        webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for"
-                               " non-DOM-Element uploads.",
-                               funcName);
+        webgl->GenerateWarning("Alpha-premult and y-flip are deprecated for"
+                               " non-DOM-Element uploads.");
 
         const uint32_t rowLength = mWidth;
         const uint32_t rowCount = mHeight * mDepth;
         const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment);
-        if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride,
+        if (!ConvertIfNeeded(webgl, rowLength, rowCount, format, mPtr, stride,
                              format, stride, &uploadPtr, &tempBuffer))
         {
             return false;
         }
     } while (false);
 
     //////
 
     const auto& gl = webgl->gl;
 
     bool useParanoidHandling = false;
     if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
-        webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte"
+        webgl->GenerateWarning("Uploads from a buffer with a final row with a byte"
                                " count smaller than the row stride can incur extra"
-                               " overhead.",
-                               funcName);
+                               " overhead.");
 
         if (gl->WorkAroundDriverBugs()) {
             useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
         }
     }
 
     if (!useParanoidHandling) {
         if (webgl->mBoundPixelUnpackBuffer) {
@@ -596,28 +587,27 @@ TexUnpackImage::TexUnpackImage(const Web
                     srcAlphaType)
     , mImage(image)
 { }
 
 TexUnpackImage::~TexUnpackImage()
 { }
 
 bool
-TexUnpackImage::Validate(WebGLContext* webgl, const char* funcName,
-                         const webgl::PackingInfo& pi)
+TexUnpackImage::Validate(WebGLContext* webgl, const webgl::PackingInfo& pi)
 {
-    if (!ValidatePIForDOM(webgl, funcName, pi))
+    if (!ValidatePIForDOM(webgl, pi))
         return false;
 
     const auto fullRows = mImage->GetSize().height;
-    return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
+    return ValidateUnpackPixels(webgl, fullRows, 0, this);
 }
 
 bool
-TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
                               GLenum* const out_error) const
 {
     MOZ_ASSERT_IF(needsRespec, !isSubImage);
 
     WebGLContext* webgl = tex->mContext;
@@ -709,18 +699,18 @@ TexUnpackImage::TexOrSubImage(bool isSub
             break;
         }
 
         // Blitting was successful, so we're done!
         *out_error = 0;
         return true;
     } while (false);
 
-    const nsPrintfCString perfMsg("%s: Failed to hit GPU-copy fast-path: %s (src type %u)",
-                                  funcName, fallbackReason, uint32_t(mImage->GetFormat()));
+    const nsPrintfCString perfMsg("Failed to hit GPU-copy fast-path: %s (src type %u)",
+                                  fallbackReason, uint32_t(mImage->GetFormat()));
 
     if (webgl->mPixelStore_RequireFastPath) {
         webgl->ErrorInvalidOperation("%s", perfMsg.BeginReading());
         return false;
     }
 
     webgl->GeneratePerfWarning("%s Falling back to CPU upload.",
                                perfMsg.BeginReading());
@@ -728,26 +718,25 @@ TexUnpackImage::TexOrSubImage(bool isSub
     const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
 
     RefPtr<gfx::DataSourceSurface> dataSurf;
     if (surf) {
         // WARNING: OSX can lose our MakeCurrent here.
         dataSurf = surf->GetDataSurface();
     }
     if (!dataSurf) {
-        webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
-                                " blit failed for TexUnpackImage.",
-                                funcName);
+        webgl->ErrorOutOfMemory("GetAsSourceSurface or GetDataSurface failed after"
+                                " blit failed for TexUnpackImage.");
         return false;
     }
 
     const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
                                     mSrcAlphaType);
 
-    return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
+    return surfBlob.TexOrSubImage(isSubImage, needsRespec, tex, target, level,
                                   dui, xOffset, yOffset, zOffset, pi, out_error);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // TexUnpackSurface
 
 TexUnpackSurface::TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target,
@@ -807,28 +796,27 @@ GetFormatForSurf(gfx::SourceSurface* sur
     default:
         return false;
     }
 }
 
 //////////
 
 bool
-TexUnpackSurface::Validate(WebGLContext* webgl, const char* funcName,
-                           const webgl::PackingInfo& pi)
+TexUnpackSurface::Validate(WebGLContext* webgl, const webgl::PackingInfo& pi)
 {
-    if (!ValidatePIForDOM(webgl, funcName, pi))
+    if (!ValidatePIForDOM(webgl, pi))
         return false;
 
     const auto fullRows = mSurf->GetSize().height;
-    return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
+    return ValidateUnpackPixels(webgl, fullRows, 0, this);
 }
 
 bool
-TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
                                 WebGLTexture* tex, TexImageTarget target, GLint level,
                                 const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                 GLint yOffset, GLint zOffset, const webgl::PackingInfo& dstPI,
                                 GLenum* const out_error) const
 {
     const auto& webgl = tex->mContext;
 
     ////
@@ -839,25 +827,25 @@ TexUnpackSurface::TexOrSubImage(bool isS
     const auto& dstBPP = webgl::BytesPerPixel(dstPI);
     const auto dstFormat = FormatForPackingInfo(dstPI);
 
     ////
 
     WebGLTexelFormat srcFormat;
     uint8_t srcBPP;
     if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
-        webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
+        webgl->ErrorImplementationBug("GetFormatForSurf failed for"
                                       " WebGLTexelFormat::%u.",
-                                      funcName, uint32_t(mSurf->GetFormat()));
+                                      uint32_t(mSurf->GetFormat()));
         return false;
     }
 
     gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
     if (!map.IsMapped()) {
-        webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
+        webgl->ErrorOutOfMemory("Failed to map source surface for upload.");
         return false;
     }
 
     const auto& srcBegin = map.GetData();
     const auto& srcStride = map.GetStride();
 
     ////
 
@@ -874,17 +862,17 @@ TexUnpackSurface::TexOrSubImage(bool isS
 
     const auto dstRowLengthBytes = rowLength * dstBPP;
     const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
 
     ////
 
     const uint8_t* dstBegin = srcBegin;
     UniqueBuffer tempBuffer;
-    if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin,
+    if (!ConvertIfNeeded(webgl, rowLength, rowCount, srcFormat, srcBegin,
                          srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer))
     {
         return false;
     }
 
     ////
 
     const auto& gl = webgl->gl;
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -59,35 +59,34 @@ protected:
     TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target, uint32_t rowLength,
                   uint32_t width, uint32_t height, uint32_t depth,
                   gfxAlphaType srcAlphaType);
 
 public:
     virtual ~TexUnpackBlob() { }
 
 protected:
-    bool ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
+    bool ConvertIfNeeded(WebGLContext* webgl,
                          const uint32_t rowLength, const uint32_t rowCount,
                          WebGLTexelFormat srcFormat,
                          const uint8_t* const srcBegin, const ptrdiff_t srcStride,
                          WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
 
                          const uint8_t** const out_begin,
                          UniqueBuffer* const out_anchoredBuffer) const;
 
 public:
     virtual bool HasData() const { return true; }
 
-    virtual bool Validate(WebGLContext* webgl, const char* funcName,
-                          const webgl::PackingInfo& pi) = 0;
+    virtual bool Validate(WebGLContext* webgl, const webgl::PackingInfo& pi) = 0;
 
     // Returns false when we've generated a WebGL error.
     // Returns true but with a non-zero *out_error if we still need to generate a WebGL
     // error.
-    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                const webgl::PackingInfo& pi, GLenum* const out_error) const = 0;
 };
 
 class TexUnpackBytes final : public TexUnpackBlob
 {
@@ -97,19 +96,19 @@ public:
     const size_t mAvailBytes;
 
     TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                    uint32_t height, uint32_t depth, bool isClientData, const uint8_t* ptr,
                    size_t availBytes);
 
     virtual bool HasData() const override { return !mIsClientData || bool(mPtr); }
 
-    virtual bool Validate(WebGLContext* webgl, const char* funcName,
+    virtual bool Validate(WebGLContext* webgl,
                           const webgl::PackingInfo& pi) override;
-    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                const webgl::PackingInfo& pi, GLenum* const out_error) const override;
 };
 
 class TexUnpackImage final : public TexUnpackBlob
 {
@@ -117,37 +116,37 @@ public:
     const RefPtr<layers::Image> mImage;
 
     TexUnpackImage(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                    uint32_t height, uint32_t depth, layers::Image* image,
                    gfxAlphaType srcAlphaType);
 
     ~TexUnpackImage(); // Prevent needing to define layers::Image in the header.
 
-    virtual bool Validate(WebGLContext* webgl, const char* funcName,
+    virtual bool Validate(WebGLContext* webgl,
                           const webgl::PackingInfo& pi) override;
-    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                const webgl::PackingInfo& dstPI, GLenum* const out_error)  const override;
 };
 
 class TexUnpackSurface final : public TexUnpackBlob
 {
 public:
     const RefPtr<gfx::DataSourceSurface> mSurf;
 
     TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target, uint32_t width,
                      uint32_t height, uint32_t depth, gfx::DataSourceSurface* surf,
                      gfxAlphaType srcAlphaType);
 
-    virtual bool Validate(WebGLContext* webgl, const char* funcName,
+    virtual bool Validate(WebGLContext* webgl,
                           const webgl::PackingInfo& pi) override;
-    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+    virtual bool TexOrSubImage(bool isSubImage, bool needsRespec,
                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                const webgl::PackingInfo& dstPI, GLenum* const out_error) const override;
 };
 
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -77,108 +77,110 @@ public:
 
 
     // -------------------------------------------------------------------------
     // Renderbuffer objects - WebGL2ContextRenderbuffers.cpp
 
     void GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat,
                                     GLenum pname, JS::MutableHandleValue retval,
                                     ErrorResult& rv);
-    void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat,
-                                        GLsizei width, GLsizei height);
+    void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalFormat,
+                                        GLsizei width, GLsizei height)
+    {
+        const FuncScope funcScope(*this, "renderbufferStorageMultisample");
+        RenderbufferStorage_base(target, samples, internalFormat, width, height);
+    }
 
 
     // -------------------------------------------------------------------------
     // Texture objects - WebGL2ContextTextures.cpp
 
     void TexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width,
                       GLsizei height)
     {
-        const char funcName[] = "TexStorage2D";
+        const FuncScope funcScope(*this, "TexStorage2D");
         const uint8_t funcDims = 2;
         const GLsizei depth = 1;
-        TexStorage(funcName, funcDims, target, levels, internalFormat, width, height,
-                   depth);
+        TexStorage(funcDims, target, levels, internalFormat, width, height, depth);
     }
 
     void TexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width,
                       GLsizei height, GLsizei depth)
     {
-        const char funcName[] = "TexStorage3D";
+        const FuncScope funcScope(*this, "TexStorage3D");
         const uint8_t funcDims = 3;
-        TexStorage(funcName, funcDims, target, levels, internalFormat, width, height,
-                   depth);
+        TexStorage(funcDims, target, levels, internalFormat, width, height, depth);
     }
 
 protected:
-    void TexStorage(const char* funcName, uint8_t funcDims, GLenum target, GLsizei levels,
+    void TexStorage(uint8_t funcDims, GLenum target, GLsizei levels,
                     GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth);
 
     ////////////////////////////////////
 
 public:
     void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLsizei depth, GLint border,
                               GLsizei imageSize, WebGLintptr offset)
     {
-        const char funcName[] = "compressedTexImage3D";
+        const FuncScope funcScope(*this, "compressedTexImage3D");
         const uint8_t funcDims = 3;
         const TexImageSourceAdapter src(&offset, 0, 0);
-        CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
+        CompressedTexImage(funcDims, target, level, internalFormat, width,
                            height, depth, border, src, Some(imageSize));
     }
 
     template<typename T>
     void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLsizei depth, GLint border,
                               const T& anySrc, GLuint viewElemOffset = 0,
                               GLuint viewElemLengthOverride = 0)
     {
-        const char funcName[] = "compressedTexImage3D";
+        const FuncScope funcScope(*this, "compressedTexImage3D");
         const uint8_t funcDims = 3;
         const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
-        CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
+        CompressedTexImage(funcDims, target, level, internalFormat, width,
                            height, depth, border, src, Nothing());
     }
 
     void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLint zOffset, GLsizei width, GLsizei height,
                                  GLsizei depth, GLenum unpackFormat,
                                  GLsizei imageSize, WebGLintptr offset)
     {
-        const char funcName[] = "compressedTexSubImage3D";
+        const FuncScope funcScope(*this, "compressedTexSubImage3D");
         const uint8_t funcDims = 3;
         const TexImageSourceAdapter src(&offset, 0, 0);
-        CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
+        CompressedTexSubImage(funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src, Some(imageSize));
     }
 
     template<typename T>
     void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLint zOffset, GLsizei width, GLsizei height,
                                  GLsizei depth, GLenum unpackFormat, const T& anySrc,
                                  GLuint viewElemOffset = 0,
                                  GLuint viewElemLengthOverride = 0)
     {
-        const char funcName[] = "compressedTexSubImage3D";
+        const FuncScope funcScope(*this, "compressedTexSubImage3D");
         const uint8_t funcDims = 3;
         const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
-        CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
+        CompressedTexSubImage(funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src, Nothing());
     }
 
     ////////////////////////////////////
 
     void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                            GLint zOffset, GLint x, GLint y, GLsizei width,
                            GLsizei height)
     {
-        const char funcName[] = "copyTexSubImage3D";
+        const FuncScope funcScope(*this, "copyTexSubImage3D");
         const uint8_t funcDims = 3;
-        CopyTexSubImage(funcName, funcDims, target, level, xOffset, yOffset, zOffset,
+        CopyTexSubImage(funcDims, target, level, xOffset, yOffset, zOffset,
                         x, y, width, height);
     }
 
     ////////////////////////////////////
 
     template<typename T>
     void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
@@ -199,19 +201,19 @@ public:
                    unpackFormat, unpackType, src);
     }
 
 protected:
     void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const TexImageSource& src)
     {
-        const char funcName[] = "texImage3D";
+        const FuncScope funcScope(*this, "texImage3D");
         const uint8_t funcDims = 3;
-        TexImage(funcName, funcDims, target, level, internalFormat, width, height, depth,
+        TexImage(funcDims, target, level, internalFormat, width, height, depth,
                  border, unpackFormat, unpackType, src);
     }
 
     ////////////////////////////////////
 
 public:
     template<typename T>
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
@@ -225,78 +227,79 @@ public:
     }
 
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType,
                        const dom::Nullable<dom::ArrayBufferView>& maybeSrcView,
                        GLuint srcElemOffset, ErrorResult&)
     {
+        const FuncScope funcScope(*this, "texSubImage3D");
         if (IsContextLost())
             return;
 
-        if (!ValidateNonNull("texSubImage3D", maybeSrcView))
+        if (!ValidateNonNull("src", maybeSrcView))
             return;
         const auto& srcView = maybeSrcView.Value();
 
         const TexImageSourceAdapter src(&srcView, srcElemOffset);
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, width, height, depth,
                       unpackFormat, unpackType, src);
     }
 
 protected:
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType, const TexImageSource& src)
     {
-        const char funcName[] = "texSubImage3D";
+        const FuncScope funcScope(*this, "texSubImage3D");
         const uint8_t funcDims = 3;
-        TexSubImage(funcName, funcDims, target, level, xOffset, yOffset, zOffset, width,
+        TexSubImage(funcDims, target, level, xOffset, yOffset, zOffset, width,
                     height, depth, unpackFormat, unpackType, src);
     }
 
 public:
     // -------------------------------------------------------------------------
     // Programs and shaders - WebGL2ContextPrograms.cpp
     GLint GetFragDataLocation(const WebGLProgram& program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
 
     void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride,
                               WebGLintptr byteOffset)
     {
-        const char funcName[] = "vertexAttribIPointer";
+        const FuncScope funcScope(*this, "vertexAttribIPointer");
         const bool isFuncInt = true;
         const bool normalized = false;
-        VertexAttribAnyPointer(funcName, isFuncInt, index, size, type, normalized, stride,
+        VertexAttribAnyPointer(isFuncInt, index, size, type, normalized, stride,
                                byteOffset);
     }
 
     ////////////////
 
     // GL 3.0 & ES 3.0
-    void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w,
-                         const char* funcName = nullptr);
-    void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w,
-                          const char* funcName = nullptr);
+    void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
+    void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
 
     void VertexAttribI4iv(GLuint index, const Int32ListU& list) {
+        const FuncScope funcScope(*this, "VertexAttribI4iv");
         const auto& arr = Int32Arr::From(list);
-        if (!ValidateAttribArraySetter("vertexAttribI4iv", 4, arr.elemCount))
+        if (!ValidateAttribArraySetter(4, arr.elemCount))
             return;
 
         const auto& itr = arr.elemBytes;
         VertexAttribI4i(index, itr[0], itr[1], itr[2], itr[3]);
     }
 
     void VertexAttribI4uiv(GLuint index, const Uint32ListU& list) {
+        const FuncScope funcScope(*this, "vertexAttribI4uiv");
         const auto& arr = Uint32Arr::From(list);
-        if (!ValidateAttribArraySetter("vertexAttribI4uiv", 4, arr.elemCount))
+        if (!ValidateAttribArraySetter(4, arr.elemCount))
             return;
 
         const auto& itr = arr.elemBytes;
         VertexAttribI4ui(index, itr[0], itr[1], itr[2], itr[3]);
     }
 
     // -------------------------------------------------------------------------
     // Writing to the drawing buffer
@@ -305,36 +308,36 @@ public:
     void VertexAttribDivisor(GLuint index, GLuint divisor);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
     */
 
     void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
                            GLenum type, WebGLintptr byteOffset)
     {
-        const char funcName[] = "drawRangeElements";
+        const FuncScope funcScope(*this, "drawRangeElements");
         if (IsContextLost())
             return;
 
         if (end < start) {
-            ErrorInvalidValue("%s: end must be >= start.", funcName);
+            ErrorInvalidValue("end must be >= start.");
             return;
         }
 
-        DrawElements(mode, count, type, byteOffset, funcName);
+        DrawElements(mode, count, type, byteOffset);
     }
 
     // ------------------------------------------------------------------------
     // Multiple Render Targets - WebGL2ContextMRTs.cpp
     /* Implemented in WebGLContext
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     */
 
 private:
-    bool ValidateClearBuffer(const char* funcName, GLenum buffer, GLint drawBuffer,
+    bool ValidateClearBuffer(GLenum buffer, GLint drawBuffer,
                              size_t availElemCount, GLuint elemOffset, GLenum funcType);
 
     void ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32Arr& src,
                        GLuint srcElemOffset);
     void ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32Arr& src,
                        GLuint srcElemOffset);
     void ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32Arr& src,
                         GLuint srcElemOffset);
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -14,41 +14,41 @@ namespace mozilla {
 // -------------------------------------------------------------------------
 // Buffer objects
 
 void
 WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
                                  GLintptr readOffset, GLintptr writeOffset,
                                  GLsizeiptr size)
 {
-    const char funcName[] = "copyBufferSubData";
+    const FuncScope funcScope(*this, "copyBufferSubData");
     if (IsContextLost())
         return;
 
-    const auto& readBuffer = ValidateBufferSelection(funcName, readTarget);
+    const auto& readBuffer = ValidateBufferSelection(readTarget);
     if (!readBuffer)
         return;
 
-    const auto& writeBuffer = ValidateBufferSelection(funcName, writeTarget);
+    const auto& writeBuffer = ValidateBufferSelection(writeTarget);
     if (!writeBuffer)
         return;
 
-    if (!ValidateNonNegative(funcName, "readOffset", readOffset) ||
-        !ValidateNonNegative(funcName, "writeOffset", writeOffset) ||
-        !ValidateNonNegative(funcName, "size", size))
+    if (!ValidateNonNegative("readOffset", readOffset) ||
+        !ValidateNonNegative("writeOffset", writeOffset) ||
+        !ValidateNonNegative("size", size))
     {
         return;
     }
 
     const auto fnValidateOffsetSize = [&](const char* info, GLintptr offset,
                                           const WebGLBuffer* buffer)
     {
         const auto neededBytes = CheckedInt<size_t>(offset) + size;
         if (!neededBytes.isValid() || neededBytes.value() > buffer->ByteLength()) {
-            ErrorInvalidValue("%s: Invalid %s range.", funcName, info);
+            ErrorInvalidValue("Invalid %s range.", info);
             return false;
         }
         return true;
     };
 
     if (!fnValidateOffsetSize("read", readOffset, readBuffer) ||
         !fnValidateOffsetSize("write", writeOffset, writeBuffer))
     {
@@ -57,30 +57,28 @@ WebGL2Context::CopyBufferSubData(GLenum 
 
     if (readBuffer == writeBuffer) {
         MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(readOffset) + size).isValid());
         MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
 
         const bool separate = (readOffset + size <= writeOffset ||
                                writeOffset + size <= readOffset);
         if (!separate) {
-            ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and"
-                              " [writeOffset, writeOffset + size) overlap",
-                              funcName);
+            ErrorInvalidValue("Ranges [readOffset, readOffset + size) and"
+                              " [writeOffset, writeOffset + size) overlap.");
             return;
         }
     }
 
     const auto& readType = readBuffer->Content();
     const auto& writeType = writeBuffer->Content();
     MOZ_ASSERT(readType != WebGLBuffer::Kind::Undefined);
     MOZ_ASSERT(writeType != WebGLBuffer::Kind::Undefined);
     if (writeType != readType) {
-        ErrorInvalidOperation("%s: Can't copy %s data to %s data.",
-                              funcName,
+        ErrorInvalidOperation("Can't copy %s data to %s data.",
                               (readType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                          : "element",
                               (writeType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                           : "element");
         return;
     }
 
     const ScopedLazyBind readBind(gl, readTarget, readBuffer);
@@ -90,65 +88,63 @@ WebGL2Context::CopyBufferSubData(GLenum 
     writeBuffer->ResetLastUpdateFenceId();
 }
 
 void
 WebGL2Context::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
                                 const dom::ArrayBufferView& dstData, GLuint dstElemOffset,
                                 GLuint dstElemCountOverride)
 {
-    const char funcName[] = "getBufferSubData";
+    const FuncScope funcScope(*this, "getBufferSubData");
     if (IsContextLost())
         return;
 
-    if (!ValidateNonNegative(funcName, "srcByteOffset", srcByteOffset))
+    if (!ValidateNonNegative("srcByteOffset", srcByteOffset))
         return;
 
     uint8_t* bytes;
     size_t byteLen;
-    if (!ValidateArrayBufferView(funcName, dstData, dstElemOffset, dstElemCountOverride,
+    if (!ValidateArrayBufferView(dstData, dstElemOffset, dstElemCountOverride,
                                  &bytes, &byteLen))
     {
         return;
     }
 
     ////
 
-    const auto& buffer = ValidateBufferSelection(funcName, target);
+    const auto& buffer = ValidateBufferSelection(target);
     if (!buffer)
         return;
 
-    if (!buffer->ValidateRange(funcName, srcByteOffset, byteLen))
+    if (!buffer->ValidateRange(srcByteOffset, byteLen))
         return;
 
     ////
 
     if (!CheckedInt<GLsizeiptr>(byteLen).isValid()) {
-        ErrorOutOfMemory("%s: Size too large.", funcName);
+        ErrorOutOfMemory("Size too large.");
         return;
     }
     const GLsizeiptr glByteLen(byteLen);
 
     ////
 
     switch (buffer->mUsage) {
     case LOCAL_GL_STATIC_READ:
     case LOCAL_GL_STREAM_READ:
     case LOCAL_GL_DYNAMIC_READ:
         if (mCompletedFenceId < buffer->mLastUpdateFenceId) {
-            GenerateWarning("%s: Reading from a buffer without checking for previous"
+            GenerateWarning("Reading from a buffer without checking for previous"
                             " command completion likely causes pipeline stalls."
-                            " Please use FenceSync.",
-                            funcName);
+                            " Please use FenceSync.");
         }
         break;
     default:
-        GenerateWarning("%s: Reading from a buffer with usage other than *_READ"
-                        " causes pipeline stalls. Copy through a STREAM_READ buffer.",
-                        funcName);
+        GenerateWarning("Reading from a buffer with usage other than *_READ"
+                        " causes pipeline stalls. Copy through a STREAM_READ buffer.");
         break;
     }
 
     ////
 
     const ScopedLazyBind readBind(gl, target, buffer);
 
     if (byteLen) {
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -14,56 +14,56 @@
 
 namespace mozilla {
 
 void
 WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                                GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                GLbitfield mask, GLenum filter)
 {
+    const FuncScope funcScope(*this, "blitFramebuffer");
     if (IsContextLost())
         return;
 
     const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT |
                                  LOCAL_GL_DEPTH_BUFFER_BIT |
                                  LOCAL_GL_STENCIL_BUFFER_BIT;
     if ((mask | validBits) != validBits) {
-        ErrorInvalidValue("blitFramebuffer: Invalid bit set in mask.");
+        ErrorInvalidValue("Invalid bit set in mask.");
         return;
     }
 
     switch (filter) {
     case LOCAL_GL_NEAREST:
     case LOCAL_GL_LINEAR:
         break;
     default:
-        ErrorInvalidEnumInfo("blitFramebuffer: Bad `filter`:", filter);
+        ErrorInvalidEnumInfo("filter", filter);
         return;
     }
 
     // --
 
     const auto fnLikelyOverflow = [](GLint p0, GLint p1) {
         auto checked = CheckedInt<GLint>(p1) - p0;
         checked = -checked; // And check the negation!
         return !checked.isValid();
     };
 
     if (fnLikelyOverflow(srcX0, srcX1) || fnLikelyOverflow(srcY0, srcY1) ||
         fnLikelyOverflow(dstX0, dstX1) || fnLikelyOverflow(dstY0, dstY1))
     {
-        ErrorInvalidValue("blitFramebuffer: Likely-to-overflow large ranges are"
-                          " forbidden.");
+        ErrorInvalidValue("Likely-to-overflow large ranges are forbidden.");
         return;
     }
 
     // --
 
-    if (!ValidateAndInitFB("blitFramebuffer: READ_FRAMEBUFFER", mBoundReadFramebuffer) ||
-        !ValidateAndInitFB("blitFramebuffer: DRAW_FRAMEBUFFER", mBoundDrawFramebuffer))
+    if (!ValidateAndInitFB(mBoundReadFramebuffer) ||
+        !ValidateAndInitFB(mBoundDrawFramebuffer))
     {
         return;
     }
 
     DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER);
     DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER);
 
     WebGLFramebuffer::BlitFramebuffer(this,
@@ -71,21 +71,21 @@ WebGL2Context::BlitFramebuffer(GLint src
                                       dstX0, dstY0, dstX1, dstY1,
                                       mask, filter);
 }
 
 void
 WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
                                        WebGLTexture* texture, GLint level, GLint layer)
 {
-    const char funcName[] = "framebufferTextureLayer";
+    const FuncScope funcScope(*this, "framebufferTextureLayer");
     if (IsContextLost())
         return;
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return;
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -94,90 +94,86 @@ WebGL2Context::FramebufferTextureLayer(G
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (!fb)
-        return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
+        return ErrorInvalidOperation("Cannot modify framebuffer 0.");
 
-    fb->FramebufferTextureLayer(funcName, attachment, texture, level, layer);
+    fb->FramebufferTextureLayer(attachment, texture, level, layer);
 }
 
 JS::Value
 WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                  GLenum target,
                                                  GLenum attachment,
                                                  GLenum pname,
                                                  ErrorResult& out_error)
 {
     return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname,
                                                            out_error);
 }
 
 ////
 
 static bool
-ValidateBackbufferAttachmentEnum(WebGLContext* webgl, const char* funcName,
-                                 GLenum attachment)
+ValidateBackbufferAttachmentEnum(WebGLContext* webgl, GLenum attachment)
 {
     switch (attachment) {
     case LOCAL_GL_COLOR:
     case LOCAL_GL_DEPTH:
     case LOCAL_GL_STENCIL:
         return true;
 
     default:
-        webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
-                                funcName, attachment);
+        webgl->ErrorInvalidEnumInfo("attachment", attachment);
         return false;
     }
 }
 
 static bool
-ValidateFramebufferAttachmentEnum(WebGLContext* webgl, const char* funcName,
+ValidateFramebufferAttachmentEnum(WebGLContext* webgl,
                                   GLenum attachment)
 {
     switch (attachment) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
     case LOCAL_GL_STENCIL_ATTACHMENT:
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         return true;
     }
 
     if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) {
-        webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
-                                funcName, attachment);
+        webgl->ErrorInvalidEnumInfo("attachment", attachment);
         return false;
     }
 
     if (attachment > webgl->LastColorAttachmentEnum()) {
         // That these errors have different types is ridiculous.
-        webgl->ErrorInvalidOperation("%s: Too-large LOCAL_GL_COLOR_ATTACHMENTn.",
-                                     funcName);
+        webgl->ErrorInvalidOperation("Too-large LOCAL_GL_COLOR_ATTACHMENTn.");
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLContext::ValidateInvalidateFramebuffer(const char* funcName, GLenum target,
+WebGLContext::ValidateInvalidateFramebuffer(GLenum target,
                                             const dom::Sequence<GLenum>& attachments,
                                             ErrorResult* const out_rv,
                                             std::vector<GLenum>* const scopedVector,
                                             GLsizei* const out_glNumAttachments,
                                             const GLenum** const out_glAttachments)
 {
     if (IsContextLost())
         return false;
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return false;
 
     const WebGLFramebuffer* fb;
     bool isDefaultFB = false;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
@@ -187,36 +183,36 @@ WebGLContext::ValidateInvalidateFramebuf
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (fb) {
-        const auto fbStatus = fb->CheckFramebufferStatus(funcName);
+        const auto fbStatus = fb->CheckFramebufferStatus();
         if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
             return false; // Not an error, but don't run forward to driver either.
     } else {
-        if (!EnsureDefaultFB(funcName))
+        if (!EnsureDefaultFB())
             return false;
     }
     DoBindFB(fb, target);
 
     *out_glNumAttachments = attachments.Length();
     *out_glAttachments = attachments.Elements();
 
     if (fb) {
         for (const auto& attachment : attachments) {
-            if (!ValidateFramebufferAttachmentEnum(this, funcName, attachment))
+            if (!ValidateFramebufferAttachmentEnum(this, attachment))
                 return false;
         }
     } else {
         for (const auto& attachment : attachments) {
-            if (!ValidateBackbufferAttachmentEnum(this, funcName, attachment))
+            if (!ValidateBackbufferAttachmentEnum(this, attachment))
                 return false;
         }
 
         if (!isDefaultFB) {
             MOZ_ASSERT(scopedVector->empty());
             scopedVector->reserve(attachments.Length());
             for (const auto& attachment : attachments) {
                 switch (attachment) {
@@ -250,22 +246,22 @@ WebGLContext::ValidateInvalidateFramebuf
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
                                      ErrorResult& rv)
 {
-    const char funcName[] = "invalidateSubFramebuffer";
+    const FuncScope funcScope(*this, "invalidateFramebuffer");
 
     std::vector<GLenum> scopedVector;
     GLsizei glNumAttachments;
     const GLenum* glAttachments;
-    if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector,
+    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
                                        &glNumAttachments, &glAttachments))
     {
         return;
     }
 
     ////
 
     // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
@@ -280,29 +276,29 @@ WebGL2Context::InvalidateFramebuffer(GLe
     // No-op for now.
 }
 
 void
 WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                         GLint x, GLint y, GLsizei width, GLsizei height,
                                         ErrorResult& rv)
 {
-    const char funcName[] = "invalidateSubFramebuffer";
+    const FuncScope funcScope(*this, "invalidateSubFramebuffer");
 
-    if (!ValidateNonNegative(funcName, "width", width) ||
-        !ValidateNonNegative(funcName, "height", height))
+    std::vector<GLenum> scopedVector;
+    GLsizei glNumAttachments;
+    const GLenum* glAttachments;
+    if (!ValidateInvalidateFramebuffer(target, attachments, &rv, &scopedVector,
+                                       &glNumAttachments, &glAttachments))
     {
         return;
     }
 
-    std::vector<GLenum> scopedVector;
-    GLsizei glNumAttachments;
-    const GLenum* glAttachments;
-    if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector,
-                                       &glNumAttachments, &glAttachments))
+    if (!ValidateNonNegative("width", width) ||
+        !ValidateNonNegative("height", height))
     {
         return;
     }
 
     ////
 
     // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
     const bool useFBInvalidation = (mAllowFBInvalidation &&
@@ -315,33 +311,33 @@ WebGL2Context::InvalidateSubFramebuffer(
 
     // Use clear instead?
     // No-op for now.
 }
 
 void
 WebGL2Context::ReadBuffer(GLenum mode)
 {
-    const char funcName[] = "readBuffer";
+    const FuncScope funcScope(*this, "readBuffer");
     if (IsContextLost())
         return;
 
     if (mBoundReadFramebuffer) {
-        mBoundReadFramebuffer->ReadBuffer(funcName, mode);
+        mBoundReadFramebuffer->ReadBuffer(mode);
         return;
     }
 
     // Operating on the default framebuffer.
     if (mode != LOCAL_GL_NONE &&
         mode != LOCAL_GL_BACK)
     {
         nsCString enumName;
         EnumName(mode, &enumName);
-        ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or"
+        ErrorInvalidOperation("If READ_FRAMEBUFFER is null, `mode` must be BACK or"
                               " NONE. Was %s.",
-                              funcName, enumName.BeginReading());
+                              enumName.BeginReading());
         return;
     }
 
     mDefaultFB_ReadBuffer = mode;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextMRTs.cpp
+++ b/dom/canvas/WebGL2ContextMRTs.cpp
@@ -6,22 +6,22 @@
 #include "WebGL2Context.h"
 
 #include "GLContext.h"
 #include "WebGLFramebuffer.h"
 
 namespace mozilla {
 
 bool
-WebGL2Context::ValidateClearBuffer(const char* funcName, GLenum buffer, GLint drawBuffer,
+WebGL2Context::ValidateClearBuffer(GLenum buffer, GLint drawBuffer,
                                    size_t availElemCount, GLuint elemOffset,
                                    GLenum funcType)
 {
     if (elemOffset > availElemCount) {
-        ErrorInvalidValue("%s: Offset too big for list.", funcName);
+        ErrorInvalidValue("Offset too big for list.");
         return false;
     }
     availElemCount -= elemOffset;
 
     ////
 
     size_t requiredElements;
     GLint maxDrawBuffer;
@@ -38,78 +38,77 @@ WebGL2Context::ValidateClearBuffer(const
           break;
 
     case LOCAL_GL_DEPTH_STENCIL:
           requiredElements = 2;
           maxDrawBuffer = 0;
           break;
 
     default:
-          ErrorInvalidEnumInfo(funcName, buffer);
+          ErrorInvalidEnumInfo("buffer", buffer);
           return false;
     }
 
     if (drawBuffer < 0 || drawBuffer > maxDrawBuffer) {
-        ErrorInvalidValue("%s: Invalid drawbuffer %d. This buffer only supports"
+        ErrorInvalidValue("Invalid drawbuffer %d. This buffer only supports"
                           " `drawbuffer` values between 0 and %u.",
-                          funcName, drawBuffer, maxDrawBuffer);
+                          drawBuffer, maxDrawBuffer);
         return false;
     }
 
     if (availElemCount < requiredElements) {
-        ErrorInvalidValue("%s: Not enough elements. Require %zu. Given %zu.",
-                          funcName, requiredElements, availElemCount);
+        ErrorInvalidValue("Not enough elements. Require %zu. Given %zu.",
+                          requiredElements, availElemCount);
         return false;
     }
 
     ////
 
-    if (!BindCurFBForDraw(funcName))
+    if (!BindCurFBForDraw())
         return false;
 
     const auto& fb = mBoundDrawFramebuffer;
     if (fb) {
-        if (!fb->ValidateClearBufferType(funcName, buffer, drawBuffer, funcType))
+        if (!fb->ValidateClearBufferType(buffer, drawBuffer, funcType))
             return false;
     } else if (buffer == LOCAL_GL_COLOR) {
         if (drawBuffer != 0)
             return true;
 
         if (mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE)
             return true;
 
         if (funcType != LOCAL_GL_FLOAT) {
-            ErrorInvalidOperation("%s: For default framebuffer, COLOR is always of type"
-                                  " FLOAT.",
-                                  funcName);
+            ErrorInvalidOperation("For default framebuffer, COLOR is always of type"
+                                  " FLOAT.");
             return false;
         }
     }
 
     return true;
 }
 
 ////
 
 void
 WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32Arr& src,
                              GLuint srcElemOffset)
 {
-    const char funcName[] = "clearBufferfv";
+    const FuncScope funcScope(*this, "clearBufferfv");
     if (IsContextLost())
         return;
 
     if (buffer != LOCAL_GL_COLOR &&
         buffer != LOCAL_GL_DEPTH)
     {
-        ErrorInvalidEnum("%s: buffer must be COLOR or DEPTH.", funcName);
+        ErrorInvalidEnum("`buffer` must be COLOR or DEPTH.");
         return;
     }
 
-    if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset,
+    if (!ValidateClearBuffer(buffer, drawBuffer, src.elemCount, srcElemOffset,
                              LOCAL_GL_FLOAT))
     {
         return;
     }
 
     if (!mBoundDrawFramebuffer &&
         buffer == LOCAL_GL_DEPTH &&
         mNeedsFakeNoDepth)
@@ -121,28 +120,28 @@ WebGL2Context::ClearBufferfv(GLenum buff
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferfv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32Arr& src,
                              GLuint srcElemOffset)
 {
-    const char funcName[] = "clearBufferiv";
+    const FuncScope funcScope(*this, "clearBufferiv");
     if (IsContextLost())
         return;
 
     if (buffer != LOCAL_GL_COLOR &&
         buffer != LOCAL_GL_STENCIL)
     {
-        ErrorInvalidEnum("%s: buffer must be COLOR or STENCIL.", funcName);
+        ErrorInvalidEnum("`buffer` must be COLOR or STENCIL.");
         return;
     }
 
-    if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset,
+    if (!ValidateClearBuffer(buffer, drawBuffer, src.elemCount, srcElemOffset,
                              LOCAL_GL_INT))
     {
         return;
     }
 
     if (!mBoundDrawFramebuffer &&
         buffer == LOCAL_GL_STENCIL &&
         mNeedsFakeNoStencil)
@@ -154,48 +153,48 @@ WebGL2Context::ClearBufferiv(GLenum buff
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferiv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32Arr& src,
                               GLuint srcElemOffset)
 {
-    const char funcName[] = "clearBufferuiv";
+    const FuncScope funcScope(*this, "clearBufferuiv");
     if (IsContextLost())
         return;
 
     if (buffer != LOCAL_GL_COLOR)
-        return ErrorInvalidEnum("%s: buffer must be COLOR.", funcName);
+        return ErrorInvalidEnum("`buffer` must be COLOR.");
 
-    if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset,
+    if (!ValidateClearBuffer(buffer, drawBuffer, src.elemCount, srcElemOffset,
                              LOCAL_GL_UNSIGNED_INT))
     {
         return;
     }
 
     ScopedDrawCallWrapper wrapper(*this);
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferuiv(buffer, drawBuffer, ptr);
 }
 
 ////
 
 void
 WebGL2Context::ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth,
                              GLint stencil)
 {
-    const char funcName[] = "clearBufferfi";
+    const FuncScope funcScope(*this, "clearBufferfi");
     if (IsContextLost())
         return;
 
     if (buffer != LOCAL_GL_DEPTH_STENCIL)
-        return ErrorInvalidEnum("%s: buffer must be DEPTH_STENCIL.", funcName);
+        return ErrorInvalidEnum("`buffer` must be DEPTH_STENCIL.");
 
-    if (!ValidateClearBuffer(funcName, buffer, drawBuffer, 2, 0, 0))
+    if (!ValidateClearBuffer(buffer, drawBuffer, 2, 0, 0))
         return;
 
     auto driverDepth = depth;
     auto driverStencil = stencil;
     if (!mBoundDrawFramebuffer) {
         if (mNeedsFakeNoDepth) {
             driverDepth = 1.0f;
         } else if (mNeedsFakeNoStencil) {
--- a/dom/canvas/WebGL2ContextPrograms.cpp
+++ b/dom/canvas/WebGL2ContextPrograms.cpp
@@ -11,18 +11,19 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Programs and shaders
 
 GLint
 WebGL2Context::GetFragDataLocation(const WebGLProgram& prog, const nsAString& name)
 {
+    const FuncScope funcScope(*this, "getFragDataLocation");
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObject("getFragDataLocation: program", prog))
+    if (!ValidateObject("program", prog))
         return -1;
 
     return prog.GetFragDataLocation(name);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextQueries.cpp
+++ b/dom/canvas/WebGL2ContextQueries.cpp
@@ -18,17 +18,17 @@ namespace mozilla {
  * OpenGL ES 3.0 spec 4.1.6:
  *     If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an
  *     implementation may choose to use a less precise version of the test which
  *     can additionally set the samples-boolean state to TRUE in some other
  *     implementation-dependent cases.
  */
 
 WebGLRefPtr<WebGLQuery>*
-WebGLContext::ValidateQuerySlotByTarget(const char* funcName, GLenum target)
+WebGLContext::ValidateQuerySlotByTarget(GLenum target)
 {
     if (IsWebGL2()) {
         switch (target) {
         case LOCAL_GL_ANY_SAMPLES_PASSED:
         case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
             return &mQuerySlot_SamplesPassed;
 
         case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
@@ -44,117 +44,90 @@ WebGLContext::ValidateQuerySlotByTarget(
         case LOCAL_GL_TIME_ELAPSED_EXT:
             return &mQuerySlot_TimeElapsed;
 
         default:
             break;
         }
     }
 
-    ErrorInvalidEnum("%s: Bad `target`.", funcName);
+    ErrorInvalidEnumInfo("target", target);
     return nullptr;
 }
 
 
 // -------------------------------------------------------------------------
 // Query Objects
 
 already_AddRefed<WebGLQuery>
-WebGLContext::CreateQuery(const char* funcName)
+WebGLContext::CreateQuery()
 {
-    if (!funcName) {
-        funcName = "createQuery";
-    }
-
+    const FuncScope funcScope(*this, "createQuery");
     if (IsContextLost())
         return nullptr;
 
     RefPtr<WebGLQuery> globj = new WebGLQuery(this);
     return globj.forget();
 }
 
 void
-WebGLContext::DeleteQuery(WebGLQuery* query, const char* funcName)
+WebGLContext::DeleteQuery(WebGLQuery* query)
 {
-    if (!funcName) {
-        funcName = "deleteQuery";
-    }
-
-    if (!ValidateDeleteObject(funcName, query))
+    const FuncScope funcScope(*this, "deleteQuery");
+    if (!ValidateDeleteObject(query))
         return;
 
     query->DeleteQuery();
 }
 
-bool
-WebGLContext::IsQuery(const WebGLQuery* query, const char* funcName)
+void
+WebGLContext::BeginQuery(GLenum target, WebGLQuery& query)
 {
-    if (!funcName) {
-        funcName = "isQuery";
-    }
-
-    if (!ValidateIsObject(funcName, query))
-        return false;
-
-    return query->IsQuery();
-}
-
-void
-WebGLContext::BeginQuery(GLenum target, WebGLQuery& query, const char* funcName)
-{
-    if (!funcName) {
-        funcName = "beginQuery";
-    }
-
+    const FuncScope funcScope(*this, "beginQuery");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, query))
+    if (!ValidateObject("query", query))
         return;
 
-    const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+    const auto& slot = ValidateQuerySlotByTarget(target);
     if (!slot)
         return;
 
     if (*slot)
-        return ErrorInvalidOperation("%s: Query target already active.", funcName);
+        return ErrorInvalidOperation("Query target already active.");
 
     ////
 
     query.BeginQuery(target, *slot);
 }
 
 void
-WebGLContext::EndQuery(GLenum target, const char* funcName)
+WebGLContext::EndQuery(GLenum target)
 {
-    if (!funcName) {
-        funcName = "endQuery";
-    }
-
+    const FuncScope funcScope(*this, "endQuery");
     if (IsContextLost())
         return;
 
-    const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+    const auto& slot = ValidateQuerySlotByTarget(target);
     if (!slot)
         return;
 
     const auto& query = *slot;
     if (!query)
-        return ErrorInvalidOperation("%s: Query target not active.", funcName);
+        return ErrorInvalidOperation("Query target not active.");
 
     query->EndQuery();
 }
 
 void
 WebGLContext::GetQuery(JSContext* cx, GLenum target, GLenum pname,
-                       JS::MutableHandleValue retval, const char* funcName)
+                       JS::MutableHandleValue retval)
 {
-    if (!funcName) {
-        funcName = "getQuery";
-    }
+    const FuncScope funcScope(*this, "getQuery");
 
     retval.setNull();
     if (IsContextLost())
         return;
 
     switch (pname) {
     case LOCAL_GL_CURRENT_QUERY_EXT:
         {
@@ -162,17 +135,17 @@ WebGLContext::GetQuery(JSContext* cx, GL
                 target == LOCAL_GL_TIMESTAMP)
             {
                 // Doesn't seem illegal to ask about, but is always null.
                 // TIMESTAMP has no slot, so ValidateQuerySlotByTarget would generate
                 // INVALID_ENUM.
                 return;
             }
 
-            const auto& slot = ValidateQuerySlotByTarget(funcName, target);
+            const auto& slot = ValidateQuerySlotByTarget(target);
             if (!slot || !*slot)
                 return;
 
             const auto& query = *slot;
             if (target != query->Target())
                 return;
 
             JS::Rooted<JS::Value> v(cx);
@@ -183,17 +156,17 @@ WebGLContext::GetQuery(JSContext* cx, GL
 
     case LOCAL_GL_QUERY_COUNTER_BITS_EXT:
         if (!IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query))
             break;
 
         if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
             target != LOCAL_GL_TIMESTAMP_EXT)
         {
-            ErrorInvalidEnum("%s: Bad pname for target.", funcName);
+            ErrorInvalidEnumInfo("target", target);
             return;
         }
 
         {
             GLint bits = 0;
             gl->fGetQueryiv(target, pname, &bits);
 
             if (!Has64BitTimestamps() && bits > 32) {
@@ -202,30 +175,27 @@ WebGLContext::GetQuery(JSContext* cx, GL
             retval.set(JS::Int32Value(bits));
         }
         return;
 
     default:
         break;
     }
 
-    ErrorInvalidEnum("%s: Bad pname.", funcName);
+    ErrorInvalidEnumInfo("pname", pname);
 }
 
 void
 WebGLContext::GetQueryParameter(JSContext*, const WebGLQuery& query, GLenum pname,
-                                JS::MutableHandleValue retval, const char* funcName)
+                                JS::MutableHandleValue retval)
 {
-    if (!funcName) {
-        funcName = "getQueryParameter";
-    }
-
+    const FuncScope funcScope(*this, "getQueryParameter");
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, query))
+    if (!ValidateObject("query", query))
         return;
 
     query.GetQueryParameter(pname, retval);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextRenderbuffers.cpp
+++ b/dom/canvas/WebGL2ContextRenderbuffers.cpp
@@ -12,25 +12,24 @@
 namespace mozilla {
 
 void
 WebGL2Context::GetInternalformatParameter(JSContext* cx, GLenum target,
                                           GLenum internalformat, GLenum pname,
                                           JS::MutableHandleValue retval,
                                           ErrorResult& out_rv)
 {
-    const char funcName[] = "getInternalfomratParameter";
+    const FuncScope funcScope(*this, "getInternalfomratParameter");
     retval.setObjectOrNull(nullptr);
 
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_RENDERBUFFER) {
-        ErrorInvalidEnum("%s: `target` must be RENDERBUFFER, was: 0x%04x.", funcName,
-                         target);
+        ErrorInvalidEnum("`target` must be RENDERBUFFER.");
         return;
     }
 
     // GLES 3.0.4 $4.4.4 p212:
     // "An internal format is color-renderable if it is one of the formats from table 3.13
     //  noted as color-renderable or if it is unsized format RGBA or RGB."
 
     GLenum sizedFormat;
@@ -46,23 +45,23 @@ WebGL2Context::GetInternalformatParamete
         break;
     }
 
     // In RenderbufferStorage, we allow DEPTH_STENCIL. Therefore, it is accepted for
     // internalformat as well. Please ignore the conformance test fail for DEPTH_STENCIL.
 
     const auto usage = mFormatUsage->GetRBUsage(sizedFormat);
     if (!usage) {
-        ErrorInvalidEnum("%s: `internalformat` must be color-, depth-, or stencil-renderable, was: 0x%04x.",
-                         funcName, internalformat);
+        ErrorInvalidEnum("`internalformat` must be color-, depth-, or stencil-renderable, was: 0x%04x.",
+                         internalformat);
         return;
     }
 
     if (pname != LOCAL_GL_SAMPLES) {
-        ErrorInvalidEnumInfo("%s: `pname` must be SAMPLES, was 0x%04x.", funcName, pname);
+        ErrorInvalidEnum("`pname` must be SAMPLES.");
         return;
     }
 
     GLint* samples = nullptr;
     GLint sampleCount = 0;
     gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalformat,
                              LOCAL_GL_NUM_SAMPLE_COUNTS, 1, &sampleCount);
     if (sampleCount > 0) {
@@ -76,21 +75,9 @@ WebGL2Context::GetInternalformatParamete
         out_rv = NS_ERROR_OUT_OF_MEMORY;
     }
 
     delete[] samples;
 
     retval.setObjectOrNull(obj);
 }
 
-void
-WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
-                                              GLenum internalFormat,
-                                              GLsizei width, GLsizei height)
-{
-    const char funcName[] = "renderbufferStorageMultisample";
-    if (IsContextLost())
-        return;
-
-    RenderbufferStorage_base(funcName, target, samples, internalFormat, width, height);
-}
-
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -7,106 +7,110 @@
 #include "WebGLSampler.h"
 #include "GLContext.h"
 
 namespace mozilla {
 
 already_AddRefed<WebGLSampler>
 WebGL2Context::CreateSampler()
 {
+    const FuncScope funcScope(*this, "createSampler");
     if (IsContextLost())
         return nullptr;
 
     RefPtr<WebGLSampler> globj = new WebGLSampler(this);
     return globj.forget();
 }
 
 void
 WebGL2Context::DeleteSampler(WebGLSampler* sampler)
 {
-    if (!ValidateDeleteObject("deleteSampler", sampler))
+    const FuncScope funcScope(*this, "deleteSampler");
+    if (!ValidateDeleteObject(sampler))
         return;
 
     for (uint32_t n = 0; n < mGLMaxTextureUnits; n++) {
         if (mBoundSamplers[n] == sampler) {
             mBoundSamplers[n] = nullptr;
 
             InvalidateResolveCacheForTextureWithTexUnit(n);
         }
     }
 
     sampler->RequestDelete();
 }
 
 bool
-WebGL2Context::IsSampler(const WebGLSampler* sampler)
+WebGL2Context::IsSampler(const WebGLSampler* const obj)
 {
-    if (!ValidateIsObject("isSampler", sampler))
+    const FuncScope funcScope(*this, "isSampler");
+    if (!ValidateIsObject(obj))
         return false;
 
-    return gl->fIsSampler(sampler->mGLName);
+    return gl->fIsSampler(obj->mGLName);
 }
 
 void
 WebGL2Context::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
+    const FuncScope funcScope(*this, "bindSampler");
     if (IsContextLost())
         return;
 
-    if (sampler && !ValidateObject("bindSampler", *sampler))
+    if (sampler && !ValidateObject("sampler", *sampler))
         return;
 
     if (unit >= mGLMaxTextureUnits)
-        return ErrorInvalidValue("bindSampler: unit must be < %u", mGLMaxTextureUnits);
+        return ErrorInvalidValue("unit must be < %u", mGLMaxTextureUnits);
 
     ////
 
     gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
 
     InvalidateResolveCacheForTextureWithTexUnit(unit);
     mBoundSamplers[unit] = sampler;
 }
 
 void
 WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint param)
 {
-    const char funcName[] = "samplerParameteri";
+    const FuncScope funcScope(*this, "samplerParameteri");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, sampler))
+    if (!ValidateObject("sampler", sampler))
         return;
 
-    sampler.SamplerParameter(funcName, pname, FloatOrInt(param));
+    sampler.SamplerParameter(pname, FloatOrInt(param));
 }
 
 void
 WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param)
 {
-    const char funcName[] = "samplerParameterf";
+    const FuncScope funcScope(*this, "samplerParameterf");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, sampler))
+    if (!ValidateObject("sampler", sampler))
         return;
 
-    sampler.SamplerParameter(funcName, pname, FloatOrInt(param));
+    sampler.SamplerParameter(pname, FloatOrInt(param));
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                                    JS::MutableHandleValue retval)
 {
-    const char funcName[] = "getSamplerParameter";
+    const FuncScope funcScope(*this, "getSamplerParameter");
     retval.setNull();
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, sampler))
+    if (!ValidateObject("sampler", sampler))
         return;
 
     ////
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
     case LOCAL_GL_TEXTURE_MAG_FILTER:
     case LOCAL_GL_TEXTURE_WRAP_S:
@@ -126,14 +130,14 @@ WebGL2Context::GetSamplerParameter(JSCon
         {
             GLfloat param = 0;
             gl->fGetSamplerParameterfv(sampler.mGLName, pname, &param);
             retval.set(JS::Float32Value(param));
         }
         return;
 
     default:
-        ErrorInvalidEnumArg(funcName, "pname", pname);
+        ErrorInvalidEnumInfo("pname", pname);
         return;
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -14,16 +14,17 @@
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 JS::Value
 WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
 {
+  const FuncScope funcScope(*this, "getParameter");
   // The following cases are handled in WebGLContext::GetParameter():
   //     case LOCAL_GL_MAX_COLOR_ATTACHMENTS:
   //     case LOCAL_GL_MAX_DRAW_BUFFERS:
   //     case LOCAL_GL_DRAW_BUFFERi:
 
   if (IsContextLost())
     return JS::NullValue();
 
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -11,83 +11,82 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Sync objects
 
 already_AddRefed<WebGLSync>
 WebGL2Context::FenceSync(GLenum condition, GLbitfield flags)
 {
+    const FuncScope funcScope(*this, "fenceSync");
     if (IsContextLost())
         return nullptr;
 
     if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
-        ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
+        ErrorInvalidEnum("condition must be SYNC_GPU_COMMANDS_COMPLETE");
         return nullptr;
     }
 
     if (flags != 0) {
-        ErrorInvalidValue("fenceSync: flags must be 0");
+        ErrorInvalidValue("flags must be 0");
         return nullptr;
     }
 
     RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
 
     const auto& availRunnable = EnsureAvailabilityRunnable();
     availRunnable->mSyncs.push_back(globj);
 
     return globj.forget();
 }
 
 bool
-WebGL2Context::IsSync(const WebGLSync* sync)
+WebGL2Context::IsSync(const WebGLSync* const sync)
 {
-    if (!ValidateIsObject("isSync", sync))
-        return false;
-
-    return true;
+    const FuncScope funcScope(*this, "isSync");
+    return ValidateIsObject(sync);
 }
 
 void
 WebGL2Context::DeleteSync(WebGLSync* sync)
 {
-    if (!ValidateDeleteObject("deleteSync", sync))
+    const FuncScope funcScope(*this, "deleteSync");
+    if (!ValidateDeleteObject(sync))
         return;
 
     sync->RequestDelete();
 }
 
 GLenum
 WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout)
 {
-    const char funcName[] = "clientWaitSync";
+    const FuncScope funcScope(*this, "clientWaitSync");
     if (IsContextLost())
         return LOCAL_GL_WAIT_FAILED;
 
-    if (!ValidateObject(funcName, sync))
+    if (!ValidateObject("sync", sync))
         return LOCAL_GL_WAIT_FAILED;
 
     if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
-        ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName);
+        ErrorInvalidValue("`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
         return LOCAL_GL_WAIT_FAILED;
     }
 
     if (timeout > kMaxClientWaitSyncTimeoutNS) {
-        ErrorInvalidOperation("%s: `timeout` must not exceed %s nanoseconds.", funcName,
+        ErrorInvalidOperation("`timeout` must not exceed %s nanoseconds.",
                               "MAX_CLIENT_WAIT_TIMEOUT_WEBGL");
         return LOCAL_GL_WAIT_FAILED;
     }
 
     const bool canBeAvailable = (sync.mCanBeAvailable ||
                                  gfxPrefs::WebGLImmediateQueries());
     if (!canBeAvailable) {
         if (timeout) {
-            GenerateWarning("%s: Sync object not yet queryable. Please wait for the event"
-                            " loop.",
-                            funcName);
+            GenerateWarning("Sync object not yet queryable. Please wait for the event"
+                            " loop.");
         }
         return LOCAL_GL_WAIT_FAILED;
     }
 
     const auto ret = gl->fClientWaitSync(sync.mGLName, flags, timeout);
 
     if (ret == LOCAL_GL_CONDITION_SATISFIED ||
         ret == LOCAL_GL_ALREADY_SIGNALED)
@@ -96,46 +95,46 @@ WebGL2Context::ClientWaitSync(const WebG
     }
 
     return ret;
 }
 
 void
 WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout)
 {
-    const char funcName[] = "waitSync";
+    const FuncScope funcScope(*this, "waitSync");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, sync))
+    if (!ValidateObject("sync", sync))
         return;
 
     if (flags != 0) {
-        ErrorInvalidValue("%s: `flags` must be 0.", funcName);
+        ErrorInvalidValue("`flags` must be 0.");
         return;
     }
 
     if (timeout != -1) {
-        ErrorInvalidValue("%s: `timeout` must be TIMEOUT_IGNORED.", funcName);
+        ErrorInvalidValue("`timeout` must be TIMEOUT_IGNORED.");
         return;
     }
 
     gl->fWaitSync(sync.mGLName, flags, LOCAL_GL_TIMEOUT_IGNORED);
 }
 
 void
 WebGL2Context::GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                                 JS::MutableHandleValue retval)
 {
-    const char funcName[] = "getSyncParameter";
+    const FuncScope funcScope(*this, "getSyncParameter");
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject(funcName, sync))
+    if (!ValidateObject("sync", sync))
         return;
 
     ////
 
     const bool canBeAvailable = (sync.mCanBeAvailable ||
                                  gfxPrefs::WebGLImmediateQueries());
     if (!canBeAvailable && pname == LOCAL_GL_SYNC_STATUS) {
         retval.set(JS::Int32Value(LOCAL_GL_UNSIGNALED));
@@ -155,14 +154,14 @@ WebGL2Context::GetSyncParameter(JSContex
         {
             sync.MarkSignaled();
         }
 
         retval.set(JS::Int32Value(result));
         return;
 
     default:
-        ErrorInvalidEnum("%s: Invalid pname 0x%04x", funcName, pname);
+        ErrorInvalidEnumInfo("pname", pname);
         return;
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -6,26 +6,28 @@
 #include "GLContext.h"
 #include "WebGL2Context.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
 void
-WebGL2Context::TexStorage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
+WebGL2Context::TexStorage(uint8_t funcDims, GLenum rawTarget,
                           GLsizei levels, GLenum internalFormat, GLsizei width,
                           GLsizei height, GLsizei depth)
 {
+    const FuncScope funcScope(*this, "texStorage");
+
     TexTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
-    tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth);
+    tex->TexStorage(target, levels, internalFormat, width, height, depth);
 }
 
 ////////////////////
 
 /*virtual*/ bool
 WebGL2Context::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -12,73 +12,74 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Transform Feedback
 
 already_AddRefed<WebGLTransformFeedback>
 WebGL2Context::CreateTransformFeedback()
 {
+    const FuncScope funcScope(*this, "createTransformFeedback");
     if (IsContextLost())
         return nullptr;
 
     GLuint tf = 0;
     gl->fGenTransformFeedbacks(1, &tf);
 
     RefPtr<WebGLTransformFeedback> ret = new WebGLTransformFeedback(this, tf);
     return ret.forget();
 }
 
 void
 WebGL2Context::DeleteTransformFeedback(WebGLTransformFeedback* tf)
 {
-    const char funcName[] = "deleteTransformFeedback";
-    if (!ValidateDeleteObject(funcName, tf))
+    const FuncScope funcScope(*this, "deleteTransformFeedback");
+    if (!ValidateDeleteObject(tf))
         return;
 
     if (tf->mIsActive) {
-        ErrorInvalidOperation("%s: Cannot delete active transform feedbacks.", funcName);
+        ErrorInvalidOperation("Cannot delete active transform feedbacks.");
         return;
     }
 
     if (mBoundTransformFeedback == tf) {
         BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
     }
 
     tf->RequestDelete();
 }
 
 bool
-WebGL2Context::IsTransformFeedback(const WebGLTransformFeedback* tf)
+WebGL2Context::IsTransformFeedback(const WebGLTransformFeedback* const obj)
 {
-    if (!ValidateIsObject("isTransformFeedback", tf))
+    const FuncScope funcScope(*this, "isTransformFeedback");
+    if (!ValidateIsObject(obj))
         return false;
 
-    return gl->fIsTransformFeedback(tf->mGLName);
+    return gl->fIsTransformFeedback(obj->mGLName);
 }
 
 void
 WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
 {
-    const char funcName[] = "bindTransformFeedback";
+    const FuncScope funcScope(*this, "bindTransformFeedback");
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
-        return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName);
+        return ErrorInvalidEnum("`target` must be TRANSFORM_FEEDBACK.");
 
-    if (tf && !ValidateObject(funcName, *tf))
+    if (tf && !ValidateObject("tf", *tf))
         return;
 
     if (mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
-        ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
-                              " paused.",
-                              funcName);
+        ErrorInvalidOperation("Currently bound transform feedback is active and not"
+                              " paused.");
         return;
     }
 
     ////
 
     if (mBoundTransformFeedback) {
         mBoundTransformFeedback->AddBufferBindCounts(-1);
     }
@@ -90,68 +91,74 @@ WebGL2Context::BindTransformFeedback(GLe
     if (mBoundTransformFeedback) {
         mBoundTransformFeedback->AddBufferBindCounts(+1);
     }
 }
 
 void
 WebGL2Context::BeginTransformFeedback(GLenum primMode)
 {
+    const FuncScope funcScope(*this, "beginTransformFeedback");
     if (IsContextLost())
         return;
 
     mBoundTransformFeedback->BeginTransformFeedback(primMode);
 }
 
 void
 WebGL2Context::EndTransformFeedback()
 {
+    const FuncScope funcScope(*this, "endTransformFeedback");
     if (IsContextLost())
         return;
 
     mBoundTransformFeedback->EndTransformFeedback();
 }
 
 void
 WebGL2Context::PauseTransformFeedback()
 {
+    const FuncScope funcScope(*this, "pauseTransformFeedback");
     if (IsContextLost())
         return;
 
     mBoundTransformFeedback->PauseTransformFeedback();
 }
 
 void
 WebGL2Context::ResumeTransformFeedback()
 {
+    const FuncScope funcScope(*this, "resumeTransformFeedback");
     if (IsContextLost())
         return;
 
     mBoundTransformFeedback->ResumeTransformFeedback();
 }
 
 void
 WebGL2Context::TransformFeedbackVaryings(WebGLProgram& program,
                                          const dom::Sequence<nsString>& varyings,
                                          GLenum bufferMode)
 {
+    const FuncScope funcScope(*this, "transformFeedbackVaryings");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("transformFeedbackVaryings: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     program.TransformFeedbackVaryings(varyings, bufferMode);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGL2Context::GetTransformFeedbackVarying(const WebGLProgram& program, GLuint index)
 {
+    const FuncScope funcScope(*this, "getTransformFeedbackVarying");
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getTransformFeedbackVarying: program", program))
+    if (!ValidateObject("program", program))
         return nullptr;
 
     return program.GetTransformFeedbackVarying(index);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -18,58 +18,62 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Uniforms
 
 void
 WebGLContext::Uniform1ui(WebGLUniformLocation* loc, GLuint v0)
 {
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT, "uniform1ui"))
+    const FuncScope funcScope(*this, "uniform1ui");
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT))
         return;
 
     gl->fUniform1ui(loc->mLoc, v0);
 }
 
 void
 WebGLContext::Uniform2ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1)
 {
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_UNSIGNED_INT, "uniform2ui"))
+    const FuncScope funcScope(*this, "uniform2ui");
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_UNSIGNED_INT))
         return;
 
     gl->fUniform2ui(loc->mLoc, v0, v1);
 }
 
 void
 WebGLContext::Uniform3ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2)
 {
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_UNSIGNED_INT, "uniform3ui"))
+    const FuncScope funcScope(*this, "uniform3ui");
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_UNSIGNED_INT))
         return;
 
     gl->fUniform3ui(loc->mLoc, v0, v1, v2);
 }
 
 void
 WebGLContext::Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2,
                          GLuint v3)
 {
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_UNSIGNED_INT, "uniform4ui"))
+    const FuncScope funcScope(*this, "uniform4ui");
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_UNSIGNED_INT))
         return;
 
     gl->fUniform4ui(loc->mLoc, v0, v1, v2, v3);
 }
 
 // -------------------------------------------------------------------------
 // Uniform Buffer Objects and Transform Feedback Buffers
 
 void
 WebGL2Context::GetIndexedParameter(JSContext* cx, GLenum target, GLuint index,
                                    JS::MutableHandleValue retval, ErrorResult& out_error)
 {
-    const char funcName[] = "getIndexedParameter";
+    const FuncScope funcScope(*this, "getIndexedParameter");
     retval.set(JS::NullValue());
     if (IsContextLost())
         return;
 
     const std::vector<IndexedBufferBinding>* bindings;
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START:
@@ -79,22 +83,22 @@ WebGL2Context::GetIndexedParameter(JSCon
 
     case LOCAL_GL_UNIFORM_BUFFER_BINDING:
     case LOCAL_GL_UNIFORM_BUFFER_START:
     case LOCAL_GL_UNIFORM_BUFFER_SIZE:
         bindings = &mIndexedUniformBufferBindings;
         break;
 
     default:
-        ErrorInvalidEnumInfo("getIndexedParameter: target", target);
+        ErrorInvalidEnumInfo("target", target);
         return;
     }
 
     if (index >= bindings->size()) {
-        ErrorInvalidValue("%s: `index` must be < %s.", funcName,
+        ErrorInvalidValue("`index` must be < %s.",
                           "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
         return;
     }
     const auto& binding = (*bindings)[index];
 
     JS::Value ret = JS::NullValue();
 
     switch (target) {
@@ -119,83 +123,77 @@ WebGL2Context::GetIndexedParameter(JSCon
     retval.set(ret);
 }
 
 void
 WebGL2Context::GetUniformIndices(const WebGLProgram& program,
                                  const dom::Sequence<nsString>& uniformNames,
                                  dom::Nullable< nsTArray<GLuint> >& retval)
 {
+    const FuncScope funcScope(*this, "getUniformIndices");
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getUniformIndices: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     if (!uniformNames.Length())
         return;
 
     program.GetUniformIndices(uniformNames, retval);
 }
 
-static bool
-ValidateUniformEnum(WebGLContext* webgl, GLenum pname, const char* info)
+void
+WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
+                                 const dom::Sequence<GLuint>& uniformIndices,
+                                 GLenum pname, JS::MutableHandleValue retval)
 {
+    const FuncScope funcScope(*this, "getActiveUniforms");
+    retval.setNull();
+    if (IsContextLost())
+        return;
+
     switch (pname) {
     case LOCAL_GL_UNIFORM_TYPE:
     case LOCAL_GL_UNIFORM_SIZE:
     case LOCAL_GL_UNIFORM_BLOCK_INDEX:
     case LOCAL_GL_UNIFORM_OFFSET:
     case LOCAL_GL_UNIFORM_ARRAY_STRIDE:
     case LOCAL_GL_UNIFORM_MATRIX_STRIDE:
     case LOCAL_GL_UNIFORM_IS_ROW_MAJOR:
-        return true;
+        break;
 
     default:
-        webgl->ErrorInvalidEnumArg(info, "pname", pname);
-        return false;
+        ErrorInvalidEnumInfo("pname", pname);
+        return;
     }
-}
 
-void
-WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
-                                 const dom::Sequence<GLuint>& uniformIndices,
-                                 GLenum pname, JS::MutableHandleValue retval)
-{
-    const char funcName[] = "getActiveUniforms";
-    retval.setNull();
-    if (IsContextLost())
-        return;
-
-    if (!ValidateUniformEnum(this, pname, funcName))
-        return;
-
-    if (!ValidateObject("getActiveUniforms: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     if (!program.IsLinked()) {
-        ErrorInvalidOperation("%s: `program` must be linked.", funcName);
+        ErrorInvalidOperation("`program` must be linked.");
         return;
     }
 
     const auto& numActiveUniforms = program.LinkInfo()->uniforms.size();
     for (const auto& curIndex : uniformIndices) {
         if (curIndex >= numActiveUniforms) {
-            ErrorInvalidValue("%s: Too-large active uniform index queried.", funcName);
+            ErrorInvalidValue("Too-large active uniform index queried.");
             return;
         }
     }
 
     const auto& count = uniformIndices.Length();
 
     JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
     UniquePtr<GLint[]> samples(new GLint[count]);
     if (!array || !samples) {
-        ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
+        ErrorOutOfMemory("Failed to allocate buffers.");
         return;
     }
     retval.setObject(*array);
 
     gl->fGetActiveUniformsiv(program.mGLName, count, uniformIndices.Elements(), pname,
                              samples.get());
 
     switch (pname) {
@@ -225,36 +223,38 @@ WebGL2Context::GetActiveUniforms(JSConte
         MOZ_CRASH("Invalid pname");
     }
 }
 
 GLuint
 WebGL2Context::GetUniformBlockIndex(const WebGLProgram& program,
                                     const nsAString& uniformBlockName)
 {
+    const FuncScope funcScope(*this, "getUniformBlockIndex");
     if (IsContextLost())
         return 0;
 
-    if (!ValidateObject("getUniformBlockIndex: program", program))
+    if (!ValidateObject("program", program))
         return 0;
 
     return program.GetUniformBlockIndex(uniformBlockName);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, const WebGLProgram& program,
                                               GLuint uniformBlockIndex, GLenum pname,
                                               JS::MutableHandleValue out_retval,
                                               ErrorResult& out_error)
 {
+    const FuncScope funcScope(*this, "getActiveUniformBlockParameter");
     out_retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getActiveUniformBlockParameter: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
     case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
@@ -262,39 +262,41 @@ WebGL2Context::GetActiveUniformBlockPara
         return;
 
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
         out_retval.set(program.GetActiveUniformBlockActiveUniforms(cx, uniformBlockIndex,
                                                                    &out_error));
         return;
     }
 
-    ErrorInvalidEnumInfo("getActiveUniformBlockParameter: parameter", pname);
+    ErrorInvalidEnumInfo("parameter", pname);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockName(const WebGLProgram& program,
                                          GLuint uniformBlockIndex, nsAString& retval)
 {
+    const FuncScope funcScope(*this, "getActiveUniformBlockName");
     retval.SetIsVoid(true);
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getActiveUniformBlockName: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     program.GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
 
 void
 WebGL2Context::UniformBlockBinding(WebGLProgram& program, GLuint uniformBlockIndex,
                                    GLuint uniformBlockBinding)
 {
+    const FuncScope funcScope(*this, "uniformBlockBinding");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("uniformBlockBinding: program", program))
+    if (!ValidateObject("program", program))
         return;
 
     program.UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -65,17 +65,17 @@ WebGLBuffer::Delete()
     mIndexCache = nullptr;
     mIndexRanges.clear();
     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
 }
 
 ////////////////////////////////////////
 
 static bool
-ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
+ValidateBufferUsageEnum(WebGLContext* webgl, GLenum usage)
 {
     switch (usage) {
     case LOCAL_GL_STREAM_DRAW:
     case LOCAL_GL_STATIC_DRAW:
     case LOCAL_GL_DYNAMIC_DRAW:
         return true;
 
     case LOCAL_GL_DYNAMIC_COPY:
@@ -87,52 +87,50 @@ ValidateBufferUsageEnum(WebGLContext* we
         if (MOZ_LIKELY(webgl->IsWebGL2()))
             return true;
         break;
 
     default:
         break;
     }
 
-    webgl->ErrorInvalidEnum("%s: Invalid `usage`: 0x%04x", funcName, usage);
+    webgl->ErrorInvalidEnumInfo("usage", usage);
     return false;
 }
 
 void
 WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage)
 {
-    const char funcName[] = "bufferData";
-
     // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
     // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(size).isValid())
-        return mContext->ErrorOutOfMemory("%s: bad size", funcName);
+        return mContext->ErrorOutOfMemory("bad size");
 
-    if (!ValidateBufferUsageEnum(mContext, funcName, usage))
+    if (!ValidateBufferUsageEnum(mContext, usage))
         return;
 
 #ifdef XP_MACOSX
     // bug 790879
     if (mContext->gl->WorkAroundDriverBugs() &&
         size > INT32_MAX)
     {
-        mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
+        mContext->ErrorOutOfMemory("Allocation size too large.");
         return;
     }
 #endif
 
     const void* uploadData = data;
 
     UniqueBuffer newIndexCache;
     if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER &&
         mContext->mNeedsIndexValidation)
     {
         newIndexCache = malloc(size);
         if (!newIndexCache) {
-            mContext->ErrorOutOfMemory("%s: Failed to alloc index cache.", funcName);
+            mContext->ErrorOutOfMemory("Failed to alloc index cache.");
             return;
         }
         memcpy(newIndexCache.get(), data, size);
         uploadData = newIndexCache.get();
     }
 
     const auto& gl = mContext->gl;
     const ScopedLazyBind lazyBind(gl, target, this);
@@ -140,17 +138,17 @@ WebGLBuffer::BufferData(GLenum target, s
     const bool sizeChanges = (size != ByteLength());
     if (sizeChanges) {
         gl::GLContext::LocalErrorScope errorScope(*gl);
         gl->fBufferData(target, size, uploadData, usage);
         const auto error = errorScope.GetError();
 
         if (error) {
             MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
-            mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
+            mContext->ErrorOutOfMemory("Error from driver: 0x%04x", error);
             return;
         }
     } else {
         gl->fBufferData(target, size, uploadData, usage);
     }
 
     mContext->OnDataAllocCall();
 
@@ -169,23 +167,21 @@ WebGLBuffer::BufferData(GLenum target, s
 
     ResetLastUpdateFenceId();
 }
 
 void
 WebGLBuffer::BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
                            const void* data) const
 {
-    const char funcName[] = "bufferSubData";
-
-    if (!ValidateRange(funcName, dstByteOffset, dataLen))
+    if (!ValidateRange(dstByteOffset, dataLen))
         return;
 
     if (!CheckedInt<GLintptr>(dataLen).isValid())
-        return mContext->ErrorOutOfMemory("%s: Size too large.", funcName);
+        return mContext->ErrorOutOfMemory("Size too large.");
 
     ////
 
     const void* uploadData = data;
     if (mIndexCache) {
         const auto cachedDataBegin = (uint8_t*)mIndexCache.get() + dstByteOffset;
         memcpy(cachedDataBegin, data, dataLen);
         uploadData = cachedDataBegin;
@@ -199,28 +195,27 @@ WebGLBuffer::BufferSubData(GLenum target
     const ScopedLazyBind lazyBind(gl, target, this);
 
     gl->fBufferSubData(target, dstByteOffset, dataLen, uploadData);
 
     ResetLastUpdateFenceId();
 }
 
 bool
-WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const
+WebGLBuffer::ValidateRange(size_t byteOffset, size_t byteLen) const
 {
     auto availLength = mByteLength;
     if (byteOffset > availLength) {
-        mContext->ErrorInvalidValue("%s: Offset passes the end of the buffer.", funcName);
+        mContext->ErrorInvalidValue("Offset passes the end of the buffer.");
         return false;
     }
     availLength -= byteOffset;
 
     if (byteLen > availLength) {
-        mContext->ErrorInvalidValue("%s: Offset+size passes the end of the buffer.",
-                                    funcName);
+        mContext->ErrorInvalidValue("Offset+size passes the end of the buffer.");
         return false;
     }
 
     return true;
 }
 
 ////////////////////////////////////////
 
@@ -356,17 +351,17 @@ WebGLBuffer::GetIndexedFetchMaxVert(cons
     }
 
     return maxFetchIndex;
 }
 
 ////
 
 bool
-WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
+WebGLBuffer::ValidateCanBindToTarget(GLenum target)
 {
     /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
      *
      * In the WebGL 2 API, buffers have their WebGL buffer type
      * initially set to undefined. Calling bindBuffer, bindBufferRange
      * or bindBufferBase with the target argument set to any buffer
      * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
      * then set the WebGL buffer type of the buffer being bound
@@ -403,17 +398,17 @@ WebGLBuffer::ValidateCanBindToTarget(con
         break;
 
     default:
         MOZ_CRASH();
     }
 
     const auto dataType = (mContent == WebGLBuffer::Kind::OtherData) ? "other"
                                                                      : "element";
-    mContext->ErrorInvalidOperation("%s: Buffer already contains %s data.", funcName,
+    mContext->ErrorInvalidOperation("Buffer already contains %s data.",
                                     dataType);
     return false;
 }
 
 void
 WebGLBuffer::ResetLastUpdateFenceId() const
 {
     mLastUpdateFenceId = mContext->mNextFenceId;
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -42,25 +42,25 @@ public:
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     GLenum Usage() const { return mUsage; }
     size_t ByteLength() const { return mByteLength; }
 
     Maybe<uint32_t> GetIndexedFetchMaxVert(GLenum type, uint64_t byteOffset,
                                            uint32_t indexCount) const;
-    bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const;
+    bool ValidateRange(size_t byteOffset, size_t byteLen) const;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
-    bool ValidateCanBindToTarget(const char* funcName, GLenum target);
+    bool ValidateCanBindToTarget(GLenum target);
     void BufferData(GLenum target, size_t size, const void* data, GLenum usage);
     void BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
                        const void* data) const;
 
     ////
 
     static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
         if (!buffer)
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -160,17 +160,16 @@ WebGLContext::WebGLContext()
 
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::AddWebGLContext(this);
     }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
-    mContextStatus = ContextNotLost;
     mLoseContextOnMemoryPressure = false;
     mCanLoseContextInForeground = true;
     mRestoreWhenVisible = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
 
@@ -324,19 +323,18 @@ WebGLContext::Invalidate()
 
     mInvalidated = true;
     mCanvasElement->InvalidateCanvasContent(nullptr);
 }
 
 void
 WebGLContext::OnVisibilityChange()
 {
-    if (!IsContextLost()) {
+    if (gl) // Context not lost.
         return;
-    }
 
     if (!mRestoreWhenVisible || mLastLossWasSimulated) {
         return;
     }
 
     ForceRestoreContext();
 }
 
@@ -519,17 +517,17 @@ WebGLContext::CreateAndInitGL(bool force
         flags |= gl::CreateContextFlags::PREFER_ES3;
     } else if (!gfxPrefs::WebGL1AllowCoreProfile()) {
         flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
     }
 
     switch (mOptions.powerPreference) {
     case dom::WebGLPowerPreference::Low_power:
         break;
-        
+
         // Eventually add a heuristic, but for now default to high-performance.
         // We can even make it dynamic by holding on to a ForceDiscreteGPUHelperCGL iff
         // we decide it's a high-performance application:
         // - Non-trivial canvas size
         // - Many draw calls
         // - Same origin with root page (try to stem bleeding from WebGL ads/trackers)
     case dom::WebGLPowerPreference::High_performance:
     default:
@@ -656,17 +654,17 @@ WebGLContext::CreateAndInitGL(bool force
     }
 
     return true;
 }
 
 // Fallback for resizes:
 
 bool
-WebGLContext::EnsureDefaultFB(const char* const funcName)
+WebGLContext::EnsureDefaultFB()
 {
     if (mDefaultFB) {
         MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
         return true;
     }
 
     const bool depthStencil = mOptions.depth || mOptions.stencil;
     auto attemptSize = mRequestedSize;
@@ -693,17 +691,17 @@ WebGLContext::EnsureDefaultFB(const char
         if (mDefaultFB)
             break;
 
         attemptSize.width /= 2;
         attemptSize.height /= 2;
     }
 
     if (!mDefaultFB) {
-        GenerateWarning("%s: Backbuffer resize failed. Losing context.", funcName);
+        GenerateWarning("Backbuffer resize failed. Losing context.");
         ForceLoseContext();
         return false;
     }
 
     mDefaultFB_IsInvalid = true;
 
     if (mDefaultFB->mSize != mRequestedSize) {
         GenerateWarning("Requested size %dx%d was too large, but resize"
@@ -742,16 +740,19 @@ WebGLContext::ThrowEvent_WebGLContextCre
     //////
 
     GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
 }
 
 NS_IMETHODIMP
 WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
 {
+    const FuncScope funcScope(*this, "<SetDimensions>");
+    (void)IsContextLost(); // We handle this ourselves.
+
     if (signedWidth < 0 || signedHeight < 0) {
         if (!gl) {
             Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
                                   NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_SIZE"));
         }
         GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
         return NS_ERROR_OUT_OF_MEMORY;
     }
@@ -918,17 +919,17 @@ WebGLContext::SetDimensions(int32_t sign
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
 #endif
     }
 
     MOZ_ASSERT(!mDefaultFB);
     mRequestedSize = {width, height};
-    if (!EnsureDefaultFB("context initialization")) {
+    if (!EnsureDefaultFB()) {
         MOZ_ASSERT(!gl);
 
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
         const nsLiteralCString text("Initializing WebGL backbuffer failed.");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
@@ -1036,17 +1037,17 @@ WebGLContext::LoseOldestWebGLContextIfLi
     size_t numContexts = 0;
     size_t numContextsThisPrincipal = 0;
 
     for(size_t i = 0; i < contexts.Length(); ++i) {
         // don't want to lose ourselves.
         if (contexts[i] == this)
             continue;
 
-        if (contexts[i]->IsContextLost())
+        if (!contexts[i]->gl)
             continue;
 
         if (!contexts[i]->GetCanvas()) {
             // Zombie context: the canvas is already destroyed, but something else
             // (typically the compositor) is still holding on to the context.
             // Killing zombies is a no-brainer.
             const_cast<WebGLContext*>(contexts[i])->LoseContext();
             continue;
@@ -1226,16 +1227,17 @@ WebGLContext::UpdateWebRenderCanvasData(
   mResetLayer = false;
   return true;
 }
 
 bool
 WebGLContext::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
                                        CanvasRenderer* aRenderer)
 {
+    const FuncScope funcScope(*this, "<InitializeCanvasRenderer>");
     if (IsContextLost())
         return false;
 
     CanvasInitializeData data;
     if (aBuilder->IsPaintingToWindow() && mCanvasElement) {
         // Make the layer tell us whenever a transaction finishes (including
         // the current transaction), so we can clear our invalidation state and
         // start invalidating again. We need to do this for the layer that is
@@ -1250,17 +1252,17 @@ WebGLContext::InitializeCanvasRenderer(n
         // the invalidation state to indicate that the canvas is up to date.
         data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
         data.mPreTransCallbackData = this;
         data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
         data.mDidTransCallbackData = this;
     }
 
     data.mGLContext = gl;
-    data.mSize = DrawingBufferSize("InitializeCanvasRenderer");
+    data.mSize = DrawingBufferSize();
     data.mHasAlpha = mOptions.alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
     aRenderer->Initialize(data);
     aRenderer->SetDirty();
     return true;
 }
 
@@ -1311,16 +1313,17 @@ WebGLContext::GetCanvas(Nullable<dom::Ow
         retval.SetNull();
     }
 }
 
 void
 WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
 {
     retval.SetNull();
+    const FuncScope funcScope(*this, "getContextAttributes");
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
 
     result.mAlpha.Construct(mOptions.alpha);
     result.mDepth = mOptions.depth;
     result.mStencil = mOptions.stencil;
@@ -1454,23 +1457,24 @@ WebGLContext::BlitBackbufferToCurDriverF
     }
 }
 
 // For an overview of how WebGL compositing works, see:
 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
 bool
 WebGLContext::PresentScreenBuffer()
 {
+    const FuncScope funcScope(*this, "<PresentScreenBuffer>");
     if (IsContextLost())
         return false;
 
     if (!mShouldPresent)
         return false;
 
-    if (!ValidateAndInitFB("Present", nullptr))
+    if (!ValidateAndInitFB(nullptr))
         return false;
 
     const auto& screen = gl->Screen();
     if (screen->Size() != mDefaultFB->mSize &&
         !screen->Resize(mDefaultFB->mSize))
     {
         GenerateWarning("screen->Resize failed. Losing context.");
         ForceLoseContext();
@@ -1525,26 +1529,24 @@ void
 WebGLContext::EndComposition()
 {
     // Mark ourselves as no longer invalidated.
     MarkContextClean();
     UpdateLastUseIndex();
 }
 
 void
-WebGLContext::DummyReadFramebufferOperation(const char* funcName)
+WebGLContext::DummyReadFramebufferOperation()
 {
     if (!mBoundReadFramebuffer)
         return; // Infallible.
 
-    const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(funcName);
-
+    const auto status = mBoundReadFramebuffer->CheckFramebufferStatus();
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
-        ErrorInvalidFramebufferOperation("%s: Framebuffer must be complete.",
-                                         funcName);
+        ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
     }
 }
 
 bool
 WebGLContext::Has64BitTimestamps() const
 {
     // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
     return gl->IsSupported(GLFeature::sync);
@@ -1670,17 +1672,17 @@ WebGLContext::EnqueueUpdateContextLossSt
 void
 WebGLContext::UpdateContextLossStatus()
 {
     if (!mCanvasElement && !mOffscreenCanvas) {
         // the canvas is gone. That happens when the page was closed before we got
         // this timer event. In this case, there's nothing to do here, just don't crash.
         return;
     }
-    if (mContextStatus == ContextNotLost) {
+    if (mContextStatus == ContextStatus::NotLost) {
         // We don't know that we're lost, but we might be, so we need to
         // check. If we're guilty, don't allow restores, though.
 
         bool isGuilty = true;
         MOZ_ASSERT(gl); // Shouldn't be missing gl if we're NotLost.
         bool isContextLost = CheckContextLost(gl, &isGuilty);
 
         if (isContextLost) {
@@ -1688,17 +1690,17 @@ WebGLContext::UpdateContextLossStatus()
                 mAllowContextRestore = false;
 
             ForceLoseContext();
         }
 
         // Fall through.
     }
 
-    if (mContextStatus == ContextLostAwaitingEvent) {
+    if (mContextStatus == ContextStatus::LostAwaitingEvent) {
         // The context has been lost and we haven't yet triggered the
         // callback, so do that now.
         const auto kEventName = NS_LITERAL_STRING("webglcontextlost");
         const auto kCanBubble = CanBubble::eYes;
         const auto kIsCancelable = Cancelable::eYes;
         bool useDefaultHandler;
 
         if (mCanvasElement) {
@@ -1715,27 +1717,27 @@ WebGLContext::UpdateContextLossStatus()
             event->InitEvent(kEventName, kCanBubble, kIsCancelable);
             event->SetTrusted(true);
             useDefaultHandler =
                 mOffscreenCanvas->DispatchEvent(*event, CallerType::System,
                                                 IgnoreErrors());
         }
 
         // We sent the callback, so we're just 'regular lost' now.
-        mContextStatus = ContextLost;
+        mContextStatus = ContextStatus::Lost;
         // If we're told to use the default handler, it means the script
         // didn't bother to handle the event. In this case, we shouldn't
         // auto-restore the context.
         if (useDefaultHandler)
             mAllowContextRestore = false;
 
         // Fall through.
     }
 
-    if (mContextStatus == ContextLost) {
+    if (mContextStatus == ContextStatus::Lost) {
         // Context is lost, and we've already sent the callback. We
         // should try to restore the context if we're both allowed to,
         // and supposed to.
 
         // Are we allowed to restore the context?
         if (!mAllowContextRestore)
             return;
 
@@ -1747,34 +1749,34 @@ WebGLContext::UpdateContextLossStatus()
         // Restore when the app is visible
         if (mRestoreWhenVisible)
             return;
 
         ForceRestoreContext();
         return;
     }
 
-    if (mContextStatus == ContextLostAwaitingRestore) {
+    if (mContextStatus == ContextStatus::LostAwaitingRestore) {
         // Context is lost, but we should try to restore it.
 
         if (!mAllowContextRestore) {
             // We might decide this after thinking we'd be OK restoring
             // the context, so downgrade.
-            mContextStatus = ContextLost;
+            mContextStatus = ContextStatus::Lost;
             return;
         }
 
         if (!TryToRestoreContext()) {
             // Failed to restore. Try again later.
             mContextLossHandler.RunTimer();
             return;
         }
 
         // Revival!
-        mContextStatus = ContextNotLost;
+        mContextStatus = ContextStatus::NotLost;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
                 static_cast<nsIContent*>(mCanvasElement),
                 NS_LITERAL_STRING("webglcontextrestored"),
                 CanBubble::eYes,
                 Cancelable::eYes);
@@ -1791,46 +1793,47 @@ WebGLContext::UpdateContextLossStatus()
         return;
     }
 }
 
 void
 WebGLContext::ForceLoseContext(bool simulateLosing)
 {
     printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
-    MOZ_ASSERT(!IsContextLost());
-    mContextStatus = ContextLostAwaitingEvent;
+    MOZ_ASSERT(gl);
+    mContextStatus = ContextStatus::LostAwaitingEvent;
     mContextLostErrorSet = false;
 
     // Burn it all!
     DestroyResourcesAndContext();
     mLastLossWasSimulated = simulateLosing;
 
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::ForceRestoreContext()
 {
     printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
-    mContextStatus = ContextLostAwaitingRestore;
+    mContextStatus = ContextStatus::LostAwaitingRestore;
     mAllowContextRestore = true; // Hey, you did say 'force'.
 
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
 
 already_AddRefed<mozilla::gfx::SourceSurface>
 WebGLContext::GetSurfaceSnapshot(gfxAlphaType* const out_alphaType)
 {
-    if (!gl)
+    const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
+    if (IsContextLost())
         return nullptr;
 
-    if (!BindDefaultFBForRead("GetSurfaceSnapshot"))
+    if (!BindDefaultFBForRead())
         return nullptr;
 
     const auto surfFormat = mOptions.alpha ? SurfaceFormat::B8G8R8A8
                                            : SurfaceFormat::B8G8R8X8;
     const auto& size = mDefaultFB->mSize;
     RefPtr<DataSourceSurface> surf;
     surf = Factory::CreateDataSourceSurfaceWithStride(size, surfFormat, size.width * 4);
     if (NS_WARN_IF(!surf))
@@ -1877,36 +1880,35 @@ WebGLContext::DidRefresh()
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 gfx::IntSize
-WebGLContext::DrawingBufferSize(const char* const funcName)
+WebGLContext::DrawingBufferSize()
 {
     const gfx::IntSize zeros{0, 0};
     if (IsContextLost())
         return zeros;
 
-    if (!EnsureDefaultFB(funcName))
+    if (!EnsureDefaultFB())
         return zeros;
 
     return mDefaultFB->mSize;
 }
 
 bool
-WebGLContext::ValidateAndInitFB(const char* const funcName,
-                                const WebGLFramebuffer* const fb)
+WebGLContext::ValidateAndInitFB(const WebGLFramebuffer* const fb)
 {
     if (fb)
-        return fb->ValidateAndInitAttachments(funcName);
-
-    if (!EnsureDefaultFB(funcName))
+        return fb->ValidateAndInitAttachments();
+
+    if (!EnsureDefaultFB())
         return false;
 
     if (mDefaultFB_IsInvalid) {
         gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
         const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
                                 LOCAL_GL_DEPTH_BUFFER_BIT |
                                 LOCAL_GL_STENCIL_BUFFER_BIT;
         const bool fakeNoAlpha = !mOptions.alpha;
@@ -1919,80 +1921,77 @@ WebGLContext::ValidateAndInitFB(const ch
 void
 WebGLContext::DoBindFB(const WebGLFramebuffer* const fb, const GLenum target) const
 {
     const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
     gl->fBindFramebuffer(target, driverFB);
 }
 
 bool
-WebGLContext::BindCurFBForDraw(const char* const funcName)
+WebGLContext::BindCurFBForDraw()
 {
     const auto& fb = mBoundDrawFramebuffer;
-    if (!ValidateAndInitFB(funcName, fb))
+    if (!ValidateAndInitFB(fb))
         return false;
 
     DoBindFB(fb);
     return true;
 }
 
 bool
-WebGLContext::BindCurFBForColorRead(const char* const funcName,
-                                    const webgl::FormatUsageInfo** const out_format,
+WebGLContext::BindCurFBForColorRead(const webgl::FormatUsageInfo** const out_format,
                                     uint32_t* const out_width,
                                     uint32_t* const out_height)
 {
     const auto& fb = mBoundReadFramebuffer;
 
     if (fb) {
-        if (!ValidateAndInitFB(funcName, fb))
+        if (!ValidateAndInitFB(fb))
             return false;
-        if (!fb->ValidateForColorRead(funcName, out_format, out_width, out_height))
+        if (!fb->ValidateForColorRead(out_format, out_width, out_height))
             return false;
 
         gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
         return true;
     }
 
-    if (!BindDefaultFBForRead(funcName))
+    if (!BindDefaultFBForRead())
         return false;
 
     if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
-        ErrorInvalidOperation("%s: Can't read from backbuffer when readBuffer mode is"
-                              " NONE.",
-                              funcName);
+        ErrorInvalidOperation("Can't read from backbuffer when readBuffer mode is NONE.");
         return false;
     }
 
     auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
                                     : webgl::EffectiveFormat::RGB8;
 
     *out_format = mFormatUsage->GetUsage(effFormat);
     MOZ_ASSERT(*out_format);
 
     *out_width = mDefaultFB->mSize.width;
     *out_height = mDefaultFB->mSize.height;
     return true;
 }
 
 bool
-WebGLContext::BindDefaultFBForRead(const char* const funcName)
+WebGLContext::BindDefaultFBForRead()
 {
-    if (!ValidateAndInitFB(funcName, nullptr))
+    if (!ValidateAndInitFB(nullptr))
         return false;
 
     if (!mDefaultFB->mSamples) {
         gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
         return true;
     }
 
     if (!mResolvedDefaultFB) {
         mResolvedDefaultFB = MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
         if (!mResolvedDefaultFB) {
-            gfxCriticalNote << funcName << ": Failed to create mResolvedDefaultFB.";
+            gfxCriticalNote << FuncName() << ": Failed to create mResolvedDefaultFB.";
             return false;
         }
     }
 
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
     BlitBackbufferToCurDriverFB();
 
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
@@ -2294,52 +2293,51 @@ WebGLContext::GetUnpackSize(bool isFunc3
     return totalBytes;
 }
 
 
 #if defined(MOZ_WIDGET_ANDROID)
 already_AddRefed<layers::SharedSurfaceTextureClient>
 WebGLContext::GetVRFrame()
 {
-  if (IsContextLost()) {
-    ForceRestoreContext();
-  }
+  if (!gl)
+    return nullptr;
 
   int frameId = gfx::impl::VRDisplayExternal::sPushIndex;
   static int lastFrameId = -1;
   /**
    * Android doesn't like duplicated GetVRFrame within the same gfxVRExternal frame.
    * Ballout forced composition calls if we are in the same VRExternal push frame index.
    * Also discard frameId 0 because sometimes compositor is not paused yet due to channel communication delays.
    */
   const bool ignoreFrame = lastFrameId == frameId || frameId == 0;
   lastFrameId = frameId;
   if (!ignoreFrame) {
       BeginComposition();
       EndComposition();
   }
 
-  if (IsContextLost()) {
+  if (!gl) {
     return nullptr;
   }
 
   gl::GLScreenBuffer* screen = gl->Screen();
   if (!screen) {
     return nullptr;
   }
 
   RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
   if (!sharedSurface || !sharedSurface->Surf()) {
     return nullptr;
   }
 
   /**
    * Make sure that the WebGL buffer is committed to the attached SurfaceTexture on Android.
    */
-  if (!ignoreFrame && !IsContextLost()) {
+  if (!ignoreFrame) {
     sharedSurface->Surf()->ProducerAcquire();
     sharedSurface->Surf()->Commit();
     sharedSurface->Surf()->ProducerRelease();
   }
 
   return sharedSurface.forget();
 }
 #else
@@ -2349,17 +2347,17 @@ WebGLContext::GetVRFrame()
   /**
    * Swap buffers as though composition has occurred.
    * We will then share the resulting front buffer to be submitted to the VR
    * compositor.
    */
   BeginComposition();
   EndComposition();
 
-  if (IsContextLost())
+  if (!gl)
       return nullptr;
 
   gl::GLScreenBuffer* screen = gl->Screen();
   if (!screen)
       return nullptr;
 
   RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
   if (!sharedSurface)
@@ -2378,37 +2376,36 @@ SizeOfViewElem(const dom::ArrayBufferVie
     const auto& elemType = view.Type();
     if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
         return 1;
 
     return js::Scalar::byteSize(elemType);
 }
 
 bool
-WebGLContext::ValidateArrayBufferView(const char* funcName,
-                                      const dom::ArrayBufferView& view, GLuint elemOffset,
+WebGLContext::ValidateArrayBufferView(const dom::ArrayBufferView& view, GLuint elemOffset,
                                       GLuint elemCountOverride, uint8_t** const out_bytes,
                                       size_t* const out_byteLen)
 {
     view.ComputeLengthAndData();
     uint8_t* const bytes = view.DataAllowShared();
     const size_t byteLen = view.LengthAllowShared();
 
     const auto& elemSize = SizeOfViewElem(view);
 
     size_t elemCount = byteLen / elemSize;
     if (elemOffset > elemCount) {
-        ErrorInvalidValue("%s: Invalid offset into ArrayBufferView.", funcName);
+        ErrorInvalidValue("Invalid offset into ArrayBufferView.");
         return false;
     }
     elemCount -= elemOffset;
 
     if (elemCountOverride) {
         if (elemCountOverride > elemCount) {
-            ErrorInvalidValue("%s: Invalid sub-length for ArrayBufferView.", funcName);
+            ErrorInvalidValue("Invalid sub-length for ArrayBufferView.");
             return false;
         }
         elemCount = elemCountOverride;
     }
 
     *out_bytes = bytes + (elemOffset * elemSize);
     *out_byteLen = elemCount * elemSize;
     return true;
@@ -2425,16 +2422,100 @@ WebGLContext::UpdateMaxDrawBuffers()
     // 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."
     mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
 }
 
 // --
 
+const char*
+WebGLContext::FuncName() const
+{
+    const char* ret;
+    if (MOZ_LIKELY( mFuncScope )) {
+        ret = mFuncScope->mFuncName;
+    } else {
+        MOZ_ASSERT(false);
+        ret = "<funcName unknown>";
+    }
+    return ret;
+}
+
+// -
+
+WebGLContext::FuncScope::FuncScope(const WebGLContext& webgl, const char* const funcName)
+    : mWebGL(webgl)
+    , mFuncName(bool(mWebGL.mFuncScope) ? nullptr : funcName)
+{
+    if (MOZ_UNLIKELY( !mFuncName )) {
+#ifdef DEBUG
+        mStillNeedsToCheckContextLost = false;
+#endif
+        return;
+    }
+
+    mWebGL.mFuncScope = this;
+}
+
+WebGLContext::FuncScope::~FuncScope()
+{
+    if (MOZ_UNLIKELY( !mFuncName ))
+        return;
+
+    MOZ_ASSERT(!mStillNeedsToCheckContextLost);
+    mWebGL.mFuncScope = nullptr;
+}
+
+bool
+WebGLContext::IsContextLost() const
+{
+    if (MOZ_LIKELY( mFuncScope )) {
+        mFuncScope->OnCheckContextLost();
+    }
+    return mContextStatus != ContextStatus::NotLost;
+}
+
+// --
+
+bool
+WebGLContext::ValidateIsObject(const WebGLDeletableObject* const object) const
+{
+    if (IsContextLost())
+        return false;
+
+    if (!object)
+        return false;
+
+    if (!object->IsCompatibleWithContext(this))
+        return false;
+
+    return !object->IsDeleted();
+}
+
+bool
+WebGLContext::ValidateDeleteObject(const WebGLDeletableObject* const object)
+{
+    if (IsContextLost())
+        return false;
+
+    if (!object)
+        return false;
+
+    if (!ValidateObjectAllowDeleted("obj", *object))
+        return false;
+
+    if (object->IsDeleteRequested())
+        return false;
+
+    return true;
+}
+
+// --
+
 webgl::AvailabilityRunnable*
 WebGLContext::EnsureAvailabilityRunnable()
 {
     if (!mAvailabilityRunnable) {
         RefPtr<webgl::AvailabilityRunnable> runnable = new webgl::AvailabilityRunnable(this);
 
         nsIDocument* document = GetOwnerDoc();
         if (document) {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -300,17 +300,17 @@ class WebGLContext
     friend class WebGLExtensionMOZDebug;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
     friend class webgl::AvailabilityRunnable;
     friend struct webgl::LinkedProgramInfo;
     friend struct webgl::UniformBlockInfo;
 
     friend const webgl::CachedDrawFetchLimits*
-        ValidateDraw(WebGLContext*, const char*, GLenum, uint32_t);
+        ValidateDraw(WebGLContext*, GLenum, uint32_t);
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         // We throw InvalidOperation in TexImage if we fail to use GPU fast-path
         // for texture copy when it is set to true, only for debug purpose.
         UNPACK_REQUIRE_FASTPATH = 0x10001,
         CONTEXT_LOST_WEBGL = 0x9242,
@@ -323,16 +323,21 @@ class WebGLContext
     const uint32_t mMaxPerfWarnings;
     mutable uint64_t mNumPerfWarnings;
     const uint32_t mMaxAcceptableFBStatusInvals;
 
     uint64_t mNextFenceId = 1;
     uint64_t mCompletedFenceId = 0;
 
 public:
+    class FuncScope;
+private:
+    mutable FuncScope* mFuncScope = nullptr;
+
+public:
     WebGLContext();
 
 protected:
     virtual ~WebGLContext();
 
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
@@ -340,18 +345,18 @@ public:
                                                            nsICanvasRenderingContextInternal)
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
-    virtual int32_t GetWidth() override { return DrawingBufferWidth("get width"); }
-    virtual int32_t GetHeight() override { return DrawingBufferHeight("get height"); }
+    virtual int32_t GetWidth() override { return DrawingBufferWidth(); }
+    virtual int32_t GetHeight() override { return DrawingBufferHeight(); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
                                         NotNull<gfx::DrawTarget*>) override
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
@@ -384,42 +389,65 @@ public:
      * before it is destroyed.
      */
     virtual void DidRefresh() override;
 
     NS_IMETHOD Redraw(const gfxRect&) override {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
+    // -
+
+    const auto& CurFuncScope() const { return *mFuncScope; }
+    const char* FuncName() const;
+
+    class FuncScope final {
+    public:
+        const WebGLContext& mWebGL;
+        const char* const mFuncName;
+    private:
+#ifdef DEBUG
+        mutable bool mStillNeedsToCheckContextLost = true;
+#endif
+
+    public:
+        FuncScope(const WebGLContext& webgl, const char* funcName);
+        ~FuncScope();
+
+        void OnCheckContextLost() const {
+#ifdef DEBUG
+            mStillNeedsToCheckContextLost = false;
+#endif
+        }
+    };
+
     void SynthesizeGLError(GLenum err) const;
     void SynthesizeGLError(GLenum err, const char* fmt, ...) const MOZ_FORMAT_PRINTF(3, 4);
 
     void ErrorInvalidEnum(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidValue(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const;
-    void ErrorInvalidEnumInfo(const char* info, const char* funcName,
-                              GLenum enumValue) const;
     void ErrorOutOfMemory(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
     void ErrorImplementationBug(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
 
-    void ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val) const;
+    void ErrorInvalidEnumArg(const char* argName, GLenum val) const;
 
     static const char* ErrorName(GLenum error);
 
     /**
      * Return displayable name for GLenum.
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
      * Returns hex formatted version of glenum if glenum is unknown.
      */
     static void EnumName(GLenum val, nsCString* out_name);
 
-    void DummyReadFramebufferOperation(const char* funcName);
+    void DummyReadFramebufferOperation();
 
     WebGLTexture* ActiveBoundTextureForTarget(const TexTarget texTarget) const {
         switch (texTarget.get()) {
         case LOCAL_GL_TEXTURE_2D:
             return mBound2DTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_CUBE_MAP:
             return mBoundCubeMapTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_3D:
@@ -499,31 +527,35 @@ public:
 
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     nsIDocument* GetOwnerDoc() const;
 
     // WebIDL WebGLRenderingContext API
     void Commit();
     void GetCanvas(dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
 private:
-    gfx::IntSize DrawingBufferSize(const char* funcName);
+    gfx::IntSize DrawingBufferSize();
 public:
-    GLsizei DrawingBufferWidth(const char* const funcName = "drawingBufferWidth") {
-        return DrawingBufferSize(funcName).width;
+    GLsizei DrawingBufferWidth() {
+        const FuncScope funcScope(*this, "drawingBufferWidth");
+        return DrawingBufferSize().width;
     }
-    GLsizei DrawingBufferHeight(const char* const funcName = "drawingBufferHeight") {
-        return DrawingBufferSize(funcName).height;
+    GLsizei DrawingBufferHeight() {
+        const FuncScope funcScope(*this, "drawingBufferHeight");
+        return DrawingBufferSize().height;
     }
 
     layers::LayersBackend GetCompositorBackendType() const;
 
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
-    bool IsContextLost() const { return mContextStatus != ContextNotLost; }
+    // This is the entrypoint. Don't test against it directly.
+    bool IsContextLost() const;
+
     void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
                                 dom::CallerType callerType);
     void GetExtension(JSContext* cx, const nsAString& name,
                       JS::MutableHandle<JSObject*> retval,
                       dom::CallerType callerType, ErrorResult& rv);
     void AttachShader(WebGLProgram& prog, WebGLShader& shader);
     void BindAttribLocation(WebGLProgram& prog, GLuint location,
                             const nsAString& name);
@@ -644,21 +676,25 @@ public:
     {
         retval.set(GetUniform(cx, prog, loc));
     }
 
     already_AddRefed<WebGLUniformLocation>
     GetUniformLocation(const WebGLProgram& prog, const nsAString& name);
 
     void Hint(GLenum target, GLenum mode);
-    bool IsFramebuffer(const WebGLFramebuffer* fb);
-    bool IsProgram(const WebGLProgram* prog);
-    bool IsRenderbuffer(const WebGLRenderbuffer* rb);
-    bool IsShader(const WebGLShader* shader);
-    bool IsVertexArray(const WebGLVertexArray* vao);
+
+    bool IsBuffer(const WebGLBuffer* obj);
+    bool IsFramebuffer(const WebGLFramebuffer* obj);
+    bool IsProgram(const WebGLProgram* obj);
+    bool IsRenderbuffer(const WebGLRenderbuffer* obj);
+    bool IsShader(const WebGLShader* obj);
+    bool IsTexture(const WebGLTexture* obj);
+    bool IsVertexArray(const WebGLVertexArray* obj);
+
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
 
     ////
@@ -675,40 +711,41 @@ protected:
                                 GLsizei width, GLsizei height, GLenum format,
                                 GLenum destType, void* dest, uint32_t dataLen,
                                 uint32_t rowStride);
 public:
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                     GLenum type, const dom::Nullable<dom::ArrayBufferView>& maybeView,
                     dom::CallerType aCallerType, ErrorResult& rv)
     {
-        const char funcName[] = "readPixels";
-        if (maybeView.IsNull()) {
-            ErrorInvalidValue("%s: `pixels` must not be null.", funcName);
+        const FuncScope funcScope(*this, "readPixels");
+        if (!ValidateNonNull("pixels", maybeView))
             return;
-        }
         ReadPixels(x, y, width, height, format, type, maybeView.Value(), 0,
                    aCallerType, rv);
     }
 
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                     GLenum type, WebGLsizeiptr offset,
                     dom::CallerType, ErrorResult& out_error);
 
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                     GLenum type, const dom::ArrayBufferView& dstData, GLuint dstOffset,
                     dom::CallerType, ErrorResult& out_error);
 
     ////
 
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
-                             GLsizei width, GLsizei height);
+                             GLsizei width, GLsizei height)
+    {
+        const FuncScope funcScope(*this, "renderbufferStorage");
+        RenderbufferStorage_base(target, 0, internalFormat, width, height);
+    }
 protected:
-    void RenderbufferStorage_base(const char* funcName, GLenum target,
-                                  GLsizei samples, GLenum internalformat,
+    void RenderbufferStorage_base(GLenum target, GLsizei samples, GLenum internalformat,
                                   GLsizei width, GLsizei height);
 public:
     void SampleCoverage(GLclampf value, WebGLboolean invert);
     void Scissor(GLint x, GLint y, GLsizei width, GLsizei height);
     void ShaderSource(WebGLShader& shader, const nsAString& source);
     void StencilFunc(GLenum func, GLint ref, GLuint mask);
     void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void StencilMask(GLuint mask);
@@ -887,32 +924,30 @@ public:
     FOO(4,4,4)
 
     #undef FOO
 
     ////////////////////////////////////
 
     void UseProgram(WebGLProgram* prog);
 
-    bool ValidateAttribArraySetter(const char* name, uint32_t count,
-                                   uint32_t arrayLength);
-    bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName);
+    bool ValidateAttribArraySetter(uint32_t count, uint32_t arrayLength);
+    bool ValidateUniformLocation(WebGLUniformLocation* loc);
     bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
-                               GLenum setterType, const char* funcName);
+                               GLenum setterType);
     bool ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
-                                    uint32_t setterArraySize, const char* funcName,
+                                    uint32_t setterArraySize,
                                     uint32_t* out_numElementsToUpload);
     bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                           uint8_t setterCols,
                                           uint8_t setterRows,
                                           GLenum setterType,
                                           uint32_t setterArraySize,
                                           bool setterTranspose,
-                                          const char* funcName,
                                           uint32_t* out_numElementsToUpload);
     void ValidateProgram(const WebGLProgram& prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation* loc);
     bool ValidateSamplerUniformSetter(const char* info,
                                       WebGLUniformLocation* loc, GLint value);
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
@@ -947,17 +982,16 @@ public:
                        GLuint srcElemCountOverride = 0);
     void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                        const dom::ArrayBuffer& src);
     void BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                        const dom::SharedArrayBuffer& src);
 
     already_AddRefed<WebGLBuffer> CreateBuffer();
     void DeleteBuffer(WebGLBuffer* buf);
-    bool IsBuffer(WebGLBuffer* buf);
 
 protected:
     // bound buffer state
     WebGLRefPtr<WebGLBuffer> mBoundArrayBuffer;
     WebGLRefPtr<WebGLBuffer> mBoundCopyReadBuffer;
     WebGLRefPtr<WebGLBuffer> mBoundCopyWriteBuffer;
     WebGLRefPtr<WebGLBuffer> mBoundPixelPackBuffer;
     WebGLRefPtr<WebGLBuffer> mBoundPixelUnpackBuffer;
@@ -973,28 +1007,28 @@ protected:
 // -----------------------------------------------------------------------------
 // Queries (WebGL2ContextQueries.cpp)
 protected:
     WebGLRefPtr<WebGLQuery> mQuerySlot_SamplesPassed;
     WebGLRefPtr<WebGLQuery> mQuerySlot_TFPrimsWritten;
     WebGLRefPtr<WebGLQuery> mQuerySlot_TimeElapsed;
 
     WebGLRefPtr<WebGLQuery>*
-    ValidateQuerySlotByTarget(const char* funcName, GLenum target);
+    ValidateQuerySlotByTarget(GLenum target);
 
 public:
-    already_AddRefed<WebGLQuery> CreateQuery(const char* funcName = nullptr);
-    void DeleteQuery(WebGLQuery* query, const char* funcName = nullptr);
-    bool IsQuery(const WebGLQuery* query, const char* funcName = nullptr);
-    void BeginQuery(GLenum target, WebGLQuery& query, const char* funcName = nullptr);
-    void EndQuery(GLenum target, const char* funcName = nullptr);
+    already_AddRefed<WebGLQuery> CreateQuery();
+    void DeleteQuery(WebGLQuery* query);
+    bool IsQuery(const WebGLQuery* query);
+    void BeginQuery(GLenum target, WebGLQuery& query);
+    void EndQuery(GLenum target);
     void GetQuery(JSContext* cx, GLenum target, GLenum pname,
-                  JS::MutableHandleValue retval, const char* funcName = nullptr);
+                  JS::MutableHandleValue retval);
     void GetQueryParameter(JSContext* cx, const WebGLQuery& query, GLenum pname,
-                           JS::MutableHandleValue retval, const char* funcName = nullptr);
+                           JS::MutableHandleValue retval);
 
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 private:
     void SetEnabled(const char* funcName, GLenum cap, bool enabled);
 public:
     void Disable(GLenum cap) { SetEnabled("disabled", cap, false); }
@@ -1016,17 +1050,17 @@ private:
     // State tracking slots
     realGLboolean mDitherEnabled;
     realGLboolean mRasterizerDiscardEnabled;
     realGLboolean mScissorTestEnabled;
     realGLboolean mDepthTestEnabled = 0;
     realGLboolean mStencilTestEnabled;
     GLenum mGenerateMipmapHint = 0;
 
-    bool ValidateCapabilityEnum(GLenum cap, const char* info);
+    bool ValidateCapabilityEnum(GLenum cap);
     realGLboolean* GetStateTrackingSlot(GLenum cap);
 
     // Allocation debugging variables
     mutable uint64_t mDataAllocGLCallCount;
 
     void OnDataAllocCall() const {
        mDataAllocGLCallCount++;
     }
@@ -1047,18 +1081,16 @@ public:
     void GenerateMipmap(GLenum texTarget);
 
     void GetTexParameter(JSContext*, GLenum texTarget, GLenum pname,
                          JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetTexParameter(texTarget, pname));
     }
 
-    bool IsTexture(WebGLTexture* tex);
-
     void TexParameterf(GLenum texTarget, GLenum pname, GLfloat param) {
         TexParameter_base(texTarget, pname, FloatOrInt(param));
     }
 
     void TexParameteri(GLenum texTarget, GLenum pname, GLint param) {
         TexParameter_base(texTarget, pname, FloatOrInt(param));
     }
 
@@ -1070,95 +1102,96 @@ protected:
 
     ////////////////////////////////////
 
 public:
     void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLint border,
                               GLsizei imageSize, WebGLsizeiptr offset)
     {
-        const char funcName[] = "compressedTexImage2D";
+        const FuncScope funcScope(*this, "compressedTexImage2D");
         const uint8_t funcDims = 2;
         const GLsizei depth = 1;
         const TexImageSourceAdapter src(&offset, 0, 0);
-        CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
+        CompressedTexImage(funcDims, target, level, internalFormat, width,
                            height, depth, border, src, Some(imageSize));
     }
 
     template<typename T>
     void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLint border,
                               const T& anySrc, GLuint viewElemOffset = 0,
                               GLuint viewElemLengthOverride = 0)
     {
-        const char funcName[] = "compressedTexImage2D";
+        const FuncScope funcScope(*this, "compressedTexImage2D");
         const uint8_t funcDims = 2;
         const GLsizei depth = 1;
         const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
-        CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
+        CompressedTexImage(funcDims, target, level, internalFormat, width,
                            height, depth, border, src, Nothing());
     }
 
     void CompressedTexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLsizei width, GLsizei height, GLenum unpackFormat,
                                  GLsizei imageSize, WebGLsizeiptr offset)
     {
-        const char funcName[] = "compressedTexSubImage2D";
+        const FuncScope funcScope(*this, "compressedTexSubImage2D");
         const uint8_t funcDims = 2;
         const GLint zOffset = 0;
         const GLsizei depth = 1;
         const TexImageSourceAdapter src(&offset, 0, 0);
-        CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
+        CompressedTexSubImage(funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src, Some(imageSize));
     }
 
     template<typename T>
     void CompressedTexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLsizei width, GLsizei height, GLenum unpackFormat,
                                  const T& anySrc, GLuint viewElemOffset = 0,
                                  GLuint viewElemLengthOverride = 0)
     {
-        const char funcName[] = "compressedTexSubImage2D";
+        const FuncScope funcScope(*this, "compressedTexSubImage2D");
         const uint8_t funcDims = 2;
         const GLint zOffset = 0;
         const GLsizei depth = 1;
         const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
-        CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
+        CompressedTexSubImage(funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src, Nothing());
     }
 
 protected:
-    void CompressedTexImage(const char* funcName, uint8_t funcDims, GLenum target,
+    void CompressedTexImage(uint8_t funcDims, GLenum target,
                             GLint level, GLenum internalFormat, GLsizei width,
                             GLsizei height, GLsizei depth, GLint border,
                             const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize);
-    void CompressedTexSubImage(const char* funcName, uint8_t funcDims, GLenum target,
+    void CompressedTexSubImage(uint8_t funcDims, GLenum target,
                                GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
                                GLsizei width, GLsizei height, GLsizei depth,
                                GLenum unpackFormat, const TexImageSource& src,
                                const Maybe<GLsizei>& expectedImageSize);
+
     ////////////////////////////////////
 
 public:
     void CopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x,
                         GLint y, GLsizei width, GLsizei height, GLint border);
 
     void CopyTexSubImage2D(GLenum target, GLint level, GLint xOffset,
                            GLint yOffset, GLint x, GLint y, GLsizei width,
                            GLsizei height)
     {
-        const char funcName[] = "copyTexSubImage2D";
+        const FuncScope funcScope(*this, "copyTexSubImage2D");
         const uint8_t funcDims = 2;
         const GLint zOffset = 0;
-        CopyTexSubImage(funcName, funcDims, target, level, xOffset, yOffset, zOffset,
+        CopyTexSubImage(funcDims, target, level, xOffset, yOffset, zOffset,
                         x, y, width, height);
     }
 
 protected:
-    void CopyTexSubImage(const char* funcName, uint8_t funcDims, GLenum target,
+    void CopyTexSubImage(uint8_t funcDims, GLenum target,
                          GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
                          GLint x, GLint y, GLsizei width, GLsizei height);
 
     ////////////////////////////////////
     // TexImage
 
     // Implicit width/height uploads
 
@@ -1208,24 +1241,24 @@ public:
                    unpackType, src);
     }
 
 protected:
     void TexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const TexImageSource& src)
     {
-        const char funcName[] = "texImage2D";
+        const FuncScope funcScope(*this, "texImage2D");
         const uint8_t funcDims = 2;
         const GLsizei depth = 1;
-        TexImage(funcName, funcDims, target, level, internalFormat, width, height, depth,
+        TexImage(funcDims, target, level, internalFormat, width, height, depth,
                  border, unpackFormat, unpackType, src);
     }
 
-    void TexImage(const char* funcName, uint8_t funcDims, GLenum target, GLint level,
+    void TexImage(uint8_t funcDims, GLenum target, GLint level,
                   GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth,
                   GLint border, GLenum unpackFormat, GLenum unpackType,
                   const TexImageSource& src);
 
     ////
 
 public:
     template<typename T>
@@ -1248,86 +1281,86 @@ public:
                       unpackType, src);
     }
 
 protected:
     void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLsizei width, GLsizei height, GLenum unpackFormat,
                        GLenum unpackType, const TexImageSource& src)
     {
-        const char funcName[] = "texSubImage2D";
+        const FuncScope funcScope(*this, "texSubImage2D");
         const uint8_t funcDims = 2;
         const GLint zOffset = 0;
         const GLsizei depth = 1;
-        TexSubImage(funcName, funcDims, target, level, xOffset, yOffset, zOffset, width,
+        TexSubImage(funcDims, target, level, xOffset, yOffset, zOffset, width,
                     height, depth, unpackFormat, unpackType, src);
     }
 
-    void TexSubImage(const char* funcName, uint8_t funcDims, GLenum target, GLint level,
+    void TexSubImage(uint8_t funcDims, GLenum target, GLint level,
                      GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
                      GLsizei height, GLsizei depth, GLenum unpackFormat,
                      GLenum unpackType, const TexImageSource& src);
 
     ////////////////////////////////////
     // WebGLTextureUpload.cpp
 public:
     UniquePtr<webgl::TexUnpackBlob>
-    From(const char* funcName, TexImageTarget target, GLsizei rawWidth, GLsizei rawHeight,
+    From(TexImageTarget target, GLsizei rawWidth, GLsizei rawHeight,
          GLsizei rawDepth, GLint border, const TexImageSource& src,
          dom::Uint8ClampedArray* const scopedArr);
 
 protected:
-    bool ValidateTexImageSpecification(const char* funcName, uint8_t funcDims,
+    bool ValidateTexImageSpecification(uint8_t funcDims,
                                        GLenum texImageTarget, GLint level,
                                        GLsizei width, GLsizei height, GLsizei depth,
                                        GLint border,
                                        TexImageTarget* const out_target,
                                        WebGLTexture** const out_texture,
                                        WebGLTexture::ImageInfo** const out_imageInfo);
-    bool ValidateTexImageSelection(const char* funcName, uint8_t funcDims,
+    bool ValidateTexImageSelection(uint8_t funcDims,
                                    GLenum texImageTarget, GLint level, GLint xOffset,
                                    GLint yOffset, GLint zOffset, GLsizei width,
                                    GLsizei height, GLsizei depth,
                                    TexImageTarget* const out_target,
                                    WebGLTexture** const out_texture,
                                    WebGLTexture::ImageInfo** const out_imageInfo);
-    bool ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum format,
+    bool ValidateUnpackInfo(bool usePBOs, GLenum format,
                             GLenum type, webgl::PackingInfo* const out);
 
     UniquePtr<webgl::TexUnpackBlob>
-    FromDomElem(const char* funcName, TexImageTarget target, uint32_t width,
+    FromDomElem(TexImageTarget target, uint32_t width,
                 uint32_t height, uint32_t depth, const dom::Element& elem,
                 ErrorResult* const out_error);
 
     UniquePtr<webgl::TexUnpackBytes>
-    FromCompressed(const char* funcName, TexImageTarget target, GLsizei rawWidth,
+    FromCompressed(TexImageTarget target, GLsizei rawWidth,
                    GLsizei rawHeight, GLsizei rawDepth, GLint border,
                    const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize);
 
 // -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
     GLenum mPrimRestartTypeBytes = 0;
 
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count) {
-        DrawArraysInstanced(mode, first, count, 1, "drawArrays");
+        const FuncScope funcScope(*this, "drawArrays");
+        DrawArraysInstanced(mode, first, count, 1);
     }
 
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
-                      WebGLintptr byteOffset, const char* funcName = "drawElements")
+                      WebGLintptr byteOffset)
     {
-        DrawElementsInstanced(mode, count, type, byteOffset, 1, funcName);
+        const FuncScope funcScope(*this, "drawElements");
+        DrawElementsInstanced(mode, count, type, byteOffset, 1);
     }
 
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertexCount,
-                             GLsizei instanceCount,
-                             const char* funcName = "drawArraysInstanced");
+                             GLsizei instanceCount);
     void DrawElementsInstanced(GLenum mode, GLsizei vertexCount, GLenum type,
-                               WebGLintptr byteOffset, GLsizei instanceCount,
-                               const char* funcName = "drawElementsInstanced");
+                               WebGLintptr byteOffset, GLsizei instanceCount);
 
     void EnableVertexAttribArray(GLuint index);
     void DisableVertexAttribArray(GLuint index);
 
     JS::Value GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv);
 
     void GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
@@ -1335,112 +1368,113 @@ public:
     {
         retval.set(GetVertexAttrib(cx, index, pname, rv));
     }
 
     WebGLsizeiptr GetVertexAttribOffset(GLuint index, GLenum pname);
 
     ////
 
-    void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w,
-                        const char* funcName = nullptr);
+    void VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
 
     ////
 
     void VertexAttrib1f(GLuint index, GLfloat x) {
-        VertexAttrib4f(index, x, 0, 0, 1, "vertexAttrib1f");
+        const FuncScope funcScope(*this, "vertexAttrib1f");
+        VertexAttrib4f(index, x, 0, 0, 1);
     }
     void VertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {
-        VertexAttrib4f(index, x, y, 0, 1, "vertexAttrib2f");
+        const FuncScope funcScope(*this, "vertexAttrib2f");
+        VertexAttrib4f(index, x, y, 0, 1);
     }
     void VertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
-        VertexAttrib4f(index, x, y, z, 1, "vertexAttrib3f");
+        const FuncScope funcScope(*this, "vertexAttrib3f");
+        VertexAttrib4f(index, x, y, z, 1);
     }
 
     ////
 
     void VertexAttrib1fv(GLuint index, const Float32ListU& list) {
-        const char funcName[] = "vertexAttrib1fv";
+        const FuncScope funcScope(*this, "vertexAttrib1fv");
         const auto& arr = Float32Arr::From(list);
-        if (!ValidateAttribArraySetter(funcName, 1, arr.elemCount))
+        if (!ValidateAttribArraySetter(1, arr.elemCount))
             return;
 
-        VertexAttrib4f(index, arr.elemBytes[0], 0, 0, 1, funcName);
+        VertexAttrib4f(index, arr.elemBytes[0], 0, 0, 1);
     }
 
     void VertexAttrib2fv(GLuint index, const Float32ListU& list) {
-        const char funcName[] = "vertexAttrib2fv";
+        const FuncScope funcScope(*this, "vertexAttrib2fv");
         const auto& arr = Float32Arr::From(list);
-        if (!ValidateAttribArraySetter(funcName, 2, arr.elemCount))
+        if (!ValidateAttribArraySetter(2, arr.elemCount))
             return;
 
-        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], 0, 1, funcName);
+        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], 0, 1);
     }
 
     void VertexAttrib3fv(GLuint index, const Float32ListU& list) {
-        const char funcName[] = "vertexAttrib3fv";
+        const FuncScope funcScope(*this, "vertexAttrib3fv");
         const auto& arr = Float32Arr::From(list);
-        if (!ValidateAttribArraySetter(funcName, 3, arr.elemCount))
+        if (!ValidateAttribArraySetter(3, arr.elemCount))
             return;
 
-        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], arr.elemBytes[2], 1,
-                       funcName);
+        VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], arr.elemBytes[2], 1);
     }
 
     void VertexAttrib4fv(GLuint index, const Float32ListU& list) {
-        const char funcName[] = "vertexAttrib4fv";
+        const FuncScope funcScope(*this, "vertexAttrib4fv");
         const auto& arr = Float32Arr::From(list);
-        if (!ValidateAttribArraySetter(funcName, 4, arr.elemCount))
+        if (!ValidateAttribArraySetter(4, arr.elemCount))
             return;
 
         VertexAttrib4f(index, arr.elemBytes[0], arr.elemBytes[1], arr.elemBytes[2],
-                       arr.elemBytes[3], funcName);
+                       arr.elemBytes[3]);
     }
 
     ////
 
 protected:
-    void VertexAttribAnyPointer(const char* funcName, bool isFuncInt, GLuint index,
+    void VertexAttribAnyPointer(bool isFuncInt, GLuint index,
                                 GLint size, GLenum type, bool normalized, GLsizei stride,
                                 WebGLintptr byteOffset);
 
 public:
     void VertexAttribPointer(GLuint index, GLint size, GLenum type,
                              WebGLboolean normalized, GLsizei stride,
                              WebGLintptr byteOffset)
     {
-        const char funcName[] = "vertexAttribPointer";
+        const FuncScope funcScope(*this, "vertexAttribPointer");
         const bool isFuncInt = false;
-        VertexAttribAnyPointer(funcName, isFuncInt, index, size, type, normalized, stride,
+        VertexAttribAnyPointer(isFuncInt, index, size, type, normalized, stride,
                                byteOffset);
     }
 
     void VertexAttribDivisor(GLuint index, GLuint divisor);
 
 private:
-    WebGLBuffer* DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type,
+    WebGLBuffer* DrawElements_check(GLsizei indexCount, GLenum type,
                                     WebGLintptr byteOffset, GLsizei instanceCount);
-    void Draw_cleanup(const char* funcName);
+    void Draw_cleanup();
 
     void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
 
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
-    bool DoFakeVertexAttrib0(const char* funcName, uint64_t vertexCount);
+    bool DoFakeVertexAttrib0(uint64_t vertexCount);
     void UndoFakeVertexAttrib0();
 
     CheckedUint32 mGeneration;
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mCapturedFrameInvalidated;
@@ -1523,29 +1557,29 @@ public:
     bool IsFormatValidForFB(TexInternalFormat format) 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
     // context loss.
-    enum ContextStatus {
+    enum class ContextStatus {
         // The context is stable; there either are none or we don't know of any.
-        ContextNotLost,
+        NotLost,
         // The context has been lost, but we have not yet sent an event to the
         // script informing it of this.
-        ContextLostAwaitingEvent,
+        LostAwaitingEvent,
         // The context has been lost, and we have sent the script an event
         // informing it of this.
-        ContextLost,
+        Lost,
         // The context is lost, an event has been sent to the script, and the
         // script correctly handled the event. We are waiting for the context to
         // be restored.
-        ContextLostAwaitingRestore
+        LostAwaitingRestore
     };
 
     // -------------------------------------------------------------------------
     // WebGL extensions (implemented in WebGLContextExtensions.cpp)
     typedef EnumeratedArray<WebGLExtensionID, WebGLExtensionID::Max,
                             RefPtr<WebGLExtensionBase>> ExtensionsArrayType;
 
     ExtensionsArrayType mExtensions;
@@ -1598,27 +1632,24 @@ protected:
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
     bool InitAndValidateGL(FailureReason* const out_failReason);
 
     bool ValidateBlendEquationEnum(GLenum cap, const char* info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
                                              const char* info);
-    bool ValidateComparisonEnum(GLenum target, const char* info);
     bool ValidateStencilOpEnum(GLenum action, const char* info);
-    bool ValidateFaceEnum(GLenum face, const char* info);
+    bool ValidateFaceEnum(GLenum face);
     bool ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType,
                               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(const char* funcName) const;
+    bool ValidateStencilParamsForDrawCall() const;
 
     bool ValidateCopyTexImage(TexInternalFormat srcFormat, TexInternalFormat dstformat,
                               WebGLTexImageFunc func, WebGLTexDimensions dims);
 
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
                           GLint xoffset, GLint yoffset, GLint zoffset,
                           GLint width, GLint height, GLint depth,
@@ -1652,54 +1683,52 @@ protected:
                                   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);
+                                           WebGLProgram* program);
 
     bool HasDrawBuffers() const {
         return IsWebGL2() ||
                IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
     }
 
-    WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(const char* funcName, GLenum target);
+    WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(GLenum target);
 public:
-    WebGLBuffer* ValidateBufferSelection(const char* funcName, GLenum target);
+    WebGLBuffer* ValidateBufferSelection(GLenum target);
 protected:
-    IndexedBufferBinding* ValidateIndexedBufferSlot(const char* funcName, GLenum target,
-                                                    GLuint index);
+    IndexedBufferBinding* ValidateIndexedBufferSlot(GLenum target, GLuint index);
 
-    bool ValidateIndexedBufferBinding(const char* funcName, GLenum target, GLuint index,
+    bool ValidateIndexedBufferBinding(GLenum target, GLuint index,
                                       WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
                                       IndexedBufferBinding** const out_indexedBinding);
 
-    bool ValidateNonNegative(const char* funcName, const char* argName, int64_t val) {
+    bool ValidateNonNegative(const char* argName, int64_t val) {
         if (MOZ_UNLIKELY(val < 0)) {
-            ErrorInvalidValue("%s: `%s` must be non-negative.", funcName, argName);
+            ErrorInvalidValue("`%s` must be non-negative.", argName);
             return false;
         }
         return true;
     }
 
 public:
     template<typename T>
-    bool ValidateNonNull(const char* funcName, const dom::Nullable<T>& maybe) {
+    bool ValidateNonNull(const char* const argName, const dom::Nullable<T>& maybe) {
         if (maybe.IsNull()) {
-            ErrorInvalidValue("%s: `null` is invalid.", funcName);
+            ErrorInvalidValue("%s: Cannot be null.", argName);
             return false;
         }
         return true;
     }
 
-    bool ValidateArrayBufferView(const char* funcName, const dom::ArrayBufferView& view,
+    bool ValidateArrayBufferView(const dom::ArrayBufferView& view,
                                  GLuint elemOffset, GLuint elemCountOverride,
                                  uint8_t** const out_bytes, size_t* const out_byteLen);
 
 protected:
     ////
 
     void Invalidate();
     void DestroyResourcesAndContext();
@@ -1709,103 +1738,74 @@ protected:
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     //////
 public:
-    bool ValidateObjectAllowDeleted(const char* funcName,
+    bool ValidateObjectAllowDeleted(const char* const argName,
                                     const WebGLContextBoundObject& object)
     {
         if (!object.IsCompatibleWithContext(this)) {
             ErrorInvalidOperation("%s: Object from different WebGL context (or older"
                                   " generation of this one) passed as argument.",
-                                  funcName);
+                                  argName);
             return false;
         }
 
         return true;
     }
 
-    bool ValidateObject(const char* funcName, const WebGLDeletableObject& object,
-                        bool isShaderOrProgram = false)
+    bool ValidateObject(const char* const argName, const WebGLDeletableObject& object,
+                        const bool isShaderOrProgram = false)
     {
-        if (!ValidateObjectAllowDeleted(funcName, object))
+        if (!ValidateObjectAllowDeleted(argName, object))
             return false;
 
         if (isShaderOrProgram) {
             /* GLES 3.0.5 p45:
              * "Commands that accept shader or program object names will generate the
              *  error INVALID_VALUE if the provided name is not the name of either a
              *  shader or program object[.]"
              * Further, shaders and programs appear to be different from other objects,
              * in that their lifetimes are better defined. However, they also appear to
              * allow use of objects marked for deletion, and only reject
              * actually-destroyed objects.
              */
             if (object.IsDeleted()) {
                 ErrorInvalidValue("%s: Shader or program object argument cannot have been"
                                   " deleted.",
-                                  funcName);
+                                  argName);
                 return false;
             }
         } else {
             if (object.IsDeleteRequested()) {
                 ErrorInvalidOperation("%s: Object argument cannot have been marked for"
                                       " deletion.",
-                                      funcName);
+                                      argName);
                 return false;
             }
         }
 
         return true;
     }
 
     ////
 
-    bool ValidateObject(const char* funcName, const WebGLProgram& object);
-    bool ValidateObject(const char* funcName, const WebGLShader& object);
+    // Program and Shader are incomplete, so we can't inline the conversion to
+    // WebGLDeletableObject here.
+    bool ValidateObject(const char* const argName, const WebGLProgram& object);
+    bool ValidateObject(const char* const argName, const WebGLShader& object);
 
     ////
 
-    bool ValidateIsObject(const char* funcName,
-                          const WebGLDeletableObject* object) const
-    {
-        if (IsContextLost())
-            return false;
-
-        if (!object)
-            return false;
-
-        if (!object->IsCompatibleWithContext(this))
-            return false;
-
-        if (object->IsDeleted())
-            return false;
-
-        return true;
-    }
-
-    bool ValidateDeleteObject(const char* funcName, const WebGLDeletableObject* object) {
-        if (IsContextLost())
-            return false;
-
-        if (!object)
-            return false;
-
-        if (!ValidateObjectAllowDeleted(funcName, *object))
-            return false;
-
-        if (object->IsDeleteRequested())
-            return false;
-
-        return true;
-    }
+    bool ValidateIsObject(const WebGLDeletableObject* object) const;
+    bool ValidateDeleteObject(const WebGLDeletableObject* object);
 
     ////
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual WebGLVertexArray* CreateVertexArrayImpl();
 
@@ -1821,18 +1821,18 @@ protected:
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DArrayTextures;
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
     void ResolveTexturesForDraw() const;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
     RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
-    bool ValidateFramebufferTarget(GLenum target, const char* const info);
-    bool ValidateInvalidateFramebuffer(const char* funcName, GLenum target,
+    bool ValidateFramebufferTarget(GLenum target);
+    bool ValidateInvalidateFramebuffer(GLenum target,
                                        const dom::Sequence<GLenum>& attachments,
                                        ErrorResult* const out_rv,
                                        std::vector<GLenum>* const scopedVector,
                                        GLsizei* const out_glNumAttachments,
                                        const GLenum** const out_glAttachments);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
     WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
@@ -1865,17 +1865,17 @@ protected:
     uint32_t mPixelStore_PackRowLength = 0;
     uint32_t mPixelStore_PackSkipRows = 0;
     uint32_t mPixelStore_PackSkipPixels = 0;
     uint32_t mPixelStore_PackAlignment = 0;
 
     CheckedUint32 GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
                                 uint32_t depth, uint8_t bytesPerPixel);
 
-    bool ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
+    bool ValidatePackSize(uint32_t width, uint32_t height,
                           uint8_t bytesPerPixel, uint32_t* const out_rowStride,
                           uint32_t* const out_endOffset);
 
     GLenum mPixelStore_ColorspaceConversion = 0;
     bool mPixelStore_FlipY = false;
     bool mPixelStore_PremultiplyAlpha = false;
     bool mPixelStore_RequireFastPath = false;
 
@@ -1944,17 +1944,17 @@ protected:
     GLsizei mViewportHeight;
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
     GLfloat mLineWidth = 0.0;
 
     WebGLContextLossHandler mContextLossHandler;
     bool mAllowContextRestore;
     bool mLastLossWasSimulated;
-    ContextStatus mContextStatus;
+    ContextStatus mContextStatus = ContextStatus::NotLost;
     bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
     mutable int mAlreadyGeneratedWarnings;
     int mMaxWarnings;
@@ -1988,27 +1988,26 @@ protected:
     const uint8_t mMsaaSamples;
     mutable gfx::IntSize mRequestedSize;
     mutable UniquePtr<gl::MozFramebuffer> mDefaultFB;
     mutable bool mDefaultFB_IsInvalid = false;
     mutable UniquePtr<gl::MozFramebuffer> mResolvedDefaultFB;
 
     // --
 
-    bool EnsureDefaultFB(const char* funcName);
-    bool ValidateAndInitFB(const char* funcName, const WebGLFramebuffer* fb);
+    bool EnsureDefaultFB();
+    bool ValidateAndInitFB(const WebGLFramebuffer* fb);
     void DoBindFB(const WebGLFramebuffer* fb, GLenum target = LOCAL_GL_FRAMEBUFFER) const;
 
-    bool BindCurFBForDraw(const char* funcName);
-    bool BindCurFBForColorRead(const char* funcName,
-                               const webgl::FormatUsageInfo** out_format,
+    bool BindCurFBForDraw();
+    bool BindCurFBForColorRead(const webgl::FormatUsageInfo** out_format,
                                uint32_t* out_width, uint32_t* out_height);
     void DoColorMask(uint8_t bitmask) const;
     void BlitBackbufferToCurDriverFB() const;
-    bool BindDefaultFBForRead(const char* funcName);
+    bool BindDefaultFBForRead();
 
     // --
 
 public:
     void LoseOldestWebGLContextIfLimitExceeded();
     void UpdateLastUseIndex();
 
     template <typename WebGLObjectType>
@@ -2078,22 +2077,25 @@ ToSupports(WebGLContext* webgl)
 // AKA PadToAlignment, StrideForAlignment.
 template<typename V, typename M>
 V
 RoundUpToMultipleOf(const V& value, const M& multiple)
 {
     return ((value + multiple - 1) / multiple) * multiple;
 }
 
+const char* GetEnumName(GLenum val, const char* defaultRet = "<unknown>");
+std::string EnumString(GLenum val);
+
 bool
-ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+ValidateTexTarget(WebGLContext* webgl, uint8_t funcDims,
                   GLenum rawTexTarget, TexTarget* const out_texTarget,
                   WebGLTexture** const out_tex);
 bool
-ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+ValidateTexImageTarget(WebGLContext* webgl, uint8_t funcDims,
                        GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex);
 
 class ScopedUnpackReset final
     : public gl::ScopedGLWrapper<ScopedUnpackReset>
 {
     friend struct gl::ScopedGLWrapper<ScopedUnpackReset>;
 
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -8,17 +8,17 @@
 #include "GLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 WebGLRefPtr<WebGLBuffer>*
-WebGLContext::ValidateBufferSlot(const char* funcName, GLenum target)
+WebGLContext::ValidateBufferSlot(GLenum target)
 {
     WebGLRefPtr<WebGLBuffer>* slot = nullptr;
 
     switch (target) {
     case LOCAL_GL_ARRAY_BUFFER:
         slot = &mBoundArrayBuffer;
         break;
 
@@ -51,107 +51,104 @@ WebGLContext::ValidateBufferSlot(const c
 
         case LOCAL_GL_UNIFORM_BUFFER:
             slot = &mBoundUniformBuffer;
             break;
         }
     }
 
     if (!slot) {
-        ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
+        ErrorInvalidEnumInfo("target", target);
         return nullptr;
     }
 
     return slot;
 }
 
 WebGLBuffer*
-WebGLContext::ValidateBufferSelection(const char* funcName, GLenum target)
+WebGLContext::ValidateBufferSelection(GLenum target)
 {
-    const auto& slot = ValidateBufferSlot(funcName, target);
+    const auto& slot = ValidateBufferSlot(target);
     if (!slot)
         return nullptr;
     const auto& buffer = *slot;
 
     if (!buffer) {
-        ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
+        ErrorInvalidOperation("Buffer for `target` is null.");
         return nullptr;
     }
 
     if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
         if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
-            ErrorInvalidOperation("%s: Cannot select TRANSFORM_FEEDBACK_BUFFER when"
-                                  " transform feedback is active and unpaused.",
-                                  funcName);
+            ErrorInvalidOperation("Cannot select TRANSFORM_FEEDBACK_BUFFER when"
+                                  " transform feedback is active and unpaused.");
             return nullptr;
         }
         if (buffer->IsBoundForNonTF()) {
-            ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
-                                  " non-transform-feedback.",
-                                  funcName);
+            ErrorInvalidOperation("Specified WebGLBuffer is currently bound for"
+                                  " non-transform-feedback.");
             return nullptr;
         }
     } else {
         if (buffer->IsBoundForTF()) {
-            ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
-                                  " transform feedback.",
-                                  funcName);
+            ErrorInvalidOperation("Specified WebGLBuffer is currently bound for"
+                                  " transform feedback.");
             return nullptr;
         }
     }
 
     return buffer.get();
 }
 
 IndexedBufferBinding*
-WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
+WebGLContext::ValidateIndexedBufferSlot(GLenum target, GLuint index)
 {
     decltype(mIndexedUniformBufferBindings)* bindings;
     const char* maxIndexEnum;
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         bindings = &(mBoundTransformFeedback->mIndexedBindings);
         maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
         break;
 
     case LOCAL_GL_UNIFORM_BUFFER:
         bindings = &mIndexedUniformBufferBindings;
         maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
         break;
 
     default:
-        ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
+        ErrorInvalidEnumInfo("target", target);
         return nullptr;
     }
 
     if (index >= bindings->size()) {
-        ErrorInvalidValue("%s: `index` >= %s.", funcName, maxIndexEnum);
+        ErrorInvalidValue("`index` >= %s.", maxIndexEnum);
         return nullptr;
     }
 
     return &(*bindings)[index];
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
 {
-    const char funcName[] = "bindBuffer";
+    const FuncScope funcScope(*this, "bindBuffer");
     if (IsContextLost())
         return;
 
-    if (buffer && !ValidateObject(funcName, *buffer))
+    if (buffer && !ValidateObject("buffer", *buffer))
         return;
 
-    const auto& slot = ValidateBufferSlot(funcName, target);
+    const auto& slot = ValidateBufferSlot(target);
     if (!slot)
         return;
 
-    if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
+    if (buffer && !buffer->ValidateCanBindToTarget(target))
         return;
 
     gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
 
     WebGLBuffer::SetSlot(target, buffer, slot);
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
@@ -162,60 +159,58 @@ WebGLContext::BindBuffer(GLenum target, 
         gl->fBindBuffer(target, 0);
         break;
     }
 }
 
 ////////////////////////////////////////
 
 bool
-WebGLContext::ValidateIndexedBufferBinding(const char* funcName, GLenum target,
-                                           GLuint index,
+WebGLContext::ValidateIndexedBufferBinding(GLenum target, GLuint index,
                                            WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
                                            IndexedBufferBinding** const out_indexedBinding)
 {
-    *out_genericBinding = ValidateBufferSlot(funcName, target);
+    *out_genericBinding = ValidateBufferSlot(target);
     if (!*out_genericBinding)
         return false;
 
-    *out_indexedBinding = ValidateIndexedBufferSlot(funcName, target, index);
+    *out_indexedBinding = ValidateIndexedBufferSlot(target, index);
     if (!*out_indexedBinding)
         return false;
 
     if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
         mBoundTransformFeedback->mIsActive)
     {
-        ErrorInvalidOperation("%s: Cannot update indexed buffer bindings on active"
-                              " transform feedback objects.",
-                              funcName);
+        ErrorInvalidOperation("Cannot update indexed buffer bindings on active"
+                              " transform feedback objects.");
         return false;
     }
 
     return true;
 }
 
 void
 WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
-    const char funcName[] = "bindBufferBase";
+    const FuncScope funcScope(*this, "bindBufferBase");
     if (IsContextLost())
         return;
 
-    if (buffer && !ValidateObject(funcName, *buffer))
+    if (buffer && !ValidateObject("buffer", *buffer))
         return;
 
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
-    if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
+    if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
                                       &indexedBinding))
     {
         return;
     }
 
-    if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
+    if (buffer && !buffer->ValidateCanBindToTarget(target))
         return;
 
     ////
 
     gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
 
     ////
 
@@ -228,63 +223,63 @@ WebGLContext::BindBufferBase(GLenum targ
         buffer->SetContentAfterBind(target);
     }
 }
 
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
-    const char funcName[] = "bindBufferRange";
+    const FuncScope funcScope(*this, "bindBufferRange");
     if (IsContextLost())
         return;
 
-    if (buffer && !ValidateObject(funcName, *buffer))
+    if (buffer && !ValidateObject("buffer", *buffer))
         return;
 
-    if (!ValidateNonNegative(funcName, "offset", offset) ||
-        !ValidateNonNegative(funcName, "size", size))
+    if (!ValidateNonNegative("offset", offset) ||
+        !ValidateNonNegative("size", size))
     {
         return;
     }
 
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
-    if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
+    if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
                                       &indexedBinding))
     {
         return;
     }
 
-    if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
+    if (buffer && !buffer->ValidateCanBindToTarget(target))
         return;
 
     if (buffer && !size) {
-        ErrorInvalidValue("%s: size must be non-zero for non-null buffer.", funcName);
+        ErrorInvalidValue("Size must be non-zero for non-null buffer.");
         return;
     }
 
     ////
 
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (offset % 4 != 0 || size % 4 != 0) {
-            ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
-                              funcName, "TRANSFORM_FEEDBACK_BUFFER");
+            ErrorInvalidValue("For %s, `offset` and `size` must be multiples of 4.",
+                              "TRANSFORM_FEEDBACK_BUFFER");
             return;
         }
         break;
 
     case LOCAL_GL_UNIFORM_BUFFER:
         {
             GLuint offsetAlignment = 0;
             gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
             if (offset % offsetAlignment != 0) {
-                ErrorInvalidValue("%s: For %s, `offset` must be a multiple of %s.",
-                                  funcName, "UNIFORM_BUFFER",
+                ErrorInvalidValue("For %s, `offset` must be a multiple of %s.",
+                                  "UNIFORM_BUFFER",
                                   "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
                 return;
             }
         }
         break;
     }
 
     ////
@@ -314,151 +309,154 @@ WebGLContext::BindBufferRange(GLenum tar
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
                              GLenum usage)
 {
-    const char funcName[] = "bufferData";
 
-    const auto& buffer = ValidateBufferSelection(funcName, target);
+    const auto& buffer = ValidateBufferSelection(target);
     if (!buffer)
         return;
 
     buffer->BufferData(target, dataLen, data, usage);
 }
 
 ////
 
 void
 WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
 {
-    const char funcName[] = "bufferData";
+    const FuncScope funcScope(*this, "bufferData");
     if (IsContextLost())
         return;
 
-    if (!ValidateNonNegative(funcName, "size", size))
+    if (!ValidateNonNegative("size", size))
         return;
 
     ////
 
     const UniqueBuffer zeroBuffer(calloc(size, 1));
     if (!zeroBuffer)
-        return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
+        return ErrorOutOfMemory("Failed to allocate zeros.");
 
     BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
 }
 
 void
 WebGLContext::BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
                          GLenum usage)
 {
+    const FuncScope funcScope(*this, "bufferData");
     if (IsContextLost())
         return;
 
-    if (!ValidateNonNull("bufferData", maybeSrc))
+    if (!ValidateNonNull("src", maybeSrc))
         return;
     const auto& src = maybeSrc.Value();
 
     src.ComputeLengthAndData();
     BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
 }
 
 void
 WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& src, GLenum usage,
                          GLuint srcElemOffset, GLuint srcElemCountOverride)
 {
-    const char funcName[] = "bufferData";
+    const FuncScope funcScope(*this, "bufferData");
     if (IsContextLost())
         return;
 
     uint8_t* bytes;
     size_t byteLen;
-    if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
+    if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
                                  &bytes, &byteLen))
     {
         return;
     }
 
     BufferDataImpl(target, byteLen, bytes, usage);
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
                                 size_t dataLen, const uint8_t* data)
 {
-    const char funcName[] = "bufferSubData";
+    const FuncScope funcScope(*this, "bufferSubData");
 
-    if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
+    if (!ValidateNonNegative("byteOffset", dstByteOffset))
         return;
 
-    const auto& buffer = ValidateBufferSelection(funcName, target);
+    const auto& buffer = ValidateBufferSelection(target);
     if (!buffer)
         return;
 
     buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
 }
 
 ////
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                             const dom::ArrayBuffer& src)
 {
+    const FuncScope funcScope(*this, "bufferSubData");
     if (IsContextLost())
         return;
 
     src.ComputeLengthAndData();
     BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
                       src.DataAllowShared());
 }
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                             const dom::ArrayBufferView& src, GLuint srcElemOffset,
                             GLuint srcElemCountOverride)
 {
-    const char funcName[] = "bufferSubData";
+    const FuncScope funcScope(*this, "bufferSubData");
     if (IsContextLost())
         return;
 
     uint8_t* bytes;
     size_t byteLen;
-    if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
+    if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
                                  &bytes, &byteLen))
     {
         return;
     }
 
     BufferSubDataImpl(target, dstByteOffset, byteLen, bytes);
 }
 
 ////////////////////////////////////////
 
 already_AddRefed<WebGLBuffer>
 WebGLContext::CreateBuffer()
 {
+    const FuncScope funcScope(*this, "createBuffer");
     if (IsContextLost())
         return nullptr;
 
     GLuint buf = 0;
     gl->fGenBuffers(1, &buf);
 
     RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
     return globj.forget();
 }
 
 void
 WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
 {
-    if (!ValidateDeleteObject("deleteBuffer", buffer))
+    const FuncScope funcScope(*this, "deleteBuffer");
+    if (!ValidateDeleteObject(buffer))
         return;
 
     ////
 
     const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) {
         if (bindPoint == buffer) {
             WebGLBuffer::SetSlot(target, nullptr, &bindPoint);
         }
@@ -493,18 +491,9 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
         }
     }
 
     ////
 
     buffer->RequestDelete();
 }
 
-bool
-WebGLContext::IsBuffer(WebGLBuffer* buffer)
-{
-    if (!ValidateIsObject("isBuffer", buffer))
-        return false;
-
-    return gl->fIsBuffer(buffer->mGLName);
-}
-
 } // namespace mozilla
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -38,23 +38,22 @@ class ScopedResolveTexturesForDraw
         uint32_t texUnit;
         WebGLTexture* tex;
     };
 
     WebGLContext* const mWebGL;
     std::vector<TexRebindRequest> mRebindRequests;
 
 public:
-    ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
-                                 bool* const out_error);
+    ScopedResolveTexturesForDraw(WebGLContext* webgl, bool* const out_error);
     ~ScopedResolveTexturesForDraw();
 };
 
 bool
-WebGLTexture::IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+WebGLTexture::IsFeedback(WebGLContext* webgl, uint32_t texUnit,
                          const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const
 {
     auto itr = fbAttachments.cbegin();
     for (; itr != fbAttachments.cend(); ++itr) {
         const auto& attach = *itr;
         if (attach->Texture() == this)
             break;
     }
@@ -76,30 +75,29 @@ WebGLTexture::IsFeedback(WebGLContext* w
     for (; itr != fbAttachments.cend(); ++itr) {
         const auto& attach = *itr;
         if (attach->Texture() != this)
             continue;
 
         const auto dstLevel = attach->MipLevel();
 
         if (minLevel <= dstLevel && dstLevel <= maxLevel) {
-            webgl->ErrorInvalidOperation("%s: Feedback loop detected between tex target"
+            webgl->ErrorInvalidOperation("Feedback loop detected between tex target"
                                          " 0x%04x, tex unit %u, levels %u-%u; and"
                                          " framebuffer attachment 0x%04x, level %u.",
-                                         funcName, mTarget.get(), texUnit, minLevel,
+                                         mTarget.get(), texUnit, minLevel,
                                          maxLevel, attach->mAttachmentPoint, dstLevel);
             return true;
         }
     }
 
     return false;
 }
 
 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
-                                                           const char* funcName,
                                                            bool* const out_error)
     : mWebGL(webgl)
 {
     MOZ_ASSERT(mWebGL->gl->IsCurrent());
 
     const std::vector<const WebGLFBAttachPoint*>* attachList = nullptr;
     const auto& fb = mWebGL->mBoundDrawFramebuffer;
     if (fb) {
@@ -115,36 +113,34 @@ ScopedResolveTexturesForDraw::ScopedReso
             if (texUnit >= texList.Length())
                 continue;
 
             const auto& tex = texList[texUnit];
             if (!tex)
                 continue;
 
             if (attachList &&
-                tex->IsFeedback(mWebGL, funcName, texUnit, *attachList))
+                tex->IsFeedback(mWebGL, texUnit, *attachList))
             {
                 *out_error = true;
                 return;
             }
 
             FakeBlackType fakeBlack;
-            if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
-                mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
-                                         funcName);
+            if (!tex->ResolveForDraw(texUnit, &fakeBlack)) {
+                mWebGL->ErrorOutOfMemory("Failed to resolve textures for draw.");
                 *out_error = true;
                 return;
             }
 
             if (fakeBlack == FakeBlackType::None)
                 continue;
 
             if (!mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack)) {
-                mWebGL->ErrorOutOfMemory("%s: Failed to create fake black texture.",
-                                         funcName);
+                mWebGL->ErrorOutOfMemory("Failed to create fake black texture.");
                 *out_error = true;
                 return;
             }
 
             mRebindRequests.push_back({texUnit, tex});
         }
     }
 
@@ -229,17 +225,17 @@ WebGLContext::BindFakeBlack(uint32_t tex
     gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
     return true;
 }
 
 ////////////////////////////////////////
 
 bool
-WebGLContext::ValidateStencilParamsForDrawCall(const char* const funcName) const
+WebGLContext::ValidateStencilParamsForDrawCall() const
 {
     const auto stencilBits = [&]() -> uint8_t {
         if (!mStencilTestEnabled)
             return 0;
 
         if (!mBoundDrawFramebuffer)
             return mOptions.stencil ? 8 : 0;
 
@@ -259,22 +255,21 @@ WebGLContext::ValidateStencilParamsForDr
     };
 
     bool ok = true;
     ok &= (fnMask(mStencilWriteMaskFront) == fnMask(mStencilWriteMaskBack));
     ok &= (fnMask(mStencilValueMaskFront) == fnMask(mStencilValueMaskBack));
     ok &= (fnClamp(mStencilRefFront) == fnClamp(mStencilRefBack));
 
     if (!ok) {
-        ErrorInvalidOperation("%s: Stencil front/back state must effectively match."
+        ErrorInvalidOperation("Stencil front/back state must effectively match."
                               " (before front/back comparison, WRITEMASK and VALUE_MASK"
                               " are masked with (2^s)-1, and REF is clamped to"
                               " [0, (2^s)-1], where `s` is the number of enabled stencil"
-                              " bits in the draw framebuffer)",
-                              funcName);
+                              " bits in the draw framebuffer)");
     }
     return ok;
 }
 
 ////////////////////////////////////////
 
 template<typename T>
 static bool
@@ -282,60 +277,68 @@ DoSetsIntersect(const std::set<T>& a, co
 {
     std::vector<T> intersection;
     std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
                           std::back_inserter(intersection));
     return bool(intersection.size());
 }
 
 const webgl::CachedDrawFetchLimits*
-ValidateDraw(WebGLContext* const webgl, const char* const funcName, const GLenum mode,
+ValidateDraw(WebGLContext* const webgl, const GLenum mode,
              const uint32_t instanceCount)
 {
     MOZ_ASSERT(webgl->gl->IsCurrent());
 
-    if (!webgl->BindCurFBForDraw(funcName))
+    if (!webgl->BindCurFBForDraw())
         return nullptr;
 
-    if (!webgl->ValidateDrawModeEnum(mode, funcName))
+    switch (mode) {
+    case LOCAL_GL_TRIANGLES:
+    case LOCAL_GL_TRIANGLE_STRIP:
+    case LOCAL_GL_TRIANGLE_FAN:
+    case LOCAL_GL_POINTS:
+    case LOCAL_GL_LINE_STRIP:
+    case LOCAL_GL_LINE_LOOP:
+    case LOCAL_GL_LINES:
+        break;
+    default:
+        webgl->ErrorInvalidEnumInfo("mode", mode);
         return nullptr;
+    }
 
-    if (!webgl->ValidateStencilParamsForDrawCall(funcName))
+    if (!webgl->ValidateStencilParamsForDrawCall())
         return nullptr;
 
     if (!webgl->mActiveProgramLinkInfo) {
-        webgl->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
+        webgl->ErrorInvalidOperation("The current program is not linked.");
         return nullptr;
     }
     const auto& linkInfo = webgl->mActiveProgramLinkInfo;
 
     // -
     // Check UBO sizes.
 
     for (const auto& cur : linkInfo->uniformBlocks) {
         const auto& dataSize = cur->mDataSize;
         const auto& binding = cur->mBinding;
         if (!binding) {
-            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
-                                          funcName);
+            webgl->ErrorInvalidOperation("Buffer for uniform block is null.");
             return nullptr;
         }
 
         const auto availByteCount = binding->ByteCount();
         if (dataSize > availByteCount) {
-            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
-                                         " than UNIFORM_BLOCK_DATA_SIZE.",
-                                         funcName);
+            webgl->ErrorInvalidOperation("Buffer for uniform block is smaller"
+                                         " than UNIFORM_BLOCK_DATA_SIZE.");
             return nullptr;
         }
 
         if (binding->mBufferBinding->IsBoundForTF()) {
-            webgl->ErrorInvalidOperation("%s: Buffer for uniform block is bound or"
-                                         " in use for transform feedback.",
-                                         funcName);
+            webgl->ErrorInvalidOperation("Buffer for uniform block is bound or"
+                                         " in use for transform feedback.");
             return nullptr;
         }
     }
 
     // -
 
     const auto& tfo = webgl->mBoundTransformFeedback;
     if (tfo && tfo->IsActiveAndNotPaused()) {
@@ -351,38 +354,38 @@ ValidateDraw(WebGLContext* const webgl, 
 
         default:
             MOZ_CRASH();
         }
 
         for (uint32_t i = 0; i < numUsed; ++i) {
             const auto& buffer = tfo->mIndexedBindings[i].mBufferBinding;
             if (buffer->IsBoundForNonTF()) {
-                webgl->ErrorInvalidOperation("%s: Transform feedback varying %u's buffer"
+                webgl->ErrorInvalidOperation("Transform feedback varying %u's buffer"
                                              " is bound for non-transform-feedback.",
-                                             funcName, i);
+                                             i);
                 return nullptr;
             }
 
             // Technically we don't know that this will be updated yet, but we can
             // speculatively mark it.
             buffer->ResetLastUpdateFenceId();
         }
     }
 
     // -
 
-    const auto fetchLimits = linkInfo->GetDrawFetchLimits(funcName);
+    const auto fetchLimits = linkInfo->GetDrawFetchLimits();
     if (!fetchLimits)
         return nullptr;
 
     if (instanceCount > fetchLimits->maxInstances) {
-        webgl->ErrorInvalidOperation("%s: Instance fetch requires %u, but attribs only"
+        webgl->ErrorInvalidOperation("Instance fetch requires %u, but attribs only"
                                      " supply %u.",
-                                     funcName, instanceCount,
+                                     instanceCount,
                                      uint32_t(fetchLimits->maxInstances));
         return nullptr;
     }
 
     // -
 
     webgl->RunContextLossTimer();
 
@@ -392,23 +395,23 @@ ValidateDraw(WebGLContext* const webgl, 
 ////////////////////////////////////////
 
 class ScopedFakeVertexAttrib0 final
 {
     WebGLContext* const mWebGL;
     bool mDidFake = false;
 
 public:
-    ScopedFakeVertexAttrib0(WebGLContext* const webgl, const char* const funcName,
+    ScopedFakeVertexAttrib0(WebGLContext* const webgl, 
                             const uint64_t vertexCount, bool* const out_error)
         : mWebGL(webgl)
     {
         *out_error = false;
 
-        if (!mWebGL->DoFakeVertexAttrib0(funcName, vertexCount)) {
+        if (!mWebGL->DoFakeVertexAttrib0(vertexCount)) {
             *out_error = true;
             return;
         }
         mDidFake = true;
     }
 
     ~ScopedFakeVertexAttrib0()
     {
@@ -445,49 +448,47 @@ UsedVertsForTFDraw(GLenum mode, uint32_t
 class ScopedDrawWithTransformFeedback final
 {
     WebGLContext* const mWebGL;
     WebGLTransformFeedback* const mTFO;
     const bool mWithTF;
     uint32_t mUsedVerts;
 
 public:
-    ScopedDrawWithTransformFeedback(WebGLContext* webgl, const char* funcName,
+    ScopedDrawWithTransformFeedback(WebGLContext* webgl,
                                     GLenum mode, uint32_t vertCount,
                                     uint32_t instanceCount, bool* const out_error)
         : mWebGL(webgl)
         , mTFO(mWebGL->mBoundTransformFeedback)
         , mWithTF(mTFO &&
                   mTFO->mIsActive &&
                   !mTFO->mIsPaused)
         , mUsedVerts(0)
     {
         *out_error = false;
         if (!mWithTF)
             return;
 
         if (mode != mTFO->mActive_PrimMode) {
-            mWebGL->ErrorInvalidOperation("%s: Drawing with transform feedback requires"
+            mWebGL->ErrorInvalidOperation("Drawing with transform feedback requires"
                                           " `mode` to match BeginTransformFeedback's"
-                                          " `primitiveMode`.",
-                                          funcName);
+                                          " `primitiveMode`.");
             *out_error = true;
             return;
         }
 
         const auto usedVertsPerInstance = UsedVertsForTFDraw(mode, vertCount);
         const auto usedVerts = CheckedInt<uint32_t>(usedVertsPerInstance) * instanceCount;
 
         const auto remainingCapacity = mTFO->mActive_VertCapacity - mTFO->mActive_VertPosition;
         if (!usedVerts.isValid() ||
             usedVerts.value() > remainingCapacity)
         {
-            mWebGL->ErrorInvalidOperation("%s: Insufficient buffer capacity remaining for"
-                                          " transform feedback.",
-                                          funcName);
+            mWebGL->ErrorInvalidOperation("Insufficient buffer capacity remaining for"
+                                          " transform feedback.");
             *out_error = true;
             return;
         }
 
         mUsedVerts = usedVerts.value();
     }
 
     void Advance() const {
@@ -504,77 +505,77 @@ HasInstancedDrawing(const WebGLContext& 
     return webgl.IsWebGL2() ||
            webgl.IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays);
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
-                                  GLsizei instanceCount, const char* const funcName)
+                                  GLsizei instanceCount)
 {
     AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS);
     if (IsContextLost())
         return;
     const gl::GLContext::TlsScope inTls(gl);
 
     // -
 
-    if (!ValidateNonNegative(funcName, "first", first) ||
-        !ValidateNonNegative(funcName, "vertCount", vertCount) ||
-        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
+    if (!ValidateNonNegative("first", first) ||
+        !ValidateNonNegative("vertCount", vertCount) ||
+        !ValidateNonNegative("instanceCount", instanceCount))
     {
         return;
     }
 
     if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
         MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
         if (mPrimRestartTypeBytes != 0) {
             mPrimRestartTypeBytes = 0;
 
             // OSX appears to have severe perf issues with leaving this enabled.
             gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
         }
     }
 
     // -
 
-    const auto fetchLimits = ValidateDraw(this, funcName, mode, instanceCount);
+    const auto fetchLimits = ValidateDraw(this, mode, instanceCount);
     if (!fetchLimits)
         return;
 
     // -
 
     const auto totalVertCount_safe = CheckedInt<uint32_t>(first) + vertCount;
     if (!totalVertCount_safe.isValid()) {
-        ErrorOutOfMemory("%s: `first+vertCount` out of range.", funcName);
+        ErrorOutOfMemory("`first+vertCount` out of range.");
         return;
     }
     auto totalVertCount = totalVertCount_safe.value();
 
     if (vertCount && instanceCount &&
         totalVertCount > fetchLimits->maxVerts)
     {
-        ErrorInvalidOperation("%s: Vertex fetch requires %u, but attribs only supply %u.",
-                              funcName, totalVertCount, uint32_t(fetchLimits->maxVerts));
+        ErrorInvalidOperation("Vertex fetch requires %u, but attribs only supply %u.",
+                              totalVertCount, uint32_t(fetchLimits->maxVerts));
         return;
     }
 
     // -
 
     bool error = false;
-    const ScopedFakeVertexAttrib0 attrib0(this, funcName, totalVertCount, &error);
+    const ScopedFakeVertexAttrib0 attrib0(this, totalVertCount, &error);
     if (error)
         return;
 
-    const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    const ScopedResolveTexturesForDraw scopedResolve(this, &error);
     if (error)
         return;
 
-    const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
+    const ScopedDrawWithTransformFeedback scopedTF(this, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
         if (vertCount && instanceCount) {
             AUTO_PROFILER_LABEL("glDrawArraysInstanced", GRAPHICS);
@@ -582,40 +583,39 @@ WebGLContext::DrawArraysInstanced(GLenum
                 gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
             } else {
                 MOZ_ASSERT(instanceCount == 1);
                 gl->fDrawArrays(mode, first, vertCount);
             }
         }
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
     scopedTF.Advance();
 }
 
 ////////////////////////////////////////
 
 WebGLBuffer*
-WebGLContext::DrawElements_check(const char* const funcName, const GLsizei rawIndexCount,
+WebGLContext::DrawElements_check(const GLsizei rawIndexCount,
                                  const GLenum type, const WebGLintptr byteOffset,
                                  const GLsizei instanceCount)
 {
     if (mBoundTransformFeedback &&
         mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
-        ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
-                              " transform feedback.",
-                              funcName);
+        ErrorInvalidOperation("DrawElements* functions are incompatible with"
+                              " transform feedback.");
         return nullptr;
     }
 
-    if (!ValidateNonNegative(funcName, "vertCount", rawIndexCount) ||
-        !ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
-        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
+    if (!ValidateNonNegative("vertCount", rawIndexCount) ||
+        !ValidateNonNegative("byteOffset", byteOffset) ||
+        !ValidateNonNegative("instanceCount", instanceCount))
     {
         return nullptr;
     }
     const auto indexCount = uint32_t(rawIndexCount);
 
     uint8_t bytesPerIndex = 0;
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
@@ -628,22 +628,21 @@ WebGLContext::DrawElements_check(const c
 
     case LOCAL_GL_UNSIGNED_INT:
         if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
             bytesPerIndex = 4;
         }
         break;
     }
     if (!bytesPerIndex) {
-        ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
+        ErrorInvalidEnumInfo("type", type);
         return nullptr;
     }
     if (byteOffset % bytesPerIndex != 0) {
-        ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
-                              funcName);
+        ErrorInvalidOperation("`byteOffset` must be a multiple of the size of `type`");
         return nullptr;
     }
 
     ////
 
     if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
         MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
         if (mPrimRestartTypeBytes != bytesPerIndex) {
@@ -655,71 +654,70 @@ WebGLContext::DrawElements_check(const c
         }
     }
 
     ////
     // Index fetching
 
     const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer;
     if (!indexBuffer) {
-        ErrorInvalidOperation("%s: Index buffer not bound.", funcName);
+        ErrorInvalidOperation("Index buffer not bound.");
         return nullptr;
     }
     MOZ_ASSERT(!indexBuffer->IsBoundForTF(), "This should be impossible.");
 
     const size_t availBytes = indexBuffer->ByteLength();
     const auto availIndices = AvailGroups(availBytes, byteOffset, bytesPerIndex,
                                           bytesPerIndex);
     if (instanceCount && indexCount > availIndices) {
-        ErrorInvalidOperation("%s: Index buffer too small.", funcName);
+        ErrorInvalidOperation("Index buffer too small.");
         return nullptr;
     }
 
     return indexBuffer.get();
 }
 
 static void
-HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
+HandleDrawElementsErrors(WebGLContext* webgl,
                          gl::GLContext::LocalErrorScope& errorScope)
 {
     const auto err = errorScope.GetError();
     if (err == LOCAL_GL_INVALID_OPERATION) {
-        webgl->ErrorInvalidOperation("%s: Driver rejected indexed draw call, possibly"
-                                     " due to out-of-bounds indices.", funcName);
+        webgl->ErrorInvalidOperation("Driver rejected indexed draw call, possibly"
+                                     " due to out-of-bounds indices.");
         return;
     }
 
     MOZ_ASSERT(!err);
     if (err) {
-        webgl->ErrorImplementationBug("%s: Unexpected driver error during indexed draw"
-                                      " call. Please file a bug.",
-                                      funcName);
+        webgl->ErrorImplementationBug("Unexpected driver error during indexed draw"
+                                      " call. Please file a bug.");
         return;
     }
 }
 
 void
 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei indexCount, GLenum type,
-                                    WebGLintptr byteOffset, GLsizei instanceCount,
-                                    const char* const funcName)
+                                    WebGLintptr byteOffset, GLsizei instanceCount)
 {
+    const FuncScope funcScope(*this, "drawElementsInstanced");
     AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS);
     if (IsContextLost())
         return;
 
     const gl::GLContext::TlsScope inTls(gl);
 
-    const auto indexBuffer = DrawElements_check(funcName, indexCount, type, byteOffset,
+    const auto indexBuffer = DrawElements_check(indexCount, type, byteOffset,
                                                 instanceCount);
     if (!indexBuffer)
         return;
 
     // -
 
-    const auto fetchLimits = ValidateDraw(this, funcName, mode, instanceCount);
+    const auto fetchLimits = ValidateDraw(this, mode, instanceCount);
     if (!fetchLimits)
         return;
 
     bool collapseToDrawArrays = false;
     auto fakeVertCount = fetchLimits->maxVerts;
     if (fetchLimits->maxVerts == UINT64_MAX) {
         // This isn't observable, and keeps FakeVertexAttrib0 sane.
         collapseToDrawArrays = true;
@@ -755,31 +753,31 @@ WebGLContext::DrawElementsInstanced(GLen
 
             const auto exactMaxVertId = indexBuffer->GetIndexedFetchMaxVert(type,
                                                                             byteOffset,
                                                                             indexCount);
             maxVertId = exactMaxVertId.value();
             return maxVertId < fetchLimits->maxVerts;
         }();
         if (!isFetchValid) {
-            ErrorInvalidOperation("%s: Indexed vertex fetch requires %u vertices, but"
+            ErrorInvalidOperation("Indexed vertex fetch requires %u vertices, but"
                                   " attribs only supply %u.",
-                                  funcName, maxVertId+1, uint32_t(fetchLimits->maxVerts));
+                                  maxVertId+1, uint32_t(fetchLimits->maxVerts));
             return;
         }
     }
 
     // -
 
     bool error = false;
-    const ScopedFakeVertexAttrib0 attrib0(this, funcName, fakeVertCount, &error);
+    const ScopedFakeVertexAttrib0 attrib0(this, fakeVertCount, &error);
     if (error)
         return;
 
-    const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    const ScopedResolveTexturesForDraw scopedResolve(this, &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
         {
             UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
             if (MOZ_UNLIKELY( gl->IsANGLE() &&
@@ -807,28 +805,28 @@ WebGLContext::DrawElementsInstanced(GLen
                     } else {
                         gl->fDrawElements(mode, indexCount, type,
                                           reinterpret_cast<GLvoid*>(byteOffset));
                     }
                 }
             }
 
             if (errorScope) {
-                HandleDrawElementsErrors(this, funcName, *errorScope);
+                HandleDrawElementsErrors(this, *errorScope);
             }
         }
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
 }
 
 ////////////////////////////////////////
 
 void
-WebGLContext::Draw_cleanup(const char* funcName)
+WebGLContext::Draw_cleanup()
 {
     if (gl->WorkAroundDriverBugs()) {
         if (gl->Renderer() == gl::GLRenderer::Tegra) {
             mDrawCallsSinceLastFlush++;
 
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
                 mDrawCallsSinceLastFlush = 0;
@@ -853,19 +851,18 @@ WebGLContext::Draw_cleanup(const char* f
         destWidth = mDefaultFB->mSize.width;
         destHeight = mDefaultFB->mSize.height;
     }
 
     if (mViewportWidth > int32_t(destWidth) ||
         mViewportHeight > int32_t(destHeight))
     {
         if (!mAlreadyWarnedAboutViewportLargerThanDest) {
-            GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
-                            " rect. (This warning will only be given once)",
-                            funcName);
+            GenerateWarning("Drawing to a destination rect smaller than the viewport"
+                            " rect. (This warning will only be given once)");
             mAlreadyWarnedAboutViewportLargerThanDest = true;
         }
     }
 }
 
 WebGLVertexAttrib0Status
 WebGLContext::WhatDoesVertexAttrib0Need() const
 {
@@ -890,17 +887,17 @@ WebGLContext::WhatDoesVertexAttrib0Need(
     }
 
     const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
     return isAttribArray0Enabled ? WebGLVertexAttrib0Status::Default
                                  : WebGLVertexAttrib0Status::EmulatedInitializedArray;
 }
 
 bool
-WebGLContext::DoFakeVertexAttrib0(const char* const funcName, const uint64_t vertexCount)
+WebGLContext::DoFakeVertexAttrib0(const uint64_t vertexCount)
 {
     const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return true;
 
     if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
         GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
                         "to do expensive emulation work when running on desktop OpenGL "
@@ -966,35 +963,34 @@ WebGLContext::DoFakeVertexAttrib0(const 
     {
         return true;
     }
 
     ////
 
     const UniqueBuffer data(malloc(dataSize));
     if (!data) {
-        ErrorOutOfMemory("%s: Failed to allocate fake vertex attrib 0 array.",
-                         funcName);
+        ErrorOutOfMemory("Failed to allocate fake vertex attrib 0 array.");
         return false;
     }
     auto itr = (uint8_t*)data.get();
     const auto itrEnd = itr + dataSize;
     while (itr != itrEnd) {
         memcpy(itr, mGenericVertexAttrib0Data, bytesPerVert);
         itr += bytesPerVert;
     }
 
     {
         gl::GLContext::LocalErrorScope errorScope(*gl);
 
         gl->fBufferSubData(LOCAL_GL_ARRAY_BUFFER, 0, dataSize, data.get());
 
         const auto err = errorScope.GetError();
         if (err) {
-            ErrorOutOfMemory("%s: Failed to upload fake vertex attrib 0 data.", funcName);
+            ErrorOutOfMemory("Failed to upload fake vertex attrib 0 data.");
             return false;
         }
     }
 
     ////
 
     memcpy(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert);
     mFakeVertexAttrib0DataDefined = true;
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -257,17 +257,17 @@ WebGLContext::EnableSupportedExtension(d
 void
 WebGLContext::GetExtension(JSContext* cx,
                            const nsAString& wideName,
                            JS::MutableHandle<JSObject*> retval,
                            dom::CallerType callerType,
                            ErrorResult& rv)
 {
     retval.set(nullptr);
-
+    const FuncScope funcScope(*this, "getExtension");
     if (IsContextLost())
         return;
 
     NS_LossyConvertUTF16toASCII name(wideName);
 
     WebGLExtensionID ext = WebGLExtensionID::Unknown;
 
     // step 1: figure what extension is wanted
@@ -425,16 +425,17 @@ WebGLContext::EnableExtension(WebGLExten
     mExtensions[ext] = obj;
 }
 
 void
 WebGLContext::GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
                                      dom::CallerType callerType)
 {
     retval.SetNull();
+    const FuncScope funcScope(*this, "getSupportedExtensions");
     if (IsContextLost())
         return;
 
     nsTArray<nsString>& arr = retval.SetValue();
 
     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) {
         const auto extension = WebGLExtensionID(i);
         if (extension == WebGLExtensionID::MOZ_debug)
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -10,24 +10,23 @@
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 
 namespace mozilla {
 
 void
 WebGLContext::Clear(GLbitfield mask)
 {
-    const char funcName[] = "clear";
-
+    const FuncScope funcScope(*this, "clear");
     if (IsContextLost())
         return;
 
     uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
     if (mask != m)
-        return ErrorInvalidValue("%s: invalid mask bits", funcName);
+        return ErrorInvalidValue("Invalid mask bits.");
 
     if (mask == 0) {
         GenerateWarning("Calling gl.clear(0) has no effect.");
     } else if (mRasterizerDiscardEnabled) {
         GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
     }
 
     if (mask & LOCAL_GL_COLOR_BUFFER_BIT && mBoundDrawFramebuffer) {
@@ -38,26 +37,25 @@ WebGLContext::Clear(GLbitfield mask)
 
                 switch (cur->Format()->format->componentType) {
                 case webgl::ComponentType::Float:
                 case webgl::ComponentType::NormInt:
                 case webgl::ComponentType::NormUInt:
                     break;
 
                 default:
-                    ErrorInvalidOperation("%s: Color draw buffers must be floating-point"
-                                          " or fixed-point. (normalized (u)ints)",
-                                          funcName);
+                    ErrorInvalidOperation("Color draw buffers must be floating-point"
+                                          " or fixed-point. (normalized (u)ints)");
                     return;
                 }
             }
         }
     }
 
-    if (!BindCurFBForDraw(funcName))
+    if (!BindCurFBForDraw())
         return;
 
     auto driverMask = mask;
     if (!mBoundDrawFramebuffer) {
         if (mNeedsFakeNoDepth) {
             driverMask &= ~LOCAL_GL_DEPTH_BUFFER_BIT;
         }
         if (mNeedsFakeNoStencil) {
@@ -79,16 +77,17 @@ GLClampFloat(GLfloat val)
         return 1.0;
 
     return val;
 }
 
 void
 WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
 {
+    const FuncScope funcScope(*this, "clearColor");
     if (IsContextLost())
         return;
 
     const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float) ||
                                             IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) ||
                                             IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float));
     if (!supportsFloatColorBuffers) {
         r = GLClampFloat(r);
@@ -103,114 +102,118 @@ WebGLContext::ClearColor(GLfloat r, GLfl
     mColorClearValue[1] = g;
     mColorClearValue[2] = b;
     mColorClearValue[3] = a;
 }
 
 void
 WebGLContext::ClearDepth(GLclampf v)
 {
+    const FuncScope funcScope(*this, "clearDepth");
     if (IsContextLost())
         return;
 
     mDepthClearValue = GLClampFloat(v);
     gl->fClearDepth(mDepthClearValue);
 }
 
 void
 WebGLContext::ClearStencil(GLint v)
 {
+    const FuncScope funcScope(*this, "clearStencil");
     if (IsContextLost())
         return;
 
     mStencilClearValue = v;
     gl->fClearStencil(v);
 }
 
 void
 WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a)
 {
+    const FuncScope funcScope(*this, "colorMask");
     if (IsContextLost())
         return;
 
     mColorWriteMask = uint8_t(bool(r)) << 0 |
                       uint8_t(bool(g)) << 1 |
                       uint8_t(bool(b)) << 2 |
                       uint8_t(bool(a)) << 3;
 }
 
 void
 WebGLContext::DepthMask(WebGLboolean b)
 {
+    const FuncScope funcScope(*this, "depthMask");
     if (IsContextLost())
         return;
 
     mDepthWriteMask = b;
     gl->fDepthMask(b);
 }
 
 void
 WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
 {
-    const char funcName[] = "drawBuffers";
+    const FuncScope funcScope(*this, "drawBuffers");
     if (IsContextLost())
         return;
 
     if (mBoundDrawFramebuffer) {
-        mBoundDrawFramebuffer->DrawBuffers(funcName, buffers);
+        mBoundDrawFramebuffer->DrawBuffers(buffers);
         return;
     }
 
     // GLES 3.0.4 p186:
     // "If the GL is bound to the default framebuffer, then `n` must be 1 and the
     //  constant must be BACK or NONE. [...] If DrawBuffers is supplied with a
     //  constant other than BACK and NONE, or with a value of `n` other than 1, the
     //  error INVALID_OPERATION is generated."
     if (buffers.Length() != 1) {
-        ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a"
-                              " length of 1.",
-                              funcName);
+        ErrorInvalidOperation("For the default framebuffer, `buffers` must have a"
+                              " length of 1.");
         return;
     }
 
     switch (buffers[0]) {
     case LOCAL_GL_NONE:
     case LOCAL_GL_BACK:
         break;
 
     default:
-        ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be"
-                              " BACK or NONE.",
-                              funcName);
+        ErrorInvalidOperation("For the default framebuffer, `buffers[0]` must be"
+                              " BACK or NONE.");
         return;
     }
 
     mDefaultFB_DrawBuffer0 = buffers[0];
     // Don't actually set it.
 }
 
 void
 WebGLContext::StencilMask(GLuint mask)
 {
+    const FuncScope funcScope(*this, "stencilMask");
     if (IsContextLost())
         return;
 
     mStencilWriteMaskFront = mask;
     mStencilWriteMaskBack = mask;
 
     gl->fStencilMask(mask);
 }
 
 void
 WebGLContext::StencilMaskSeparate(GLenum face, GLuint mask)
 {
+    const FuncScope funcScope(*this, "stencilMaskSeparate");
     if (IsContextLost())
         return;
 
-    if (!ValidateFaceEnum(face, "stencilMaskSeparate: face"))
+    if (!ValidateFaceEnum(face))
         return;
 
     switch (face) {
         case LOCAL_GL_FRONT_AND_BACK:
             mStencilWriteMaskFront = mask;
             mStencilWriteMaskBack = mask;
             break;
         case LOCAL_GL_FRONT:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -10,16 +10,17 @@
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLFormats.h"
 #include "WebGLFramebuffer.h"
+#include "WebGLQuery.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShaderPrecisionFormat.h"
 #include "WebGLTexture.h"
 #include "WebGLExtensions.h"
 #include "WebGLVertexArray.h"
 
 #include "nsDebug.h"
 #include "nsReadableUtils.h"
@@ -53,93 +54,97 @@
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 
 bool
-WebGLContext::ValidateObject(const char* funcName, const WebGLProgram& object)
+WebGLContext::ValidateObject(const char* const argName, const WebGLProgram& object)
 {
-    return ValidateObject(funcName, object, true);
+    return ValidateObject(argName, object, true);
 }
 
 bool
-WebGLContext::ValidateObject(const char* funcName, const WebGLShader& object)
+WebGLContext::ValidateObject(const char* const argName, const WebGLShader& object)
 {
-    return ValidateObject(funcName, object, true);
+    return ValidateObject(argName, object, true);
 }
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 //
 //  WebGL API
 //
 
 void
 WebGLContext::ActiveTexture(GLenum texture)
 {
+    const FuncScope funcScope(*this, "activeTexture");
     if (IsContextLost())
         return;
 
     if (texture < LOCAL_GL_TEXTURE0 ||
         texture >= LOCAL_GL_TEXTURE0 + mGLMaxTextureUnits)
     {
         return ErrorInvalidEnum(
-            "ActiveTexture: texture unit %d out of range. "
+            "Texture unit %d out of range. "
             "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
             "Notice that TEXTURE0 != 0.",
             texture, mGLMaxTextureUnits);
     }
 
     mActiveTexture = texture - LOCAL_GL_TEXTURE0;
     gl->fActiveTexture(texture);
 }
 
 void
 WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
 {
+    const FuncScope funcScope(*this, "attachShader");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("attachShader: program", program) ||
-        !ValidateObject("attachShader: shader", shader))
+    if (!ValidateObject("program", program) ||
+        !ValidateObject("shader", shader))
     {
         return;
     }
 
     program.AttachShader(&shader);
 }
 
 void
 WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
                                  const nsAString& name)
 {
+    const FuncScope funcScope(*this, "bindAttribLocation");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("bindAttribLocation: program", prog))
+    if (!ValidateObject("program", prog))
         return;
 
     prog.BindAttribLocation(location, name);
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
 {
+    const FuncScope funcScope(*this, "bindFramebuffer");
     if (IsContextLost())
         return;
 
-    if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
+    if (!ValidateFramebufferTarget(target))
         return;
 
-    if (wfb && !ValidateObject("bindFramebuffer", *wfb))
+    if (wfb && !ValidateObject("fb", *wfb))
         return;
 
     if (!wfb) {
         gl->fBindFramebuffer(target, 0);
     } else {
         GLuint framebuffername = wfb->mGLName;
         gl->fBindFramebuffer(target, framebuffername);
 #ifdef ANDROID
@@ -161,62 +166,67 @@ WebGLContext::BindFramebuffer(GLenum tar
     default:
         break;
     }
 }
 
 void
 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
 {
+    const FuncScope funcScope(*this, "bindRenderbuffer");
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_RENDERBUFFER)
-        return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
-
-    if (wrb && !ValidateObject("bindRenderbuffer", *wrb))
+        return ErrorInvalidEnumInfo("target", target);
+
+    if (wrb && !ValidateObject("rb", *wrb))
         return;
 
     // Usually, we would now call into glBindRenderbuffer. However, since we have to
     // potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
     // we know we should bind here.
     // Instead, we do all renderbuffer binding lazily.
 
     if (wrb) {
         wrb->mHasBeenBound = true;
     }
 
     mBoundRenderbuffer = wrb;
 }
 
 void WebGLContext::BlendEquation(GLenum mode)
 {
+    const FuncScope funcScope(*this, "blendEquation");
     if (IsContextLost())
         return;
 
-    if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
+    if (!ValidateBlendEquationEnum(mode, "mode"))
         return;
 
     gl->fBlendEquation(mode);
 }
 
 void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
 {
+    const FuncScope funcScope(*this, "blendEquationSeparate");
     if (IsContextLost())
         return;
 
-    if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") ||
-        !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha"))
+    if (!ValidateBlendEquationEnum(modeRGB, "modeRGB") ||
+        !ValidateBlendEquationEnum(modeAlpha, "modeAlpha"))
+    {
         return;
+    }
 
     gl->fBlendEquationSeparate(modeRGB, modeAlpha);
 }
 
 static bool
-ValidateBlendFuncEnum(WebGLContext* webgl, GLenum factor, const char* funcName, const char* varName)
+ValidateBlendFuncEnum(WebGLContext* webgl, GLenum factor, const char* varName)
 {
     switch (factor) {
     case LOCAL_GL_ZERO:
     case LOCAL_GL_ONE:
     case LOCAL_GL_SRC_COLOR:
     case LOCAL_GL_ONE_MINUS_SRC_COLOR:
     case LOCAL_GL_DST_COLOR:
     case LOCAL_GL_ONE_MINUS_DST_COLOR:
@@ -227,88 +237,88 @@ ValidateBlendFuncEnum(WebGLContext* webg
     case LOCAL_GL_CONSTANT_COLOR:
     case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR:
     case LOCAL_GL_CONSTANT_ALPHA:
     case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA:
     case LOCAL_GL_SRC_ALPHA_SATURATE:
         return true;
 
     default:
-        const nsPrintfCString err("%s: %s", funcName, varName);
-        webgl->ErrorInvalidEnumInfo(err.get(), factor);
+        webgl->ErrorInvalidEnumInfo(varName, factor);
         return false;
     }
 }
 
 static bool
 ValidateBlendFuncEnums(WebGLContext* webgl, GLenum srcRGB, GLenum srcAlpha,
-                       GLenum dstRGB, GLenum dstAlpha, const char* funcName)
+                       GLenum dstRGB, GLenum dstAlpha)
 {
     if (!webgl->IsWebGL2()) {
        if (dstRGB == LOCAL_GL_SRC_ALPHA_SATURATE || dstAlpha == LOCAL_GL_SRC_ALPHA_SATURATE) {
-          const nsPrintfCString err("%s: LOCAL_GL_SRC_ALPHA_SATURATE as a destination"
-                                    " blend function is disallowed in WebGL 1 (dstRGB ="
-                                    " 0x%04x, dstAlpha = 0x%04x).",
-                                    funcName, dstRGB, dstAlpha);
-          webgl->ErrorInvalidEnum("%s", err.get());
+          webgl->ErrorInvalidEnum("LOCAL_GL_SRC_ALPHA_SATURATE as a destination"
+                                  " blend function is disallowed in WebGL 1 (dstRGB ="
+                                  " 0x%04x, dstAlpha = 0x%04x).",
+                                  dstRGB, dstAlpha);
           return false;
        }
     }
 
-    if (!ValidateBlendFuncEnum(webgl, srcRGB, funcName, "srcRGB") ||
-        !ValidateBlendFuncEnum(webgl, srcAlpha, funcName, "srcAlpha") ||
-        !ValidateBlendFuncEnum(webgl, dstRGB, funcName, "dstRGB") ||
-        !ValidateBlendFuncEnum(webgl, dstAlpha, funcName, "dstAlpha"))
+    if (!ValidateBlendFuncEnum(webgl, srcRGB, "srcRGB") ||
+        !ValidateBlendFuncEnum(webgl, srcAlpha, "srcAlpha") ||
+        !ValidateBlendFuncEnum(webgl, dstRGB, "dstRGB") ||
+        !ValidateBlendFuncEnum(webgl, dstAlpha, "dstAlpha"))
     {
        return false;
     }
 
     return true;
 }
 
 void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor)
 {
+    const FuncScope funcScope(*this, "blendFunc");
     if (IsContextLost())
         return;
 
-    if (!ValidateBlendFuncEnums(this, sfactor, sfactor, dfactor, dfactor, "blendFunc"))
+    if (!ValidateBlendFuncEnums(this, sfactor, sfactor, dfactor, dfactor))
        return;
 
-    if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB"))
+    if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "srcRGB and dstRGB"))
         return;
 
     gl->fBlendFunc(sfactor, dfactor);
 }
 
 void
 WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
                                 GLenum srcAlpha, GLenum dstAlpha)
 {
+    const FuncScope funcScope(*this, "blendFuncSeparate");
     if (IsContextLost())
         return;
 
-    if (!ValidateBlendFuncEnums(this, srcRGB, srcAlpha, dstRGB, dstAlpha, "blendFuncSeparate"))
+    if (!ValidateBlendFuncEnums(this, srcRGB, srcAlpha, dstRGB, dstAlpha))
        return;
 
     // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see
     // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list
-    if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB"))
+    if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "srcRGB and dstRGB"))
         return;
 
     gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
 }
 
 GLenum
 WebGLContext::CheckFramebufferStatus(GLenum target)
 {
-    const char funcName[] = "checkFramebufferStatus";
+    const FuncScope funcScope(*this, "checkFramebufferStatus");
     if (IsContextLost())
         return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return 0;
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -319,61 +329,65 @@ WebGLContext::CheckFramebufferStatus(GLe
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (!fb)
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 
-    return fb->CheckFramebufferStatus(funcName).get();
+    return fb->CheckFramebufferStatus().get();
 }
 
 already_AddRefed<WebGLProgram>
 WebGLContext::CreateProgram()
 {
+    const FuncScope funcScope(*this, "createProgram");
     if (IsContextLost())
         return nullptr;
     RefPtr<WebGLProgram> globj = new WebGLProgram(this);
     return globj.forget();
 }
 
 already_AddRefed<WebGLShader>
 WebGLContext::CreateShader(GLenum type)
 {
+    const FuncScope funcScope(*this, "createShader");
     if (IsContextLost())
         return nullptr;
 
     if (type != LOCAL_GL_VERTEX_SHADER &&
         type != LOCAL_GL_FRAGMENT_SHADER)
     {
-        ErrorInvalidEnumInfo("createShader: type", type);
+        ErrorInvalidEnumInfo("type", type);
         return nullptr;
     }
 
     RefPtr<WebGLShader> shader = new WebGLShader(this, type);
     return shader.forget();
 }
 
 void
 WebGLContext::CullFace(GLenum face)
 {
+    const FuncScope funcScope(*this, "cullFace");
     if (IsContextLost())
         return;
 
-    if (!ValidateFaceEnum(face, "cullFace"))
+    if (!ValidateFaceEnum(face))
         return;
 
     gl->fCullFace(face);
 }
 
 void
 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
 {
-    if (!ValidateDeleteObject("deleteFramebuffer", fbuf))
+    const FuncScope funcScope(*this, "deleteFramebuffer");
+    if (!ValidateDeleteObject(fbuf))
         return;
 
     fbuf->RequestDelete();
 
     if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer == fbuf) {
             BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
                             static_cast<WebGLFramebuffer*>(nullptr));
@@ -385,46 +399,46 @@ WebGLContext::DeleteFramebuffer(WebGLFra
         BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
                         static_cast<WebGLFramebuffer*>(nullptr));
     }
 }
 
 void
 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
 {
-    const char funcName[] = "deleteRenderbuffer";
-    if (!ValidateDeleteObject(funcName, rbuf))
+    const FuncScope funcScope(*this, "deleteRenderbuffer");
+    if (!ValidateDeleteObject(rbuf))
         return;
 
     if (mBoundDrawFramebuffer)
-        mBoundDrawFramebuffer->DetachRenderbuffer(funcName, rbuf);
+        mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
 
     if (mBoundReadFramebuffer)
-        mBoundReadFramebuffer->DetachRenderbuffer(funcName, rbuf);
-
-    rbuf->InvalidateStatusOfAttachedFBs(funcName);
+        mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
+
+    rbuf->InvalidateStatusOfAttachedFBs();
 
     if (mBoundRenderbuffer == rbuf)
         BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
 
     rbuf->RequestDelete();
 }
 
 void
 WebGLContext::DeleteTexture(WebGLTexture* tex)
 {
-    const char funcName[] = "deleteTexture";
-    if (!ValidateDeleteObject(funcName, tex))
+    const FuncScope funcScope(*this, "deleteTexture");
+    if (!ValidateDeleteObject(tex))
         return;
 
     if (mBoundDrawFramebuffer)
-        mBoundDrawFramebuffer->DetachTexture(funcName, tex);
+        mBoundDrawFramebuffer->DetachTexture(tex);
 
     if (mBoundReadFramebuffer)
-        mBoundReadFramebuffer->DetachTexture(funcName, tex);
+        mBoundReadFramebuffer->DetachTexture(tex);
 
     GLuint activeTexture = mActiveTexture;
     for (uint32_t i = 0; i < mGLMaxTextureUnits; i++) {
         if (mBound2DTextures[i] == tex ||
             mBoundCubeMapTextures[i] == tex ||
             mBound3DTextures[i] == tex ||
             mBound2DArrayTextures[i] == tex)
         {
@@ -435,81 +449,106 @@ WebGLContext::DeleteTexture(WebGLTexture
     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
 WebGLContext::DeleteProgram(WebGLProgram* prog)
 {
-    if (!ValidateDeleteObject("deleteProgram", prog))
+    const FuncScope funcScope(*this, "deleteProgram");
+    if (!ValidateDeleteObject(prog))
         return;
 
     prog->RequestDelete();
 }
 
 void
 WebGLContext::DeleteShader(WebGLShader* shader)
 {
-    if (!ValidateDeleteObject("deleteShader", shader))
+    const FuncScope funcScope(*this, "deleteShader");
+    if (!ValidateDeleteObject(shader))
         return;
 
     shader->RequestDelete();
 }
 
 void
 WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
 {
+    const FuncScope funcScope(*this, "detachShader");
     if (IsContextLost())
         return;
 
     // It's valid to attempt to detach a deleted shader, since it's still a
     // shader.
-    if (!ValidateObject("detachShader: program", program) ||
-        !ValidateObjectAllowDeleted("detachShader: shader", shader))
+    if (!ValidateObject("program", program) ||
+        !ValidateObjectAllowDeleted("shader", shader))
     {
         return;
     }
 
     program.DetachShader(&shader);
 }
 
+static bool
+ValidateComparisonEnum(WebGLContext& webgl, const GLenum func)
+{
+    switch (func) {
+    case LOCAL_GL_NEVER:
+    case LOCAL_GL_LESS:
+    case LOCAL_GL_LEQUAL:
+    case LOCAL_GL_GREATER:
+    case LOCAL_GL_GEQUAL:
+    case LOCAL_GL_EQUAL:
+    case LOCAL_GL_NOTEQUAL:
+    case LOCAL_GL_ALWAYS:
+        return true;
+
+    default:
+        webgl.ErrorInvalidEnumInfo("func", func);
+        return false;
+    }
+}
+
 void
 WebGLContext::DepthFunc(GLenum func)
 {
+    const FuncScope funcScope(*this, "depthFunc");
     if (IsContextLost())
         return;
 
-    if (!ValidateComparisonEnum(func, "depthFunc"))
+    if (!ValidateComparisonEnum(*this, func))
         return;
 
     gl->fDepthFunc(func);
 }
 
 void
 WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar)
 {
+    const FuncScope funcScope(*this, "depthRange");
     if (IsContextLost())
         return;
 
     if (zNear > zFar)
-        return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
+        return ErrorInvalidOperation("the near value is greater than the far value!");
 
     gl->fDepthRange(zNear, zFar);
 }
 
 void
 WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment,
                                       GLenum rbtarget, WebGLRenderbuffer* wrb)
 {
-    const char funcName[] = "framebufferRenderbuffer";
+    const FuncScope funcScope(*this, "framebufferRenderbuffer");
     if (IsContextLost())
         return;
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return;
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -518,33 +557,33 @@ WebGLContext::FramebufferRenderbuffer(GL
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (!fb)
-        return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
-
-    fb->FramebufferRenderbuffer(funcName, attachment, rbtarget, wrb);
+        return ErrorInvalidOperation("Cannot modify framebuffer 0.");
+
+    fb->FramebufferRenderbuffer(attachment, rbtarget, wrb);
 }
 
 void
 WebGLContext::FramebufferTexture2D(GLenum target,
                                    GLenum attachment,
                                    GLenum textarget,
                                    WebGLTexture* tobj,
                                    GLint level)
 {
-    const char funcName[] = "framebufferTexture2D";
+    const FuncScope funcScope(*this, "framebufferTexture2D");
     if (IsContextLost())
         return;
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return;
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -553,131 +592,135 @@ WebGLContext::FramebufferTexture2D(GLenu
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (!fb)
-        return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
-
-    fb->FramebufferTexture2D(funcName, attachment, textarget, tobj, level);
+        return ErrorInvalidOperation("Cannot modify framebuffer 0.");
+
+    fb->FramebufferTexture2D(attachment, textarget, tobj, level);
 }
 
 void
 WebGLContext::FrontFace(GLenum mode)
 {
+    const FuncScope funcScope(*this, "frontFace");
     if (IsContextLost())
         return;
 
     switch (mode) {
         case LOCAL_GL_CW:
         case LOCAL_GL_CCW:
             break;
         default:
-            return ErrorInvalidEnumInfo("frontFace: mode", mode);
+            return ErrorInvalidEnumInfo("mode", mode);
     }
 
     gl->fFrontFace(mode);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
 {
+    const FuncScope funcScope(*this, "getActiveAttrib");
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getActiveAttrib: program", prog))
+    if (!ValidateObject("program", prog))
         return nullptr;
 
     return prog.GetActiveAttrib(index);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
 {
+    const FuncScope funcScope(*this, "getActiveUniform");
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getActiveUniform: program", prog))
+    if (!ValidateObject("program", prog))
         return nullptr;
 
     return prog.GetActiveUniform(index);
 }
 
 void
 WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
                                  dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
+    const FuncScope funcScope(*this, "getAttachedShaders");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getAttachedShaders", prog))
+    if (!ValidateObject("prog", prog))
         return;
 
     prog.GetAttachedShaders(&retval.SetValue());
 }
 
 GLint
 WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
 {
+    const FuncScope funcScope(*this, "getAttribLocation");
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObject("getAttribLocation: program", prog))
+    if (!ValidateObject("program", prog))
         return -1;
 
     return prog.GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
-    const char funcName[] = "getBufferParameter";
+    const FuncScope funcScope(*this, "getBufferParameter");
     if (IsContextLost())
         return JS::NullValue();
 
-    const auto& slot = ValidateBufferSlot(funcName, target);
+    const auto& slot = ValidateBufferSlot(target);
     if (!slot)
         return JS::NullValue();
     const auto& buffer = *slot;
 
     if (!buffer) {
-        ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
+        ErrorInvalidOperation("Buffer for `target` is null.");
         return JS::NullValue();
     }
 
     switch (pname) {
     case LOCAL_GL_BUFFER_SIZE:
         return JS::NumberValue(buffer->ByteLength());
 
     case LOCAL_GL_BUFFER_USAGE:
         return JS::NumberValue(buffer->Usage());
 
     default:
-        ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
+        ErrorInvalidEnumInfo("pname", pname);
         return JS::NullValue();
     }
 }
 
 JS::Value
 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& rv)
 {
-    const char funcName[] = "getFramebufferAttachmentParameter";
-
+    const FuncScope funcScope(*this, "getFramebufferAttachmentParameter");
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target))
         return JS::NullValue();
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -686,37 +729,35 @@ WebGLContext::GetFramebufferAttachmentPa
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (fb)
-        return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv);
+        return fb->GetAttachmentParameter(cx, target, attachment, pname, &rv);
 
     ////////////////////////////////////
 
     if (!IsWebGL2()) {
-        ErrorInvalidOperation("%s: Querying against the default framebuffer is not"
-                              " allowed in WebGL 1.",
-                              funcName);
+        ErrorInvalidOperation("Querying against the default framebuffer is not"
+                              " allowed in WebGL 1.");
         return JS::NullValue();
     }
 
     switch (attachment) {
     case LOCAL_GL_BACK:
     case LOCAL_GL_DEPTH:
     case LOCAL_GL_STENCIL:
         break;
 
     default:
-        ErrorInvalidEnum("%s: For the default framebuffer, can only query COLOR, DEPTH,"
-                         " or STENCIL.",
-                         funcName);
+        ErrorInvalidEnum("For the default framebuffer, can only query COLOR, DEPTH,"
+                         " or STENCIL.");
         return JS::NullValue();
     }
 
     switch (pname) {
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
         switch (attachment) {
         case LOCAL_GL_BACK:
             break;
@@ -726,19 +767,18 @@ WebGLContext::GetFramebufferAttachmentPa
             }
             break;
         case LOCAL_GL_STENCIL:
             if (!mOptions.stencil) {
               return JS::Int32Value(LOCAL_GL_NONE);
             }
             break;
         default:
-            ErrorInvalidEnum("%s: With the default framebuffer, can only query COLOR, DEPTH,"
-                             " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
-                             funcName);
+            ErrorInvalidEnum("With the default framebuffer, can only query COLOR, DEPTH,"
+                             " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE");
             return JS::NullValue();
         }
         return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
 
     ////////////////
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
@@ -803,33 +843,34 @@ WebGLContext::GetFramebufferAttachmentPa
             if (!mOptions.depth) {
                 ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
                 return JS::NullValue();
             }
         }
         return JS::NumberValue(LOCAL_GL_LINEAR);
     }
 
-    ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
+    ErrorInvalidEnumInfo("pname", pname);
     return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
 {
+    const FuncScope funcScope(*this, "getRenderbufferParameter");
     if (IsContextLost())
         return JS::NullValue();
 
     if (target != LOCAL_GL_RENDERBUFFER) {
-        ErrorInvalidEnumInfo("getRenderbufferParameter: target", target);
+        ErrorInvalidEnumInfo("target", target);
         return JS::NullValue();
     }
 
     if (!mBoundRenderbuffer) {
-        ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound");
+        ErrorInvalidOperation("No renderbuffer is bound.");
         return JS::NullValue();
     }
 
     switch (pname) {
     case LOCAL_GL_RENDERBUFFER_SAMPLES:
         if (!IsWebGL2())
             break;
         MOZ_FALLTHROUGH;
@@ -848,23 +889,24 @@ WebGLContext::GetRenderbufferParameter(G
         GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
         return JS::Int32Value(i);
     }
 
     default:
         break;
     }
 
-    ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
+    ErrorInvalidEnumInfo("pname", pname);
     return JS::NullValue();
 }
 
 already_AddRefed<WebGLTexture>
 WebGLContext::CreateTexture()
 {
+    const FuncScope funcScope(*this, "createTexture");
     if (IsContextLost())
         return nullptr;
 
     GLuint tex = 0;
     gl->fGenTextures(1, &tex);
 
     RefPtr<WebGLTexture> globj = new WebGLTexture(this, tex);
     return globj.forget();
@@ -877,16 +919,17 @@ GetAndClearError(GLenum* errorVar)
     GLenum ret = *errorVar;
     *errorVar = LOCAL_GL_NO_ERROR;
     return ret;
 }
 
 GLenum
 WebGLContext::GetError()
 {
+    const FuncScope funcScope(*this, "getError");
     /* WebGL 1.0: Section 5.14.3: Setting and getting state:
      *   If the context's webgl context lost flag is set, returns
      *   CONTEXT_LOST_WEBGL the first time this method is called.
      *   Afterward, returns NO_ERROR until the context has been
      *   restored.
      *
      * WEBGL_lose_context:
      *   [When this extension is enabled: ] loseContext and
@@ -917,155 +960,203 @@ WebGLContext::GetError()
 
     err = GetAndClearError(&mUnderlyingGLError);
     return err;
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
+    const FuncScope funcScope(*this, "getProgramParameter");
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
+    if (!ValidateObjectAllowDeleted("program", prog))
         return JS::NullValue();
 
     return prog.GetProgramParameter(pname);
 }
 
 void
 WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
 {
     retval.SetIsVoid(true);
+    const FuncScope funcScope(*this, "getProgramInfoLog");
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getProgramInfoLog: program", prog))
+    if (!ValidateObject("program", prog))
         return;
 
     prog.GetProgramInfoLog(&retval);
 }
 
 JS::Value
 WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
                          const WebGLUniformLocation& loc)
 {
+    const FuncScope funcScope(*this, "getUniform");
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObject("getUniform: `program`", prog))
+    if (!ValidateObject("program", prog))
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getUniform: `location`", loc))
+    if (!ValidateObjectAllowDeleted("location", loc))
         return JS::NullValue();
 
-    if (!loc.ValidateForProgram(&prog, "getUniform"))
+    if (!loc.ValidateForProgram(&prog))
         return JS::NullValue();
 
     return loc.GetUniform(js);
 }
 
 already_AddRefed<WebGLUniformLocation>
 WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
 {
+    const FuncScope funcScope(*this, "getUniformLocation");
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObject("getUniformLocation: program", prog))
+    if (!ValidateObject("program", prog))
         return nullptr;
 
     return prog.GetUniformLocation(name);
 }
 
 void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
+    const FuncScope funcScope(*this, "hint");
     if (IsContextLost())
         return;
 
     bool isValid = false;
 
     switch (target) {
     case LOCAL_GL_GENERATE_MIPMAP_HINT:
         mGenerateMipmapHint = mode;
+        isValid = true;
 
         // Deprecated and removed in desktop GL Core profiles.
         if (gl->IsCoreProfile())
             return;
 
-        isValid = true;
         break;
 
     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
         if (IsWebGL2() ||
             IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
         {
             isValid = true;
         }
         break;
     }
 
     if (!isValid)
-        return ErrorInvalidEnum("hint: invalid hint");
+        return ErrorInvalidEnumInfo("target", target);
 
     gl->fHint(target, mode);
 }
 
+// -
+
 bool
-WebGLContext::IsFramebuffer(const WebGLFramebuffer* fb)
+WebGLContext::IsBuffer(const WebGLBuffer* const obj)
 {
-    if (!ValidateIsObject("isFramebuffer", fb))
+    const FuncScope funcScope(*this, "isBuffer");
+    if (!ValidateIsObject(obj))
+        return false;
+
+    return gl->fIsBuffer(obj->mGLName);
+}
+
+bool
+WebGLContext::IsFramebuffer(const WebGLFramebuffer* const obj)
+{
+    const FuncScope funcScope(*this, "isFramebuffer");
+    if (!ValidateIsObject(obj))
         return false;
 
 #ifdef ANDROID
     if (gl->WorkAroundDriverBugs() &&
         gl->Renderer() == GLRenderer::AndroidEmulator)
     {
-        return fb->mIsFB;
+        return obj->mIsFB;
     }
 #endif
 
-    return gl->fIsFramebuffer(fb->mGLName);
+    return gl->fIsFramebuffer(obj->mGLName);
 }
 
 bool
-WebGLContext::IsProgram(const WebGLProgram* prog)
+WebGLContext::IsProgram(const WebGLProgram* const obj)
 {
-    if (!ValidateIsObject("isProgram", prog))
+    const FuncScope funcScope(*this, "isProgram");
+    return ValidateIsObject(obj);
+}
+
+bool
+WebGLContext::IsQuery(const WebGLQuery* const obj)
+{
+    const FuncScope funcScope(*this, "isQuery");
+    if (!ValidateIsObject(obj))
         return false;
 
-    return true;
+    return obj->IsQuery();
 }
 
 bool
-WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* rb)
+WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* const obj)
 {
-    if (!ValidateIsObject("isRenderbuffer", rb))
+    const FuncScope funcScope(*this, "isRenderbuffer");
+    if (!ValidateIsObject(obj))
         return false;
 
-    return rb->mHasBeenBound;
+    return obj->mHasBeenBound;
+}
+
+bool
+WebGLContext::IsShader(const WebGLShader* const obj)
+{
+    const FuncScope funcScope(*this, "isShader");
+    return ValidateIsObject(obj);
 }
 
 bool
-WebGLContext::IsShader(const WebGLShader* shader)
+WebGLContext::IsTexture(const WebGLTexture* const obj)
 {
-    if (!ValidateIsObject("isShader", shader))
+    const FuncScope funcScope(*this, "isTexture");
+    if (!ValidateIsObject(obj))
         return false;
 
-    return true;
+    return obj->IsTexture();
 }
 
+bool
+WebGLContext::IsVertexArray(const WebGLVertexArray* const obj)
+{
+    const FuncScope funcScope(*this, "isVertexArray");
+    if (!ValidateIsObject(obj))
+        return false;
+
+    return obj->IsVertexArray();
+}
+
+// -
+
 void
 WebGLContext::LinkProgram(WebGLProgram& prog)
 {
+    const FuncScope funcScope(*this, "linkProgram");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("linkProgram", prog))
+    if (!ValidateObject("prog", prog))
         return;
 
     prog.LinkProgram();
 
     if (!prog.IsLinked()) {
         // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
         // null out mActiveProgramLinkInfo.
         return;
@@ -1080,16 +1171,17 @@ WebGLContext::LinkProgram(WebGLProgram& 
             gl->fUseProgram(prog.mGLName);
         }
     }
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
+    const FuncScope funcScope(*this, "pixelStorei");
     if (IsContextLost())
         return;
 
     if (IsWebGL2()) {
         uint32_t* pValueSlot = nullptr;
         switch (pname) {
         case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
             pValueSlot = &mPixelStore_UnpackImageHeight;
@@ -1120,20 +1212,18 @@ WebGLContext::PixelStorei(GLenum pname, 
             break;
 
         case LOCAL_GL_PACK_SKIP_PIXELS:
             pValueSlot = &mPixelStore_PackSkipPixels;
             break;
         }
 
         if (pValueSlot) {
-            if (param < 0) {
-                ErrorInvalidValue("pixelStorei: param must be >= 0.");
+            if (!ValidateNonNegative("param", param))
                 return;
-            }
 
             gl->fPixelStorei(pname, param);
             *pValueSlot = param;
             return;
         }
     }
 
     switch (pname) {
@@ -1148,18 +1238,17 @@ WebGLContext::PixelStorei(GLenum pname, 
     case UNPACK_COLORSPACE_CONVERSION_WEBGL:
         switch (param) {
         case LOCAL_GL_NONE:
         case BROWSER_DEFAULT_WEBGL:
             mPixelStore_ColorspaceConversion = param;
             return;
 
         default:
-            ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter",
-                                 param);
+            ErrorInvalidEnumInfo("colorspace conversion parameter", param);
             return;
         }
 
     case UNPACK_REQUIRE_FASTPATH:
         if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) {
             mPixelStore_RequireFastPath = bool(param);
             return;
         }
@@ -1176,27 +1265,26 @@ WebGLContext::PixelStorei(GLenum pname, 
                 mPixelStore_PackAlignment = param;
             else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
                 mPixelStore_UnpackAlignment = param;
 
             gl->fPixelStorei(pname, param);
             return;
 
         default:
-            ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
+            ErrorInvalidValue("Invalid pack/unpack alignment value.");
             return;
         }
 
 
-
     default:
         break;
     }
 
-    ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
+    ErrorInvalidEnumInfo("pname", pname);
 }
 
 bool
 WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
                                      GLsizei width, GLsizei height, GLenum format,
                                      GLenum destType, void* dest, uint32_t destSize,
                                      uint32_t rowStride)
 {
@@ -1277,33 +1365,30 @@ GetJSScalarFromGLType(GLenum type, js::S
         return false;
     }
 }
 
 bool
 WebGLContext::ReadPixels_SharedPrecheck(CallerType aCallerType,
                                         ErrorResult& out_error)
 {
-    if (IsContextLost())
-        return false;
-
     if (mCanvasElement &&
         mCanvasElement->IsWriteOnly() &&
         aCallerType != CallerType::System)
     {
         GenerateWarning("readPixels: Not allowed");
         out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
+WebGLContext::ValidatePackSize(uint32_t width, uint32_t height,
                                uint8_t bytesPerPixel, uint32_t* const out_rowStride,
                                uint32_t* const out_endOffset)
 {
     if (!width || !height) {
         *out_rowStride = 0;
         *out_endOffset = 0;
         return true;
     }
@@ -1315,102 +1400,107 @@ WebGLContext::ValidatePackSize(const cha
     const auto skipPixels = mPixelStore_PackSkipPixels;
     const auto skipRows = mPixelStore_PackSkipRows;
     const auto alignment = mPixelStore_PackAlignment;
 
     const auto usedPixelsPerRow = CheckedUint32(skipPixels) + width;
     const auto usedRowsPerImage = CheckedUint32(skipRows) + height;
 
     if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > rowLength) {
-        ErrorInvalidOperation("%s: SKIP_PIXELS + width > ROW_LENGTH.", funcName);
+        ErrorInvalidOperation("SKIP_PIXELS + width > ROW_LENGTH.");
         return false;
     }
 
     const auto rowLengthBytes = CheckedUint32(rowLength) * bytesPerPixel;
     const auto rowStride = RoundUpToMultipleOf(rowLengthBytes, alignment);
 
     const auto usedBytesPerRow = usedPixelsPerRow * bytesPerPixel;
     const auto usedBytesPerImage = (usedRowsPerImage - 1) * rowStride + usedBytesPerRow;
 
     if (!rowStride.isValid() || !usedBytesPerImage.isValid()) {
-        ErrorInvalidOperation("%s: Invalid UNPACK_ params.", funcName);
+        ErrorInvalidOperation("Invalid UNPACK_ params.");
         return false;
     }
 
     *out_rowStride = rowStride.value();
     *out_endOffset = usedBytesPerImage.value();
     return true;
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                          GLenum type, const dom::ArrayBufferView& dstView,
                          GLuint dstElemOffset, CallerType aCallerType,
                          ErrorResult& out_error)
 {
-    const char funcName[] = "readPixels";
+    const FuncScope funcScope(*this, "readPixels");
+    if (IsContextLost())
+        return;
+
     if (!ReadPixels_SharedPrecheck(aCallerType, out_error))
         return;
 
     if (mBoundPixelPackBuffer) {
-        ErrorInvalidOperation("%s: PIXEL_PACK_BUFFER must be null.", funcName);
+        ErrorInvalidOperation("PIXEL_PACK_BUFFER must be null.");
         return;
     }
 
     ////
 
     js::Scalar::Type reqScalarType;
     if (!GetJSScalarFromGLType(type, &reqScalarType)) {
-        ErrorInvalidEnum("%s: Bad `type`.", funcName);
+        ErrorInvalidEnumInfo("type", type);
         return;
     }
 
     const auto& viewElemType = dstView.Type();
     if (viewElemType != reqScalarType) {
-        ErrorInvalidOperation("%s: `pixels` type does not match `type`.", funcName);
+        ErrorInvalidOperation("`pixels` type does not match `type`.");
         return;
     }
 
     ////
 
     uint8_t* bytes;
     size_t byteLen;
-    if (!ValidateArrayBufferView(funcName, dstView, dstElemOffset, 0, &bytes, &byteLen))
+    if (!ValidateArrayBufferView(dstView, dstElemOffset, 0, &bytes, &byteLen))
         return;
 
     ////
 
     ReadPixelsImpl(x, y, width, height, format, type, bytes, byteLen);
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                          GLenum type, WebGLsizeiptr offset,
                          CallerType aCallerType, ErrorResult& out_error)
 {
-    const char funcName[] = "readPixels";
+    const FuncScope funcScope(*this, "readPixels");
+    if (IsContextLost())
+        return;
+
     if (!ReadPixels_SharedPrecheck(aCallerType, out_error))
         return;
 
-    const auto& buffer = ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_PACK_BUFFER);
+    const auto& buffer = ValidateBufferSelection(LOCAL_GL_PIXEL_PACK_BUFFER);
     if (!buffer)
         return;
 
     //////
 
-    if (!ValidateNonNegative(funcName, "offset", offset))
+    if (!ValidateNonNegative("offset", offset))
         return;
 
     {
         const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
 
         if (offset % bytesPerType != 0) {
-            ErrorInvalidOperation("%s: `offset` must be divisible by the size of `type`"
-                                  " in bytes.",
-                                  funcName);
+            ErrorInvalidOperation("`offset` must be divisible by the size of `type`"
+                                  " in bytes.");
             return;
         }
     }
 
     //////
 
     const auto bytesAvailable = buffer->ByteLength();
     const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
@@ -1500,20 +1590,18 @@ WebGLContext::ValidImplementationColorRe
     return implPI;
 }
 
 static bool
 ValidateReadPixelsFormatAndType(const webgl::FormatUsageInfo* srcUsage,
                                 const webgl::PackingInfo& pi, gl::GLContext* gl,
                                 WebGLContext* webgl)
 {
-    const char funcName[] = "readPixels";
-
     if (!ArePossiblePackEnums(webgl, pi)) {
-        webgl->ErrorInvalidEnum("%s: Unexpected format or type.", funcName);
+        webgl->ErrorInvalidEnum("Unexpected format or type.");
         return false;
     }
 
     const auto defaultPI = DefaultReadPixelPI(srcUsage);
     if (pi == defaultPI)
         return true;
 
     ////
@@ -1534,86 +1622,84 @@ ValidateReadPixelsFormatAndType(const we
 
     MOZ_ASSERT(gl->IsCurrent());
     const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage);
     if (pi == implPI)
         return true;
 
     ////
 
-    webgl->ErrorInvalidOperation("%s: Incompatible format or type.", funcName);
+    webgl->ErrorInvalidOperation("Incompatible format or type.");
     return false;
 }
 
 void
 WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
                              GLenum packFormat, GLenum packType, void* dest,
                              uint32_t dataLen)
 {
-    if (rawWidth < 0 || rawHeight < 0) {
-        ErrorInvalidValue("readPixels: negative size passed");
+    if (!ValidateNonNegative("width", rawWidth) ||
+        !ValidateNonNegative("height", rawHeight))
+    {
         return;
     }
 
     const uint32_t width(rawWidth);
     const uint32_t height(rawHeight);
 
     //////
 
     const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
-    if (!BindCurFBForColorRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
+    if (!BindCurFBForColorRead(&srcFormat, &srcWidth, &srcHeight))
         return;
 
     //////
 
     const webgl::PackingInfo pi = {packFormat, packType};
     if (!ValidateReadPixelsFormatAndType(srcFormat, pi, gl, this))
         return;
 
     uint8_t bytesPerPixel;
     if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) {
-        ErrorInvalidOperation("readPixels: Unsupported format and type.");
+        ErrorInvalidOperation("Unsupported format and type.");
         return;
     }
 
     //////
 
     uint32_t rowStride;
     uint32_t bytesNeeded;
-    if (!ValidatePackSize("readPixels", width, height, bytesPerPixel, &rowStride,
-                          &bytesNeeded))
-    {
+    if (!ValidatePackSize(width, height, bytesPerPixel, &rowStride, &bytesNeeded))
         return;
-    }
 
     if (bytesNeeded > dataLen) {
-        ErrorInvalidOperation("readPixels: buffer too small");
+        ErrorInvalidOperation("buffer too small");
         return;
     }
 
     ////
 
     int32_t readX, readY;
     int32_t writeX, writeY;
     int32_t rwWidth, rwHeight;
     if (!Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth) ||
         !Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight))
     {
-        ErrorOutOfMemory("readPixels: Bad subrect selection.");
+        ErrorOutOfMemory("Bad subrect selection.");
         return;
     }
 
     ////////////////
     // Now that the errors are out of the way, on to actually reading!
 
     if (!rwWidth || !rwHeight) {
         // Disjoint rects, so we're done already.
-        DummyReadFramebufferOperation("readPixels");
+        DummyReadFramebufferOperation();
         return;
     }
 
     if (uint32_t(rwWidth) == width &&
         uint32_t(rwHeight) == height)
     {
         DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
                                packType, dest, dataLen, rowStride);
@@ -1622,17 +1708,17 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
 
     // Read request contains out-of-bounds pixels. Unfortunately:
     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
     // "If any of these pixels lies outside of the window allocated to the current GL
     //  context, or outside of the image attached to the currently bound framebuffer
     //  object, then the values obtained for those pixels are undefined."
 
     // This is a slow-path, so warn people away!
-    GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
+    GenerateWarning("Out-of-bounds reads with readPixels are deprecated, and"
                     " may be slow.");
 
     ////////////////////////////////////
     // Read only the in-bounds pixels.
 
     if (IsWebGL2()) {
         if (!mPixelStore_PackRowLength) {
             gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
@@ -1656,92 +1742,90 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
             DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
                                    packFormat, packType, row, dataLen, rowStride);
             row += rowStride;
         }
     }
 }
 
 void
-WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
-                                       GLsizei samples, GLenum internalFormat,
+WebGLContext::RenderbufferStorage_base(GLenum target, GLsizei samples,
+                                       GLenum internalFormat,
                                        GLsizei width, GLsizei height)
 {
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_RENDERBUFFER) {
-        ErrorInvalidEnumInfo("`target`", funcName, target);
+        ErrorInvalidEnumInfo("target", target);
         return;
     }
 
     if (!mBoundRenderbuffer) {
-        ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
-        return;
-    }
-
-    if (samples < 0) {
-        ErrorInvalidValue("%s: `samples` must be >= 0.", funcName);
+        ErrorInvalidOperation("Called on renderbuffer 0.");
         return;
     }
 
-    if (width < 0 || height < 0) {
-        ErrorInvalidValue("%s: `width` and `height` must be >= 0.", funcName);
+    if (!ValidateNonNegative("width", width) ||
+        !ValidateNonNegative("height", height) ||
+        !ValidateNonNegative("samples", samples))
+    {
         return;
     }
 
-    mBoundRenderbuffer->RenderbufferStorage(funcName, uint32_t(samples), internalFormat,
+    mBoundRenderbuffer->RenderbufferStorage(uint32_t(samples), internalFormat,
                                             uint32_t(width), uint32_t(height));
 }
 
 void
-WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
-{
-    RenderbufferStorage_base("renderbufferStorage", target, 0, internalFormat, width,
-                             height);
-}
-
-void
 WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
 {
+    const FuncScope funcScope(*this, "scissor");
     if (IsContextLost())
         return;
 
-    if (width < 0 || height < 0)
-        return ErrorInvalidValue("scissor: negative size");
+    if (!ValidateNonNegative("width", width) ||
+        !ValidateNonNegative("height", height))
+    {
+        return;
+    }
 
     gl->fScissor(x, y, width, height);
 }
 
 void
 WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask)
 {
+    const FuncScope funcScope(*this, "stencilFunc");
     if (IsContextLost())
         return;
 
-    if (!ValidateComparisonEnum(func, "stencilFunc: func"))
+    if (!ValidateComparisonEnum(*this, func))
         return;
 
     mStencilRefFront = ref;
     mStencilRefBack = ref;
     mStencilValueMaskFront = mask;
     mStencilValueMaskBack = mask;
 
     gl->fStencilFunc(func, ref, mask);
 }
 
 void
 WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
 {
+    const FuncScope funcScope(*this, "stencilFuncSeparate");
     if (IsContextLost())
         return;
 
-    if (!ValidateFaceEnum(face, "stencilFuncSeparate: face") ||
-        !ValidateComparisonEnum(func, "stencilFuncSeparate: func"))
+    if (!ValidateFaceEnum(face) ||
+        !ValidateComparisonEnum(*this, func))
+    {
         return;
+    }
 
     switch (face) {
         case LOCAL_GL_FRONT_AND_BACK:
             mStencilRefFront = ref;
             mStencilRefBack = ref;
             mStencilValueMaskFront = mask;
             mStencilValueMaskBack = mask;
             break;
@@ -1756,72 +1840,74 @@ WebGLContext::StencilFuncSeparate(GLenum
     }
 
     gl->fStencilFuncSeparate(face, func, ref, mask);
 }
 
 void
 WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
 {
+    const FuncScope funcScope(*this, "stencilOp");
     if (IsContextLost())
         return;
 
-    if (!ValidateStencilOpEnum(sfail, "stencilOp: sfail") ||
-        !ValidateStencilOpEnum(dpfail, "stencilOp: dpfail") ||
-        !ValidateStencilOpEnum(dppass, "stencilOp: dppass"))
+    if (!ValidateStencilOpEnum(sfail, "sfail") ||
+        !ValidateStencilOpEnum(dpfail, "dpfail") ||
+        !ValidateStencilOpEnum(dppass, "dppass"))
         return;
 
     gl->fStencilOp(sfail, dpfail, dppass);
 }
 
 void
 WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
 {
+    const FuncScope funcScope(*this, "stencilOpSeparate");
     if (IsContextLost())
         return;
 
-    if (!ValidateFaceEnum(face, "stencilOpSeparate: face") ||
-        !ValidateStencilOpEnum(sfail, "stencilOpSeparate: sfail") ||
-        !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") ||
-        !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
+    if (!ValidateFaceEnum(face) ||
+        !ValidateStencilOpEnum(sfail, "sfail") ||
+        !ValidateStencilOpEnum(dpfail, "dpfail") ||
+        !ValidateStencilOpEnum(dppass, "dppass"))
         return;
 
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Uniform setters.
 
 class ValidateIfSampler
 {
     const WebGLUniformLocation* const mLoc;
     const size_t mDataCount;
     const GLint* const mData;
     bool mIsValidatedSampler;
 
 public:
-    ValidateIfSampler(WebGLContext* webgl, const char* funcName,
+    ValidateIfSampler(WebGLContext* webgl,
                       WebGLUniformLocation* loc, size_t dataCount, const GLint* data,
                       bool* const out_error)
         : mLoc(loc)
         , mDataCount(dataCount)
         , mData(data)
         , mIsValidatedSampler(false)
     {
         if (!mLoc->mInfo->mSamplerTexList) {
             *out_error = false;
             return;
         }
 
         for (size_t i = 0; i < mDataCount; i++) {
             const auto& val = mData[i];
             if (val < 0 || uint32_t(val) >= webgl->GLMaxTextureUnits()) {
-                webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d"
+                webgl->ErrorInvalidValue("This uniform location is a sampler, but %d"
                                          " is not a valid texture unit.",
-                                         funcName, val);
+                                         val);
                 *out_error = true;
                 return;
             }
         }
 
         mIsValidatedSampler = true;
         *out_error = false;
     }
@@ -1842,150 +1928,155 @@ public:
     }
 };
 
 ////////////////////
 
 void
 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
 {
-    const char funcName[] = "uniform1i";
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, funcName))
+    const FuncScope funcScope(*this, "uniform1i");
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT))
         return;
 
     bool error;
-    const ValidateIfSampler validate(this, funcName, loc, 1, &a1, &error);
+    const ValidateIfSampler validate(this, loc, 1, &a1, &error);
     if (error)
         return;
 
     gl->fUniform1i(loc->mLoc, a1);
 }
 
 void
 WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
 {
-    const char funcName[] = "uniform2i";
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, funcName))
+    const FuncScope funcScope(*this, "uniform2i");
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT))
         return;
 
     gl->fUniform2i(loc->mLoc, a1, a2);
 }
 
 void
 WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3)
 {
-    const char funcName[] = "uniform3i";
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, funcName))
+    const FuncScope funcScope(*this, "uniform3i");
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT))
         return;
 
     gl->fUniform3i(loc->mLoc, a1, a2, a3);
 }
 
 void
 WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3,
                         GLint a4)
 {
-    const char funcName[] = "uniform4i";
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, funcName))
+    const FuncScope funcScope(*this, "uniform4i");
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT))
         return;
 
     gl->fUniform4i(loc->mLoc, a1, a2, a3, a4);
 }
 
 //////////
 
 void
 WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1)
 {
-    const char funcName[] = "uniform1f";
-    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, funcName))
+    const FuncScope funcScope(*this, "uniform1f");
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT))
         return;
 
     gl->fUniform1f(loc->mLoc, a1);
 }
 
 void
 WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2)
 {
-    const char funcName[] = "uniform2f";
-    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, funcName))
+    const FuncScope funcScope(*this, "uniform2f");
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT))
         return;
 
     gl->fUniform2f(loc->mLoc, a1, a2);
 }
 
 void
 WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
                         GLfloat a3)
 {
-    const char funcName[] = "uniform3f";
-    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, funcName))
+    const FuncScope funcScope(*this, "uniform3f");
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT))
         return;
 
     gl->fUniform3f(loc->mLoc, a1, a2, a3);
 }
 
 void
 WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
                         GLfloat a3, GLfloat a4)
 {
-    const char funcName[] = "uniform4f";
-    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, funcName))
+    const FuncScope funcScope(*this, "uniform4f");
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT))
         return;
 
     gl->fUniform4f(loc->mLoc, a1, a2, a3, a4);
 }
 
 ////////////////////////////////////////
 // Array
 
 static bool
-ValidateArrOffsetAndCount(WebGLContext* webgl, const char* funcName, size_t elemsAvail,
+ValidateArrOffsetAndCount(WebGLContext* webgl, size_t elemsAvail,
                           GLuint elemOffset, GLuint elemCountOverride,
                           size_t* const out_elemCount)
 {
+    if (webgl->IsContextLost())
+        return false;
+
     if (elemOffset > elemsAvail) {
-        webgl->ErrorInvalidValue("%s: Bad offset into list.", funcName);
+        webgl->ErrorInvalidValue("Bad offset into list.");
         return false;
     }
     elemsAvail -= elemOffset;
 
     if (elemCountOverride) {
         if (elemCountOverride > elemsAvail) {
-            webgl->ErrorInvalidValue("%s: Bad count override for sub-list.", funcName);
+            webgl->ErrorInvalidValue("Bad count override for sub-list.");
             return false;
         }
         elemsAvail = elemCountOverride;
     }
 
     *out_elemCount = elemsAvail;
     return true;
 }
 
 void
 WebGLContext::UniformNiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
                          const Int32Arr& arr, GLuint elemOffset, GLuint elemCountOverride)
 {
+    const FuncScope funcScope(*this, funcName);
+
     size_t elemCount;
-    if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
+    if (!ValidateArrOffsetAndCount(this, arr.elemCount, elemOffset,
                                    elemCountOverride, &elemCount))
     {
         return;
     }
     const auto elemBytes = arr.elemBytes + elemOffset;
 
     uint32_t numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_INT, elemCount, funcName,
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_INT, elemCount,
                                     &numElementsToUpload))
     {
         return;
     }
 
     bool error;
-    const ValidateIfSampler samplerValidator(this, funcName, loc, numElementsToUpload,
+    const ValidateIfSampler samplerValidator(this, loc, numElementsToUpload,
                                              elemBytes, &error);
     if (error)
         return;
 
     static const decltype(&gl::GLContext::fUniform1iv) kFuncList[] = {
         &gl::GLContext::fUniform1iv,
         &gl::GLContext::fUniform2iv,
         &gl::GLContext::fUniform3iv,
@@ -1996,26 +2087,28 @@ WebGLContext::UniformNiv(const char* fun
     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
 }
 
 void
 WebGLContext::UniformNuiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
                           const Uint32Arr& arr, GLuint elemOffset,
                           GLuint elemCountOverride)
 {
+    const FuncScope funcScope(*this, funcName);
+
     size_t elemCount;
-    if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
+    if (!ValidateArrOffsetAndCount(this, arr.elemCount, elemOffset,
                                    elemCountOverride, &elemCount))
     {
         return;
     }
     const auto elemBytes = arr.elemBytes + elemOffset;
 
     uint32_t numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_UNSIGNED_INT, elemCount, funcName,
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_UNSIGNED_INT, elemCount,
                                     &numElementsToUpload))
     {
         return;
     }
     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
 
     static const decltype(&gl::GLContext::fUniform1uiv) kFuncList[] = {
         &gl::GLContext::fUniform1uiv,
@@ -2028,26 +2121,28 @@ WebGLContext::UniformNuiv(const char* fu
     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
 }
 
 void
 WebGLContext::UniformNfv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
                          const Float32Arr& arr, GLuint elemOffset,
                          GLuint elemCountOverride)
 {
+    const FuncScope funcScope(*this, funcName);
+
     size_t elemCount;
-    if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
+    if (!ValidateArrOffsetAndCount(this, arr.elemCount, elemOffset,
                                    elemCountOverride, &elemCount))
     {
         return;
     }
     const auto elemBytes = arr.elemBytes + elemOffset;
 
     uint32_t numElementsToUpload;
-    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_FLOAT, elemCount, funcName,
+    if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_FLOAT, elemCount,
                                     &numElementsToUpload))
     {
         return;
     }
     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
 
     static const decltype(&gl::GLContext::fUniform1fv) kFuncList[] = {
         &gl::GLContext::fUniform1fv,
@@ -2073,27 +2168,29 @@ MatrixAxBToRowMajor(const uint8_t width,
 }
 
 void
 WebGLContext::UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
                                  WebGLUniformLocation* loc, const bool transpose,
                                  const Float32Arr& arr, GLuint elemOffset,
                                  GLuint elemCountOverride)
 {
+    const FuncScope funcScope(*this, funcName);
+
     size_t elemCount;
-    if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
+    if (!ValidateArrOffsetAndCount(this, arr.elemCount, elemOffset,
                                    elemCountOverride, &elemCount))
     {
         return;
     }
     const auto elemBytes = arr.elemBytes + elemOffset;
 
     uint32_t numMatsToUpload;
     if (!ValidateUniformMatrixArraySetter(loc, A, B, LOCAL_GL_FLOAT, elemCount,
-                                          transpose, funcName, &numMatsToUpload))
+                                          transpose, &numMatsToUpload))
     {
         return;
     }
     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
 
     ////
 
     bool uploadTranspose = transpose;
@@ -2103,18 +2200,17 @@ WebGLContext::UniformMatrixAxBfv(const c
     if (!transpose && gl->WorkAroundDriverBugs() && gl->IsANGLE() &&
         gl->IsAtLeast(gl::ContextProfile::OpenGLES, 300))
     {
         // ANGLE is really slow at non-GL-transposed matrices.
         const size_t kElemsPerMat = A * B;
 
         temp = malloc(numMatsToUpload * kElemsPerMat * sizeof(float));
         if (!temp) {
-            ErrorOutOfMemory("%s: Failed to alloc temporary buffer for transposition.",
-                             funcName);
+            ErrorOutOfMemory("Failed to alloc temporary buffer for transposition.");
             return;
         }
 
         auto srcItr = (const float*)elemBytes;
         auto dstItr = (float*)temp.get();
         const auto srcEnd = srcItr + numMatsToUpload * kElemsPerMat;
 
         while (srcItr != srcEnd) {
@@ -2147,152 +2243,164 @@ WebGLContext::UniformMatrixAxBfv(const c
     (gl->*func)(loc->mLoc, numMatsToUpload, uploadTranspose, uploadBytes);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 WebGLContext::UseProgram(WebGLProgram* prog)
 {
+    const FuncScope funcScope(*this, "useProgram");
     if (IsContextLost())
         return;
 
     if (!prog) {
         mCurrentProgram = nullptr;
         mActiveProgramLinkInfo = nullptr;
         return;
     }
 
-    if (!ValidateObject("useProgram", *prog))
+    if (!ValidateObject("prog", *prog))
         return;
 
     if (prog->UseProgram()) {
         mCurrentProgram = prog;
         mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
     }
 }
 
 void
 WebGLContext::ValidateProgram(const WebGLProgram& prog)
 {
+    const FuncScope funcScope(*this, "validateProgram");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("validateProgram", prog))
+    if (!ValidateObject("prog", prog))
         return;
 
     prog.ValidateProgram();
 }
 
 already_AddRefed<WebGLFramebuffer>
 WebGLContext::CreateFramebuffer()
 {
+    const FuncScope funcScope(*this, "createFramebuffer");
     if (IsContextLost())
         return nullptr;
 
     GLuint fbo = 0;
     gl->fGenFramebuffers(1, &fbo);
 
     RefPtr<WebGLFramebuffer> globj = new WebGLFramebuffer(this, fbo);
     return globj.forget();
 }
 
 already_AddRefed<WebGLRenderbuffer>
 WebGLContext::CreateRenderbuffer()
 {
+    const FuncScope funcScope(*this, "createRenderbuffer");
     if (IsContextLost())
         return nullptr;
 
     RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
     return globj.forget();
 }
 
 void
 WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
 {
+    const FuncScope funcScope(*this, "viewport");
     if (IsContextLost())
         return;
 
-    if (width < 0 || height < 0)
-        return ErrorInvalidValue("viewport: negative size");
+    if (!ValidateNonNegative("width", width) ||
+        !ValidateNonNegative("height", height))
+    {
+        return;
+    }
 
     width = std::min(width, (GLsizei)mGLMaxViewportDims[0]);
     height = std::min(height, (GLsizei)mGLMaxViewportDims[1]);
 
     gl->fViewport(x, y, width, height);
 
     mViewportX = x;
     mViewportY = y;
     mViewportWidth = width;
     mViewportHeight = height;
 }
 
 void
 WebGLContext::CompileShader(WebGLShader& shader)
 {
+    const FuncScope funcScope(*this, "compileShader");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("compileShader", shader))
+    if (!ValidateObject("shader", shader))
         return;
 
     shader.CompileShader();
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
 {
+    const FuncScope funcScope(*this, "getShaderParameter");
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getShaderParameter: shader", shader))
+    if (!ValidateObjectAllowDeleted("shader", shader))
         return JS::NullValue();
 
     return shader.GetShaderParameter(pname);
 }
 
 void
 WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
+    const FuncScope funcScope(*this, "getShaderInfoLog");
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getShaderInfoLog: shader", shader))
+    if (!ValidateObject("shader", shader))
         return;
 
     shader.GetShaderInfoLog(&retval);
 }
 
 already_AddRefed<WebGLShaderPrecisionFormat>
 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
 {
+    const FuncScope funcScope(*this, "getShaderPrecisionFormat");
     if (IsContextLost())
         return nullptr;
 
     switch (shadertype) {
         case LOCAL_GL_FRAGMENT_SHADER:
         case LOCAL_GL_VERTEX_SHADER:
             break;
         default:
-            ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype);
+            ErrorInvalidEnumInfo("shadertype", shadertype);
             return nullptr;
     }
 
     switch (precisiontype) {
         case LOCAL_GL_LOW_FLOAT:
         case LOCAL_GL_MEDIUM_FLOAT:
         case LOCAL_GL_HIGH_FLOAT:
         case LOCAL_GL_LOW_INT:
         case LOCAL_GL_MEDIUM_INT:
         case LOCAL_GL_HIGH_INT:
             break;
         default:
-            ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype);
+            ErrorInvalidEnumInfo("precisiontype", precisiontype);
             return nullptr;
     }
 
     GLint range[2], precision;
 
     if (mDisableFragHighP &&
         shadertype == LOCAL_GL_FRAGMENT_SHADER &&
         (precisiontype == LOCAL_GL_HIGH_FLOAT ||
@@ -2309,130 +2417,140 @@ WebGLContext::GetShaderPrecisionFormat(G
         = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
     return retShaderPrecisionFormat.forget();
 }
 
 void
 WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
+    const FuncScope funcScope(*this, "getShaderSource");
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("getShaderSource: shader", shader))
+    if (!ValidateObject("shader", shader))
         return;
 
     shader.GetShaderSource(&retval);
 }
 
 void
 WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
 {
+    const FuncScope funcScope(*this, "shaderSource");
     if (IsContextLost())
         return;
 
-    if (!ValidateObject("shaderSource: shader", shader))
+    if (!ValidateObject("shader", shader))
         return;
 
     shader.ShaderSource(source);
 }
 
 void
 WebGLContext::LoseContext()
 {
+    const FuncScope funcScope(*this, "loseContext");
     if (IsContextLost())
-        return ErrorInvalidOperation("loseContext: Context is already lost.");
+        return ErrorInvalidOperation("Context is already lost.");
 
     ForceLoseContext(true);
 }
 
 void
 WebGLContext::RestoreContext()
 {
+    const FuncScope funcScope(*this, "restoreContext");
     if (!IsContextLost())
-        return ErrorInvalidOperation("restoreContext: Context is not lost.");
+        return ErrorInvalidOperation("Context is not lost.");
 
     if (!mLastLossWasSimulated) {
-        return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
+        return ErrorInvalidOperation("Context loss was not simulated."
                                      " Cannot simulate restore.");
     }
     // If we're currently lost, and the last loss was simulated, then
     // we're currently only simulated-lost, allowing us to call
     // restoreContext().
 
     if (!mAllowContextRestore)
-        return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
+        return ErrorInvalidOperation("Context cannot be restored.");
 
     ForceRestoreContext();
 }
 
 void
 WebGLContext::BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
 {
+    const FuncScope funcScope(*this, "blendColor");
     if (IsContextLost())
         return;
 
     gl->fBlendColor(r, g, b, a);
 }
 
 void
 WebGLContext::Flush()
 {
+    const FuncScope funcScope(*this, "flush");
     if (IsContextLost())
         return;
 
     gl->fFlush();
 }
 
 void
 WebGLContext::Finish()
 {
+    const FuncScope funcScope(*this, "finish");
     if (IsContextLost())
         return;
 
     gl->fFinish();
 
     mCompletedFenceId = mNextFenceId;
     mNextFenceId += 1;
 }
 
 void
 WebGLContext::LineWidth(GLfloat width)
 {
+    const FuncScope funcScope(*this, "lineWidth");
     if (IsContextLost())
         return;
 
     // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
     const bool isValid = width > 0.0;
     if (!isValid) {
-        ErrorInvalidValue("lineWidth: `width` must be positive and non-zero.");
+        ErrorInvalidValue("`width` must be positive and non-zero.");
         return;
     }
 
     mLineWidth = width;
 
     if (gl->IsCoreProfile() && width > 1.0) {
         width = 1.0;
     }
 
     gl->fLineWidth(width);
 }
 
 void
 WebGLContext::PolygonOffset(GLfloat factor, GLfloat units)
 {
+    const FuncScope funcScope(*this, "polygonOffset");
     if (IsContextLost())
         return;
 
     gl->fPolygonOffset(factor, units);
 }
 
 void
 WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert)
 {
+    const FuncScope funcScope(*this, "sampleCoverage");
     if (IsContextLost())
         return;
 
     gl->fSampleCoverage(value, invert);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -21,20 +21,21 @@
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 void
 WebGLContext::SetEnabled(const char* const funcName, const GLenum cap, const bool enabled)
 {
+    const FuncScope funcScope(*this, funcName);
     if (IsContextLost())
         return;
 
-    if (!ValidateCapabilityEnum(cap, funcName))
+    if (!ValidateCapabilityEnum(cap))
         return;
 
     const auto& slot = GetStateTrackingSlot(cap);
     if (slot) {
         *slot = enabled;
     }
 
     switch (cap) {
@@ -72,17 +73,17 @@ WebGLContext::GetStencilBits(GLint* cons
     }
 
     return true;
 }
 
 JS::Value
 WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
 {
-    const char funcName[] = "getParameter";
+    const FuncScope funcScope(*this, "getParameter");
 
     if (IsContextLost())
         return JS::NullValue();
 
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
         if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
             return JS::Int32Value(mGLMaxColorAttachments);
 
@@ -242,17 +243,17 @@ WebGLContext::GetParameter(JSContext* cx
 
         case LOCAL_GL_GENERATE_MIPMAP_HINT:
             return JS::NumberValue(mGenerateMipmapHint);
 
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
-            if (!BindCurFBForColorRead(funcName, &usage, &width, &height))
+            if (!BindCurFBForColorRead(&usage, &width, &height))
                 return JS::NullValue();
 
             const auto implPI = ValidImplementationColorReadPI(usage);
 
             GLenum ret;
             if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
                 ret = implPI.format;
             } else {
@@ -277,22 +278,22 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(refValue & stencilMask);
         }
 
         case LOCAL_GL_SAMPLE_BUFFERS:
         case LOCAL_GL_SAMPLES: {
             const auto& fb = mBoundDrawFramebuffer;
             auto samples = [&]() -> Maybe<uint32_t> {
                 if (!fb) {
-                    if (!EnsureDefaultFB(funcName))
+                    if (!EnsureDefaultFB())
                         return Nothing();
                     return Some(mDefaultFB->mSamples);
                 }
 
-                if (!fb->IsCheckFramebufferStatusComplete(funcName))
+                if (!fb->IsCheckFramebufferStatusComplete())
                     return Some(0);
 
                 DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
                 return Some(gl->GetIntAs<uint32_t>(LOCAL_GL_SAMPLES));
             }();
             if (samples && pname == LOCAL_GL_SAMPLE_BUFFERS) {
                 samples = Some(uint32_t(bool(samples.value())));
             }
@@ -585,83 +586,85 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: {
             return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv);
         }
 
         default:
             break;
     }
 
-    ErrorInvalidEnumInfo("getParameter: parameter", pname);
+    ErrorInvalidEnumInfo("pname", pname);
     return JS::NullValue();
 }
 
 void
 WebGLContext::GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index,
                                   JS::MutableHandle<JS::Value> retval)
 {
+    const FuncScope funcScope(*this, "getParameterIndexed");
     if (IsContextLost()) {
         retval.setNull();
         return;
     }
 
     switch (pname) {
         case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
         {
             if (index >= mGLMaxTransformFeedbackSeparateAttribs) {
-                ErrorInvalidValue("getParameterIndexed: index should be less than MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
+                ErrorInvalidValue("`index` should be less than MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
                 retval.setNull();
                 return;
             }
             retval.setNull(); // See bug 903594
             return;
         }
 
         default:
             break;
     }
 
-    ErrorInvalidEnumInfo("getParameterIndexed: parameter", pname);
+    ErrorInvalidEnumInfo("pname", pname);
     retval.setNull();
 }
 
 bool
 WebGLContext::IsEnabled(GLenum cap)
 {
+    const FuncScope funcScope(*this, "isEnabled");
     if (IsContextLost())
         return false;
 
-    if (!ValidateCapabilityEnum(cap, "isEnabled"))
+    if (!ValidateCapabilityEnum(cap))
         return false;
 
     const auto& slot = GetStateTrackingSlot(cap);
     if (slot)
         return *slot;
 
     return gl->fIsEnabled(cap);
 }
 
 bool
-WebGLContext::ValidateCapabilityEnum(GLenum cap, const char* info)
+WebGLContext::ValidateCapabilityEnum(GLenum cap)
 {
     switch (cap) {
         case LOCAL_GL_BLEND:
         case LOCAL_GL_CULL_FACE:
         case LOCAL_GL_DEPTH_TEST:
         case LOCAL_GL_DITHER:
         case LOCAL_GL_POLYGON_OFFSET_FILL:
         case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
         case LOCAL_GL_SAMPLE_COVERAGE:
         case LOCAL_GL_SCISSOR_TEST:
         case LOCAL_GL_STENCIL_TEST:
             return true;
         case LOCAL_GL_RASTERIZER_DISCARD:
             return IsWebGL2();
         default:
-            ErrorInvalidEnumInfo(info, cap);
+            ErrorInvalidEnumInfo("cap", cap);
             return false;
     }
 }
 
 realGLboolean*
 WebGLContext::GetStateTrackingSlot(GLenum cap)
 {
     switch (cap) {
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -115,57 +115,57 @@ IsValidTexImageTarget(WebGLContext* webg
     if (targetDims != funcDims)
         return false;
 
     *out = rawTexImageTarget;
     return true;
 }
 
 bool
-ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+ValidateTexTarget(WebGLContext* webgl, uint8_t funcDims,
                   GLenum rawTexTarget, TexTarget* const out_texTarget,
                   WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexTarget texTarget;
     if (!IsValidTexTarget(webgl, funcDims, rawTexTarget, &texTarget)) {
-        webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName);
+        webgl->ErrorInvalidEnumInfo("texTarget", rawTexTarget);
         return false;
     }
 
     WebGLTexture* tex = webgl->ActiveBoundTextureForTarget(texTarget);
     if (!tex) {
-        webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
+        webgl->ErrorInvalidOperation("No texture is bound to this target.");
         return false;
     }
 
     *out_texTarget = texTarget;
     *out_tex = tex;
     return true;
 }
 
 bool
-ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+ValidateTexImageTarget(WebGLContext* webgl, uint8_t funcDims,
                        GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexImageTarget texImageTarget;
     if (!IsValidTexImageTarget(webgl, funcDims, rawTexImageTarget, &texImageTarget)) {
-        webgl->ErrorInvalidEnum("%s: Invalid texImageTarget.", funcName);
+        webgl->ErrorInvalidEnumInfo("texImageTarget", rawTexImageTarget);
         return false;
     }
 
     WebGLTexture* tex = webgl->ActiveBoundTextureForTexImageTarget(texImageTarget);
     if (!tex) {
-        webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
+        webgl->ErrorInvalidOperation("No texture is bound to this target.");
         return false;
     }
 
     *out_texImageTarget = texImageTarget;
     *out_tex = tex;
     return true;
 }
 
@@ -201,20 +201,21 @@ WebGLContext::InvalidateResolveCacheForT
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // GL calls
 
 void
 WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
 {
+    const FuncScope funcScope(*this, "bindTexture");
     if (IsContextLost())
         return;
 
-     if (newTex && !ValidateObject("bindTexture", *newTex))
+     if (newTex && !ValidateObject("tex", *newTex))
         return;
 
     // Need to check rawTarget first before comparing against newTex->Target() as
     // newTex->Target() returns a TexTarget, which will assert on invalid value.
     WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
     switch (rawTarget) {
     case LOCAL_GL_TEXTURE_2D:
         currentTexPtr = &mBound2DTextures[mActiveTexture];
@@ -231,17 +232,17 @@ WebGLContext::BindTexture(GLenum rawTarg
 
    case LOCAL_GL_TEXTURE_2D_ARRAY:
         if (IsWebGL2())
             currentTexPtr = &mBound2DArrayTextures[mActiveTexture];
         break;
     }
 
     if (!currentTexPtr) {
-        ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
+        ErrorInvalidEnumInfo("target", rawTarget);
         return;
     }
 
     const TexTarget texTarget(rawTarget);
     if (newTex) {
         if (!newTex->BindTexture(texTarget))
             return;
     } else {
@@ -249,164 +250,155 @@ WebGLContext::BindTexture(GLenum rawTarg
     }
 
     *currentTexPtr = newTex;
 }
 
 void
 WebGLContext::GenerateMipmap(GLenum rawTexTarget)
 {
-    const char funcName[] = "generateMipmap";
+    const FuncScope funcScope(*this, "generateMipmap");
     const uint8_t funcDims = 0;
 
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex))
         return;
 
     tex->GenerateMipmap(texTarget);
 }
 
 JS::Value
 WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname)
 {
-    const char funcName[] = "getTexParameter";
+    const FuncScope funcScope(*this, "getTexParameter");
     const uint8_t funcDims = 0;
 
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex))
         return JS::NullValue();
 
     if (!IsTexParamValid(pname)) {
-        ErrorInvalidEnumInfo("getTexParameter: pname", pname);
+        ErrorInvalidEnumInfo("pname", pname);
         return JS::NullValue();
     }
 
     return tex->GetTexParameter(texTarget, pname);
 }
 
-bool
-WebGLContext::IsTexture(WebGLTexture* tex)
-{
-    if (!ValidateIsObject("isTexture", tex))
-        return false;
-
-    return tex->IsTexture();
-}
-
 void
 WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname,
                                 const FloatOrInt& param)
 {
-    const char funcName[] = "texParameter";
+    const FuncScope funcScope(*this, "texParameter");
     const uint8_t funcDims = 0;
 
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcDims, rawTexTarget, &texTarget, &tex))
         return;
 
     tex->TexParameter(texTarget, pname, param);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // Uploads
 
 void
-WebGLContext::CompressedTexImage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
+WebGLContext::CompressedTexImage(uint8_t funcDims, GLenum rawTarget,
                                  GLint level, GLenum internalFormat, GLsizei width,
                                  GLsizei height, GLsizei depth, GLint border,
                                  const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize)
 {
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
-    tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth,
+    tex->CompressedTexImage(target, level, internalFormat, width, height, depth,
                             border, src, expectedImageSize);
 }
 
 void
-WebGLContext::CompressedTexSubImage(const char* funcName, uint8_t funcDims,
+WebGLContext::CompressedTexSubImage(uint8_t funcDims,
                                     GLenum rawTarget, GLint level, GLint xOffset,
                                     GLint yOffset, GLint zOffset, GLsizei width,
                                     GLsizei height, GLsizei depth, GLenum unpackFormat,
                                     const TexImageSource& src, const Maybe<GLsizei>& expectedImageSize)
 {
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
-    tex->CompressedTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width,
+    tex->CompressedTexSubImage(target, level, xOffset, yOffset, zOffset, width,
                                height, depth, unpackFormat, src, expectedImageSize);
 }
 
 ////
 
 void
 WebGLContext::CopyTexImage2D(GLenum rawTarget, GLint level, GLenum internalFormat,
                              GLint x, GLint y, GLsizei width, GLsizei height,
                              GLint border)
 {
-    const char funcName[] = "copyTexImage2D";
+    const FuncScope funcScope(*this, "copyTexImage2D");
     const uint8_t funcDims = 2;
 
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
     tex->CopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
 }
 
 void
-WebGLContext::CopyTexSubImage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
+WebGLContext::CopyTexSubImage(uint8_t funcDims, GLenum rawTarget,
                               GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
                               GLint x, GLint y, GLsizei width, GLsizei height)
 {
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
-    tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width,
+    tex->CopyTexSubImage(target, level, xOffset, yOffset, zOffset, x, y, width,
                          height);
 }
 
 ////
 
 void
-WebGLContext::TexImage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
+WebGLContext::TexImage(uint8_t funcDims, GLenum rawTarget,
                        GLint level, GLenum internalFormat, GLsizei width, GLsizei height,
                        GLsizei depth, GLint border, GLenum unpackFormat,
                        GLenum unpackType, const TexImageSource& src)
 {
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
     const webgl::PackingInfo pi = {unpackFormat, unpackType};
-    tex->TexImage(funcName, target, level, internalFormat, width, height, depth, border,
+    tex->TexImage(target, level, internalFormat, width, height, depth, border,
                   pi, src);
 }
 
 void
-WebGLContext::TexSubImage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
+WebGLContext::TexSubImage(uint8_t funcDims, GLenum rawTarget,
                           GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
                           GLsizei width, GLsizei height, GLsizei depth,
                           GLenum unpackFormat, GLenum unpackType,
                           const TexImageSource& src)
 {
     TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTarget, &target, &tex))
+    if (!ValidateTexImageTarget(this, funcDims, rawTarget, &target, &tex))
         return;
 
     const webgl::PackingInfo pi = {unpackFormat, unpackType};
-    tex->TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width, height,
+    tex->TexSubImage(target, level, xOffset, yOffset, zOffset, width, height,
                      depth, pi, src);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -84,17 +84,18 @@ WebGLContext::GenerateWarning(const char
     }
 
     dom::AutoJSAPI api;
     if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) {
         return;
     }
 
     JSContext* cx = api.cx();
-    JS_ReportWarningASCII(cx, "WebGL warning: %s", buf);
+    const auto funcName = FuncName();
+    JS_ReportWarningASCII(cx, "WebGL warning: %s: %s", funcName, buf);
     if (!ShouldGenerateWarnings()) {
         JS_ReportWarningASCII(cx,
                               "WebGL: No further warnings will be reported for"
                               " this WebGL context."
                               " (already reported %d warnings)",
                               mAlreadyGeneratedWarnings);
     }
 }
@@ -129,17 +130,18 @@ WebGLContext::GeneratePerfWarning(const 
 
     char buf[1024];
     VsprintfLiteral(buf, fmt, ap);
 
     va_end(ap);
 
     ////
 
-    JS_ReportWarningASCII(cx, "WebGL perf warning: %s", buf);
+    const auto funcName = FuncName();
+    JS_ReportWarningASCII(cx, "WebGL perf warning: %s: %s", funcName, buf);
     mNumPerfWarnings++;
 
     if (!ShouldGeneratePerfWarnings()) {
         JS_ReportWarningASCII(cx,
                               "WebGL: After reporting %u, no further perf warnings will"
                               " be reported for this WebGL context.",
                               uint32_t(mNumPerfWarnings));
     }
@@ -185,27 +187,16 @@ WebGLContext::ErrorInvalidEnumInfo(const
 {
     nsCString name;
     EnumName(enumValue, &name);
 
     return ErrorInvalidEnum("%s: invalid enum value %s", info, name.BeginReading());
 }
 
 void
-WebGLContext::ErrorInvalidEnumInfo(const char* info, const char* funcName,
-                                   GLenum enumValue) const
-{
-    nsCString name;
-    EnumName(enumValue, &name);
-
-    ErrorInvalidEnum("%s: %s: Invalid enum: 0x%04x (%s).", funcName, info,
-                     enumValue, name.BeginReading());
-}
-
-void
 WebGLContext::ErrorInvalidOperation(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION);
@@ -278,18 +269,18 @@ WebGLContext::ErrorName(GLenum error)
         return "NO_ERROR";
     default:
         MOZ_ASSERT(false);
         return "[unknown WebGL error]";
     }
 }
 
 // This version is fallible and will return nullptr if unrecognized.
-static const char*
-GetEnumName(GLenum val)
+const char*
+GetEnumName(const GLenum val, const char* const defaultRet)
 {
     switch (val) {
 #define XX(x) case LOCAL_GL_##x: return #x
         XX(NONE);
         XX(ALPHA);
         XX(ATC_RGB);
         XX(ATC_RGBA_EXPLICIT_ALPHA);
         XX(ATC_RGBA_INTERPOLATED_ALPHA);
@@ -604,38 +595,49 @@ GetEnumName(GLenum val)
         XX(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC);
         XX(TEXTURE_IMMUTABLE_FORMAT);
         XX(MAX_ELEMENT_INDEX);
         XX(NUM_SAMPLE_COUNTS);
         XX(TEXTURE_IMMUTABLE_LEVELS);
 #undef XX
     }
 
-    return nullptr;
+    return defaultRet;
 }
 
 /*static*/ void
 WebGLContext::EnumName(GLenum val, nsCString* out_name)
 {
-    const char* name = GetEnumName(val);
+    const char* name = GetEnumName(val, nullptr);
     if (name) {
         *out_name = name;
         return;
     }
 
     *out_name = nsPrintfCString("<enum 0x%04x>", val);
 }
 
+std::string
+EnumString(const GLenum val)
+{
+    const char* name = GetEnumName(val, nullptr);
+    if (name) {
+        return name;
+    }
+
+    const nsPrintfCString hex("<enum 0x%04x>", val);
+    return hex.BeginReading();
+}
+
 void
-WebGLContext::ErrorInvalidEnumArg(const char* funcName, const char* argName,
-                                  GLenum val) const
+WebGLContext::ErrorInvalidEnumArg(const char* argName, GLenum val) const
 {
     nsCString enumName;
     EnumName(val, &enumName);
-    ErrorInvalidEnum("%s: Bad `%s`: %s", funcName, argName, enumName.BeginReading());
+    ErrorInvalidEnum("Bad `%s`: %s", argName, enumName.BeginReading());
 }
 
 bool
 IsCompressedTextureFormat(GLenum format)
 {
     switch (format) {
     case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
     case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -143,36 +143,16 @@ WebGLContext::ValidateBlendFuncEnumsComp
                               " the WebGL 1.0 spec", info);
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLContext::ValidateComparisonEnum(GLenum target, const char* info)
-{
-    switch (target) {
-    case LOCAL_GL_NEVER:
-    case LOCAL_GL_LESS:
-    case LOCAL_GL_LEQUAL:
-    case LOCAL_GL_GREATER:
-    case LOCAL_GL_GEQUAL:
-    case LOCAL_GL_EQUAL:
-    case LOCAL_GL_NOTEQUAL:
-    case LOCAL_GL_ALWAYS:
-        return true;
-
-    default:
-        ErrorInvalidEnumInfo(info, target);
-        return false;
-    }
-}
-
-bool
 WebGLContext::ValidateStencilOpEnum(GLenum action, const char* info)
 {
     switch (action) {
     case LOCAL_GL_KEEP:
     case LOCAL_GL_ZERO:
     case LOCAL_GL_REPLACE:
     case LOCAL_GL_INCR:
     case LOCAL_GL_INCR_WRAP:
@@ -183,122 +163,99 @@ WebGLContext::ValidateStencilOpEnum(GLen
 
     default:
         ErrorInvalidEnumInfo(info, action);
         return false;
     }
 }
 
 bool
-WebGLContext::ValidateFaceEnum(GLenum face, const char* info)
+WebGLContext::ValidateFaceEnum(const GLenum face)
 {
     switch (face) {
     case LOCAL_GL_FRONT:
     case LOCAL_GL_BACK:
     case LOCAL_GL_FRONT_AND_BACK:
         return true;
 
     default:
-        ErrorInvalidEnumInfo(info, face);
+        ErrorInvalidEnumInfo("face", face);
         return false;
     }
 }
 
 bool
-WebGLContext::ValidateDrawModeEnum(GLenum mode, const char* info)
-{
-    switch (mode) {
-    case LOCAL_GL_TRIANGLES:
-    case LOCAL_GL_TRIANGLE_STRIP:
-    case LOCAL_GL_TRIANGLE_FAN:
-    case LOCAL_GL_POINTS:
-    case LOCAL_GL_LINE_STRIP:
-    case LOCAL_GL_LINE_LOOP:
-    case LOCAL_GL_LINES:
-        return true;
-
-    default:
-        ErrorInvalidEnumInfo(info, mode);
-        return false;
-    }
-}
-
-bool
-WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
+WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc)
 {
     /* 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;
 
-    if (!ValidateObjectAllowDeleted(funcName, *loc))
+    if (!ValidateObjectAllowDeleted("loc", *loc))
         return false;
 
     if (!mCurrentProgram) {
-        ErrorInvalidOperation("%s: No program is currently bound.", funcName);
+        ErrorInvalidOperation("No program is currently bound.");
         return false;
     }
 
-    return loc->ValidateForProgram(mCurrentProgram, funcName);
+    return loc->ValidateForProgram(mCurrentProgram);
 }
 
 bool
-WebGLContext::ValidateAttribArraySetter(const char* name, uint32_t setterElemSize,
-                                        uint32_t arrayLength)
+WebGLContext::ValidateAttribArraySetter(uint32_t setterElemSize, uint32_t arrayLength)
 {
     if (IsContextLost())
         return false;
 
     if (arrayLength < setterElemSize) {
-        ErrorInvalidValue("%s: Array must have >= %d elements.", name,
-                          setterElemSize);
+        ErrorInvalidValue("Array must have >= %d elements.", setterElemSize);
         return false;
     }
 
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
-                                    uint8_t setterElemSize, GLenum setterType,
-                                    const char* funcName)
+                                    uint8_t setterElemSize, GLenum setterType)
 {
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(loc, funcName))
+    if (!ValidateUniformLocation(loc))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
         return false;
 
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                          uint8_t setterElemSize,
                                          GLenum setterType,
                                          uint32_t setterArraySize,
-                                         const char* funcName,
                                          uint32_t* const out_numElementsToUpload)
 {
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(loc, funcName))
+    if (!ValidateUniformLocation(loc))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
         return false;
 
-    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize))
         return false;
 
     const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
     MOZ_ASSERT(elemCount > loc->mArrayIndex);
     const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
 
     *out_numElementsToUpload = std::min(uniformElemCount,
                                         setterArraySize / setterElemSize);
@@ -307,70 +264,47 @@ WebGLContext::ValidateUniformArraySetter
 
 bool
 WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                                uint8_t setterCols,
                                                uint8_t setterRows,
                                                GLenum setterType,
                                                uint32_t setterArraySize,
                                                bool setterTranspose,
-                                               const char* funcName,
                                                uint32_t* const out_numElementsToUpload)
 {
     const uint8_t setterElemSize = setterCols * setterRows;
 
     if (IsContextLost())
         return false;
 
-    if (!ValidateUniformLocation(loc, funcName))
+    if (!ValidateUniformLocation(loc))
         return false;
 
-    if (!loc->ValidateSizeAndType(setterElemSize, setterType, funcName))
+    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
         return false;
 
-    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, funcName))
+    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize))
         return false;
 
     if (setterTranspose && !IsWebGL2()) {
-        ErrorInvalidValue("%s: `transpose` must be false.", funcName);
+        ErrorInvalidValue("`transpose` must be false.");
         return false;
     }
 
     const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
     MOZ_ASSERT(elemCount > loc->mArrayIndex);
     const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;
 
     *out_numElementsToUpload = std::min(uniformElemCount,
                                         setterArraySize / setterElemSize);
     return true;
 }
 
 bool
-WebGLContext::ValidateAttribIndex(GLuint index, const char* info)
-{
-    bool valid = (index < MaxVertexAttribs());
-
-    if (!valid) {
-        if (index == GLuint(-1)) {
-            ErrorInvalidValue("%s: -1 is not a valid `index`. This value"
-                              " probably comes from a getAttribLocation()"
-                              " call, where this return value -1 means"
-                              " that the passed name didn't correspond to"
-                              " an active attribute in the specified"
-                              " program.", info);
-        } else {
-            ErrorInvalidValue("%s: `index` must be less than"
-                              " MAX_VERTEX_ATTRIBS.", info);
-        }
-    }
-
-    return valid;
-}
-
-bool
 WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
 {
     MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
 
     // Unconditionally create a new format usage authority. This is
     // important when restoring contexts and extensions need to add
     // formats back into the authority.
     mFormatUsage = CreateFormatUsage(gl);
@@ -750,18 +684,17 @@ WebGLContext::InitAndValidateGL(FailureR
         MOZ_ASSERT(gfxPrefs::WebGLForceIndexValidation() == 0);
         break;
     }
 
     return true;
 }
 
 bool
-WebGLContext::ValidateFramebufferTarget(GLenum target,
-                                        const char* const info)
+WebGLContext::ValidateFramebufferTarget(GLenum target)
 {
     bool isValid = true;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
         break;
 
     case LOCAL_GL_DRAW_FRAMEBUFFER:
     case LOCAL_GL_READ_FRAMEBUFFER:
@@ -772,13 +705,13 @@ WebGLContext::ValidateFramebufferTarget(
         isValid = false;
         break;
     }
 
     if (MOZ_LIKELY(isValid)) {
         return true;
     }
 
-    ErrorInvalidEnumArg(info, "target", target);
+    ErrorInvalidEnumArg("target", target);
     return false;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -10,20 +10,21 @@
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 void
 WebGLContext::BindVertexArray(WebGLVertexArray* array)
 {
+    const FuncScope funcScope(*this, "bindVertexArray");
     if (IsContextLost())
         return;
 
-    if (array && !ValidateObject("bindVertexArrayObject", *array))
+    if (array && !ValidateObject("array", *array))
         return;
 
     if (mBoundVertexArray) {
         mBoundVertexArray->AddBufferBindCounts(-1);
     }
 
     if (array == nullptr) {
         array = mDefaultVertexArray;
@@ -35,16 +36,17 @@ WebGLContext::BindVertexArray(WebGLVerte
     if (mBoundVertexArray) {
         mBoundVertexArray->AddBufferBindCounts(+1);
     }
 }
 
 already_AddRefed<WebGLVertexArray>
 WebGLContext::CreateVertexArray()
 {
+    const FuncScope funcScope(*this, "createVertexArray");
     if (IsContextLost())
         return nullptr;
 
     RefPtr<WebGLVertexArray> globj = CreateVertexArrayImpl();
 
     globj->GenVertexArray();
 
     return globj.forget();
@@ -54,27 +56,19 @@ WebGLVertexArray*
 WebGLContext::CreateVertexArrayImpl()
 {
     return WebGLVertexArray::Create(this);
 }
 
 void
 WebGLContext::DeleteVertexArray(WebGLVertexArray* array)
 {
-    if (!ValidateDeleteObject("deleteVertexArray", array))
+    const FuncScope funcScope(*this, "deleteVertexArray");
+    if (!ValidateDeleteObject(array))
         return;
 
     if (mBoundVertexArray == array)
         BindVertexArray(static_cast<WebGLVertexArray*>(nullptr));
 
     array->RequestDelete();
 }
 
-bool
-WebGLContext::IsVertexArray(const WebGLVertexArray* array)
-{
-    if (!ValidateIsObject("isVertexArray", array))
-        return false;
-
-    return array->IsVertexArray();
-}
-
 } // namespace mozilla
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -15,16 +15,38 @@
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #include "mozilla/Casting.h"
 
 namespace mozilla {
 
+static bool
+ValidateAttribIndex(WebGLContext& webgl, GLuint index)
+{
+    bool valid = (index < webgl.MaxVertexAttribs());
+
+    if (!valid) {
+        if (index == GLuint(-1)) {
+            webgl.ErrorInvalidValue("-1 is not a valid `index`. This value"
+                                    " probably comes from a getAttribLocation()"
+                                    " call, where this return value -1 means"
+                                    " that the passed name didn't correspond to"
+                                    " an active attribute in the specified"
+                                    " program.");
+        } else {
+            webgl.ErrorInvalidValue("`index` must be less than"
+                                    " MAX_VERTEX_ATTRIBS.");
+        }
+    }
+
+    return valid;
+}
+
 JSObject*
 WebGLContext::GetVertexAttribFloat32Array(JSContext* cx, GLuint index)
 {
     GLfloat attrib[4];
     if (index) {
         gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, attrib);
     } else {
         memcpy(attrib, mGenericVertexAttrib0Data, sizeof(mGenericVertexAttrib0Data));
@@ -54,27 +76,23 @@ WebGLContext::GetVertexAttribUint32Array
         memcpy(attrib, mGenericVertexAttrib0Data, sizeof(mGenericVertexAttrib0Data));
     }
     return dom::Uint32Array::Create(cx, this, 4, attrib);
 }
 
 ////////////////////////////////////////
 
 void
-WebGLContext::VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w,
-                             const char* funcName)
+WebGLContext::VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
 {
-    if (!funcName) {
-        funcName = "vertexAttrib4f";
-    }
-
+    const FuncScope funcScope(*this, "vertexAttrib4f");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, funcName))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     ////
 
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttrib4f(index, x, y, z, w);
     }
 
@@ -85,27 +103,23 @@ WebGLContext::VertexAttrib4f(GLuint inde
 
     if (!index) {
         const float data[4] = { x, y, z, w };
         memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
-WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w,
-                               const char* funcName)
+WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
 {
-    if (!funcName) {
-        funcName = "vertexAttribI4i";
-    }
-
+    const FuncScope funcScope(*this, "vertexAttribI4i");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, funcName))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     ////
 
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttribI4i(index, x, y, z, w);
     }
 
@@ -116,27 +130,23 @@ WebGL2Context::VertexAttribI4i(GLuint in
 
     if (!index) {
         const int32_t data[4] = { x, y, z, w };
         memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
     }
 }
 
 void
-WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w,
-                                const char* funcName)
+WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
 {
-    if (!funcName) {
-        funcName = "vertexAttribI4ui";
-    }
-
+    const FuncScope funcScope(*this, "vertexAttribI4ui");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, funcName))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     ////
 
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fVertexAttribI4ui(index, x, y, z, w);
     }
 
@@ -151,56 +161,58 @@ WebGL2Context::VertexAttribI4ui(GLuint i
     }
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::EnableVertexAttribArray(GLuint index)
 {
+    const FuncScope funcScope(*this, "enableVertexAttribArray");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     gl->fEnableVertexAttribArray(index);
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->mAttribs[index].mEnabled = true;
     mBoundVertexArray->InvalidateCaches();
 }
 
 void
 WebGLContext::DisableVertexAttribArray(GLuint index)
 {
+    const FuncScope funcScope(*this, "disableVertexAttribArray");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     if (index || !gl->IsCompatibilityProfile()) {
         gl->fDisableVertexAttribArray(index);
     }
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->mAttribs[index].mEnabled = false;
     mBoundVertexArray->InvalidateCaches();
 }
 
 JS::Value
 WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv)
 {
-    const char funcName[] = "getVertexAttrib";
+    const FuncScope funcScope(*this, "getVertexAttrib");
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateAttribIndex(index, funcName))
+    if (!ValidateAttribIndex(*this, index))
         return JS::NullValue();
 
     MOZ_ASSERT(mBoundVertexArray);
 
     switch (pname) {
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
         return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv);
 
@@ -256,66 +268,67 @@ WebGLContext::GetVertexAttrib(JSContext*
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
         return JS::BooleanValue(mBoundVertexArray->mAttribs[index].Normalized());
 
     default:
         break;
     }
 
-    ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
+    ErrorInvalidEnumInfo("pname", pname);
     return JS::NullValue();
 }
 
 WebGLsizeiptr
 WebGLContext::GetVertexAttribOffset(GLuint index, GLenum pname)
 {
+    const FuncScope funcScope(*this, "getVertexAttribOffset");
     if (IsContextLost())
         return 0;
 
-    if (!ValidateAttribIndex(index, "getVertexAttribOffset"))
+    if (!ValidateAttribIndex(*this, index))
         return 0;
 
     if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
-        ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
+        ErrorInvalidEnum("`pname` must be VERTEX_ATTRIB_ARRAY_POINTER.");
         return 0;
     }
 
     MOZ_ASSERT(mBoundVertexArray);
     return mBoundVertexArray->mAttribs[index].ByteOffset();
 }
 
 ////////////////////////////////////////
 
 void
-WebGLContext::VertexAttribAnyPointer(const char* funcName, bool isFuncInt, GLuint index,
+WebGLContext::VertexAttribAnyPointer(bool isFuncInt, GLuint index,
                                      GLint size, GLenum type, bool normalized,
                                      GLsizei stride, WebGLintptr byteOffset)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, funcName))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     ////
 
     if (size < 1 || size > 4) {
-        ErrorInvalidValue("%s: invalid element size", funcName);
+        ErrorInvalidValue("Invalid element size.");
         return;
     }
 
     // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
     if (stride < 0 || stride > 255) {
-        ErrorInvalidValue("%s: negative or too large stride", funcName);
+        ErrorInvalidValue("Negative or too large stride.");
         return;
     }
 
     if (byteOffset < 0) {
-        ErrorInvalidValue("%s: negative offset", funcName);
+        ErrorInvalidValue("Negative offset.");
         return;
     }
 
     ////
 
     bool isTypeValid = true;
     uint8_t typeAlignment;
     switch (type) {
@@ -362,52 +375,50 @@ WebGLContext::VertexAttribAnyPointer(con
 
     case LOCAL_GL_INT_2_10_10_10_REV:
     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
         if (isFuncInt || !IsWebGL2()) {
             isTypeValid = false;
             break;
         }
         if (size != 4) {
-            ErrorInvalidOperation("%s: size must be 4 for this type.", funcName);
+            ErrorInvalidOperation("Size must be 4 for this type.");
             return;
         }
         typeAlignment = 4;
         break;
 
     default:
         isTypeValid = false;
         break;
     }
     if (!isTypeValid) {
-        ErrorInvalidEnumArg(funcName, "type", type);
+        ErrorInvalidEnumInfo("type", type);
         return;
     }
 
     ////
 
     // `alignment` should always be a power of two.
     MOZ_ASSERT(IsPowerOfTwo(typeAlignment));
     const GLsizei typeAlignmentMask = typeAlignment - 1;
 
     if (stride & typeAlignmentMask ||
         byteOffset & typeAlignmentMask)
     {
-        ErrorInvalidOperation("%s: `stride` and `byteOffset` must satisfy the alignment"
-                              " requirement of `type`.",
-                              funcName);
+        ErrorInvalidOperation("`stride` and `byteOffset` must satisfy the alignment"
+                              " requirement of `type`.");
         return;
     }
 
     ////
 
     const auto& buffer = mBoundArrayBuffer;
     if (!buffer && byteOffset) {
-        ErrorInvalidOperation("%s: If ARRAY_BUFFER is null, byteOffset must be zero.",
-                              funcName);
+        ErrorInvalidOperation("If ARRAY_BUFFER is null, byteOffset must be zero.");
         return;
     }
 
     ////
 
     if (isFuncInt) {
         gl->fVertexAttribIPointer(index, size, type, stride,
                                   reinterpret_cast<void*>(byteOffset));
@@ -421,20 +432,21 @@ WebGLContext::VertexAttribAnyPointer(con
     mBoundVertexArray->InvalidateCaches();
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
 {
+    const FuncScope funcScope(*this, "vertexAttribDivisor");
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
+    if (!ValidateAttribIndex(*this, index))
         return;
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->mAttribs[index].mDivisor = divisor;
     mBoundVertexArray->InvalidateCaches();
 
     gl->fVertexAttribDivisor(index, divisor);
 }
--- a/dom/canvas/WebGLExtensionDebugShaders.cpp
+++ b/dom/canvas/WebGLExtensionDebugShaders.cpp
@@ -23,27 +23,23 @@ WebGLExtensionDebugShaders::~WebGLExtens
 // If no source has been defined, compileShader() has not been called, or the
 // translation has failed for shader, an empty string is returned; otherwise,
 // return the translated source.
 void
 WebGLExtensionDebugShaders::GetTranslatedShaderSource(const WebGLShader& shader,
                                                       nsAString& retval) const
 {
     retval.SetIsVoid(true);
-
-    if (mIsLost) {
-        mContext->ErrorInvalidOperation("%s: Extension is lost.",
-                                        "getTranslatedShaderSource");
-        return;
-    }
-
-    if (mContext->IsContextLost())
+    if (mIsLost)
         return;
 
-    if (!mContext->ValidateObject("getShaderTranslatedSource: shader", shader))
+    const WebGLContext::FuncScope funcScope(*mContext, "getShaderTranslatedSource");
+    MOZ_ASSERT(!mContext->IsContextLost());
+
+    if (!mContext->ValidateObject("shader", shader))
         return;
 
     shader.GetShaderTranslatedSource(&retval);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugShaders, WEBGL_debug_shaders)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -24,99 +24,107 @@ WebGLExtensionDisjointTimerQuery::WebGLE
 
 WebGLExtensionDisjointTimerQuery::~WebGLExtensionDisjointTimerQuery()
 {
 }
 
 already_AddRefed<WebGLQuery>
 WebGLExtensionDisjointTimerQuery::CreateQueryEXT() const
 {
-    const char funcName[] = "createQueryEXT";
     if (mIsLost)
         return nullptr;
+    const WebGLContext::FuncScope funcScope(*mContext, "createQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    return mContext->CreateQuery(funcName);
+    return mContext->CreateQuery();
 }
 
 void
 WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLQuery* query) const
 {
-    const char funcName[] = "deleteQueryEXT";
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "deleteQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    mContext->DeleteQuery(query, funcName);
+    mContext->DeleteQuery(query);
 }
 
 bool
 WebGLExtensionDisjointTimerQuery::IsQueryEXT(const WebGLQuery* query) const
 {
-    const char funcName[] = "isQueryEXT";
     if (mIsLost)
         return false;
+    const WebGLContext::FuncScope funcScope(*mContext, "isQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    return mContext->IsQuery(query, funcName);
+    return mContext->IsQuery(query);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery& query) const
 {
-    const char funcName[] = "beginQueryEXT";
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "beginQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    mContext->BeginQuery(target, query, funcName);
+    mContext->BeginQuery(target, query);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target) const
 {
-    const char funcName[] = "endQueryEXT";
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "endQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    mContext->EndQuery(target, funcName);
+    mContext->EndQuery(target);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery& query, GLenum target) const
 {
-    const char funcName[] = "queryCounterEXT";
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "queryCounterEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
+
+    if (!mContext->ValidateObject("query", query))
+        return;
 
-    if (!mContext->ValidateObject(funcName, query))
-        return;
-
-    query.QueryCounter(funcName, target);
+    query.QueryCounter(target);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
                                               JS::MutableHandleValue retval) const
 {
-    const char funcName[] = "getQueryEXT";
     retval.setNull();
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "getQueryEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    mContext->GetQuery(cx, target, pname, retval, funcName);
+    mContext->GetQuery(cx, target, pname, retval);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
                                                     const WebGLQuery& query, GLenum pname,
                                                     JS::MutableHandleValue retval) const
 {
-    const char funcName[] = "getQueryObjectEXT";
     retval.setNull();
     if (mIsLost)
         return;
+    const WebGLContext::FuncScope funcScope(*mContext, "getQueryObjectEXT");
+    MOZ_ASSERT(!mContext->IsContextLost());
 
-    mContext->GetQueryParameter(cx, query, pname, retval, funcName);
+    mContext->GetQueryParameter(cx, query, pname, retval);
 }
 
 bool
 WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
 {
     gl::GLContext* gl = webgl->GL();
     return gl->IsSupported(gl::GLFeature::query_objects) &&
            gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
--- a/dom/canvas/WebGLExtensionMOZDebug.cpp
+++ b/dom/canvas/WebGLExtensionMOZDebug.cpp
@@ -21,16 +21,21 @@ WebGLExtensionMOZDebug::~WebGLExtensionM
 {
 }
 
 void
 WebGLExtensionMOZDebug::GetParameter(JSContext* cx, GLenum pname,
                                      JS::MutableHandle<JS::Value> retval,
                                      ErrorResult& er) const
 {
+    if (mIsLost)
+        return;
+    const WebGLContext::FuncScope funcScope(*mContext, "MOZ_debug.getParameter");
+    MOZ_ASSERT(!mContext->IsContextLost());
+
     const auto& gl = mContext->gl;
 
     switch (pname) {
     case LOCAL_GL_EXTENSIONS:
         {
             nsString ret;
             if (!gl->IsCoreProfile()) {
                 const auto rawExts = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
@@ -67,17 +72,17 @@ WebGLExtensionMOZDebug::GetParameter(JSC
             return;
         }
 
     case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION:
         retval.set(JS::BooleanValue(mContext->mNeedsIndexValidation));
         return;
 
     default:
-        mContext->ErrorInvalidEnumArg("MOZ_debug.getParameter", "pname", pname);
+        mContext->ErrorInvalidEnumInfo("pname", pname);
         retval.set(JS::NullValue());
         return;
     }
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionMOZDebug, MOZ_debug)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -42,18 +42,17 @@ WebGLFBAttachPoint::~WebGLFBAttachPoint(
     MOZ_ASSERT(mFB, "Should have been Init'd.");
     MOZ_ASSERT(!mRenderbufferPtr);
     MOZ_ASSERT(!mTexturePtr);
 }
 
 void
 WebGLFBAttachPoint::Unlink()
 {
-    const char funcName[] = "WebGLFramebuffer::GC";
-    Clear(funcName);
+    Clear();
 }
 
 bool
 WebGLFBAttachPoint::IsDeleteRequested() const
 {
     return Texture() ? Texture()->IsDeleteRequested()
          : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
          : false;
@@ -110,51 +109,51 @@ WebGLFBAttachPoint::IsReadableFloat() co
     auto format = formatUsage->format;
     if (!format->IsColorFormat())
         return false;
 
     return format->componentType == webgl::ComponentType::Float;
 }
 
 void
-WebGLFBAttachPoint::Clear(const char* funcName)
+WebGLFBAttachPoint::Clear()
 {
     if (mRenderbufferPtr) {
         MOZ_ASSERT(!mTexturePtr);
         mRenderbufferPtr->UnmarkAttachment(*this);
     } else if (mTexturePtr) {
         mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).RemoveAttachPoint(this);
     }
 
     mTexturePtr = nullptr;
     mRenderbufferPtr = nullptr;
 
-    OnBackingStoreRespecified(funcName);
+    OnBackingStoreRespecified();
 }
 
 void
-WebGLFBAttachPoint::SetTexImage(const char* funcName, WebGLTexture* tex,
+WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex,
                                 TexImageTarget target, GLint level, GLint layer)
 {
-    Clear(funcName);
+    Clear();
 
     mTexturePtr = tex;
     mTexImageTarget = target;
     mTexImageLevel = level;
     mTexImageLayer = layer;
 
     if (mTexturePtr) {
         mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).AddAttachPoint(this);
     }
 }
 
 void
-WebGLFBAttachPoint::SetRenderbuffer(const char* funcName, WebGLRenderbuffer* rb)
+WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb)
 {
-    Clear(funcName);
+    Clear();
 
     mRenderbufferPtr = rb;
 
     if (mRenderbufferPtr) {
         mRenderbufferPtr->MarkAttachment(*this);
     }
 }
 
@@ -222,19 +221,19 @@ WebGLFBAttachPoint::Size(uint32_t* const
     MOZ_ASSERT(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined());
     const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
 
     *out_width = imageInfo.mWidth;
     *out_height = imageInfo.mHeight;
 }
 
 void
-WebGLFBAttachPoint::OnBackingStoreRespecified(const char* funcName) const
+WebGLFBAttachPoint::OnBackingStoreRespecified() const
 {
-    mFB->InvalidateFramebufferStatus(funcName);
+    mFB->InvalidateFramebufferStatus();
 }
 
 void
 WebGLFBAttachPoint::AttachmentName(nsCString* out) const
 {
     switch (mAttachmentPoint) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
         out->AssignLiteral("DEPTH_ATTACHMENT");
@@ -397,17 +396,17 @@ WebGLFBAttachPoint::Resolve(gl::GLContex
         // DEPTH_STENCIL_ATTACHMENT.
         gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, mAttachmentPoint, texName,
                                      mTexImageLevel, mTexImageLayer);
         break;
     }
 }
 
 JS::Value
-WebGLFBAttachPoint::GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx,
+WebGLFBAttachPoint::GetParameter(WebGLContext* webgl, JSContext* cx,
                                  GLenum target, GLenum attachment, GLenum pname,
                                  ErrorResult* const out_error) const
 {
     const bool hasAttachment = (mTexturePtr || mRenderbufferPtr);
     if (!hasAttachment) {
         // Divergent between GLES 3 and 2.
 
         // GLES 2.0.25 p127:
@@ -430,20 +429,20 @@ WebGLFBAttachPoint::GetParameter(const c
             break;
 
         default:
             break;
         }
         nsCString attachmentName;
         WebGLContext::EnumName(attachment, &attachmentName);
         if (webgl->IsWebGL2()) {
-            webgl->ErrorInvalidOperation("%s: No attachment at %s.", funcName,
+            webgl->ErrorInvalidOperation("No attachment at %s.",
                                          attachmentName.BeginReading());
         } else {
-            webgl->ErrorInvalidEnum("%s: No attachment at %s.", funcName,
+            webgl->ErrorInvalidEnum("No attachment at %s.",
                                     attachmentName.BeginReading());
         }
         return JS::NullValue();
     }
 
     bool isPNameValid = false;
     switch (pname) {
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
@@ -501,17 +500,17 @@ WebGLFBAttachPoint::GetParameter(const c
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
         isPNameValid = (webgl->IsWebGL2() ||
                         webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB));
         break;
     }
 
     if (!isPNameValid) {
-        webgl->ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
+        webgl->ErrorInvalidEnum("Invalid pname: 0x%04x", pname);
         return JS::NullValue();
     }
 
     const auto usage = Format();
     if (!usage) {
         if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
             return JS::NumberValue(LOCAL_GL_LINEAR);
 
@@ -638,26 +637,24 @@ WebGLFramebuffer::WebGLFramebuffer(WebGL
 
     mColorDrawBuffers.push_back(&mColorAttachments[0]);
     mColorReadBuffer = &mColorAttachments[0];
 }
 
 void
 WebGLFramebuffer::Delete()
 {
-    const char funcName[] = "WebGLFramebuffer::Delete";
-
-    InvalidateFramebufferStatus(funcName);
+    InvalidateFramebufferStatus();
 
-    mDepthAttachment.Clear(funcName);
-    mStencilAttachment.Clear(funcName);
-    mDepthStencilAttachment.Clear(funcName);
+    mDepthAttachment.Clear();
+    mStencilAttachment.Clear();
+    mDepthStencilAttachment.Clear();
 
     for (auto& cur : mColorAttachments) {
-        cur.Clear(funcName);
+        cur.Clear();
     }
 
     mContext->gl->fDeleteFramebuffers(1, &mGLName);
 
     LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
 
 #ifdef ANDROID
     mIsFB = false;
@@ -707,33 +704,33 @@ WebGLFramebuffer::GetAttachPoint(GLenum 
     X(mStencilAttachment);                \
     X(mDepthStencilAttachment);           \
                                           \
     for (auto& cur : mColorAttachments) { \
         X(cur);                           \
     }
 
 void
-WebGLFramebuffer::DetachTexture(const char* funcName, const WebGLTexture* tex)
+WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
 {
     const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
         if (attach.Texture() == tex) {
-            attach.Clear(funcName);
+            attach.Clear();
         }
     };
 
     FOR_EACH_ATTACHMENT(fnDetach)
 }
 
 void
-WebGLFramebuffer::DetachRenderbuffer(const char* funcName, const WebGLRenderbuffer* rb)
+WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
 {
     const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
         if (attach.Renderbuffer() == rb) {
-            attach.Clear(funcName);
+            attach.Clear();
         }
     };
 
     FOR_EACH_ATTACHMENT(fnDetach)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Completeness
@@ -884,33 +881,31 @@ WebGLFramebuffer::PrecheckFramebufferSta
 
     return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 }
 
 ////////////////////////////////////////
 // Validation
 
 bool
-WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName) const
+WebGLFramebuffer::ValidateAndInitAttachments() const
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
-    const auto fbStatus = CheckFramebufferStatus(funcName);
+    const auto fbStatus = CheckFramebufferStatus();
     if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
         return true;
 
-    mContext->ErrorInvalidFramebufferOperation("%s: Framebuffer must be"
-                                               " complete.",
-                                               funcName);
+    mContext->ErrorInvalidFramebufferOperation("Framebuffer must be complete.");
     return false;
 }
 
 bool
-WebGLFramebuffer::ValidateClearBufferType(const char* funcName, GLenum buffer,
+WebGLFramebuffer::ValidateClearBufferType(GLenum buffer,
                                           uint32_t drawBuffer, GLenum funcType) const
 {
     if (buffer != LOCAL_GL_COLOR)
         return true;
 
     const auto& attach = mColorAttachments[drawBuffer];
     if (!attach.IsDefined())
         return true;
@@ -927,45 +922,42 @@ WebGLFramebuffer::ValidateClearBufferTyp
         attachType = LOCAL_GL_UNSIGNED_INT;
         break;
     default:
         attachType = LOCAL_GL_FLOAT;
         break;
     }
 
     if (attachType != funcType) {
-        mContext->ErrorInvalidOperation("%s: This attachment is of type 0x%04x, but"
+        mContext->ErrorInvalidOperation("This attachment is of type 0x%04x, but"
                                         " this function is of type 0x%04x.",
-                                        funcName, attachType, funcType);
+                                        attachType, funcType);
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLFramebuffer::ValidateForColorRead(const char* funcName,
-                                       const webgl::FormatUsageInfo** const out_format,
+WebGLFramebuffer::ValidateForColorRead(const webgl::FormatUsageInfo** const out_format,
                                        uint32_t* const out_width,
                                        uint32_t* const out_height) const
 {
     if (!mColorReadBuffer) {
-        mContext->ErrorInvalidOperation("%s: READ_BUFFER must not be NONE.", funcName);
+        mContext->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
         return false;
     }
 
     if (!mColorReadBuffer->IsDefined()) {
-        mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is not defined.",
-                                        funcName);
+        mContext->ErrorInvalidOperation("The READ_BUFFER attachment is not defined.");
         return false;
     }
 
     if (mColorReadBuffer->Samples()) {
-        mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is multisampled.",
-                                        funcName);
+        mContext->ErrorInvalidOperation("The READ_BUFFER attachment is multisampled.");
         return false;
     }
 
     *out_format = mColorReadBuffer->Format();
     mColorReadBuffer->Size(out_width, out_height);
     return true;
 }
 
@@ -998,17 +990,17 @@ WebGLFramebuffer::ResolveAttachments() c
     }
 
     mDepthAttachment.Resolve(gl);
     mStencilAttachment.Resolve(gl);
     mDepthStencilAttachment.Resolve(gl);
 }
 
 bool
-WebGLFramebuffer::ResolveAttachmentData(const char* funcName) const
+WebGLFramebuffer::ResolveAttachmentData() const
 {
     //////
     // Check if we need to initialize anything
 
     const auto fnIs3D = [&](const WebGLFBAttachPoint& attach) {
         const auto& tex = attach.Texture();
         if (!tex)
             return false;
@@ -1051,17 +1043,17 @@ WebGLFramebuffer::ResolveAttachmentData(
     fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
     fnGather(mDepthStencilAttachment, LOCAL_GL_DEPTH_BUFFER_BIT |
                                       LOCAL_GL_STENCIL_BUFFER_BIT);
 
     //////
 
     for (const auto& attach : tex3DAttachmentsToInit) {
         const auto& tex = attach->Texture();
-        if (!tex->InitializeImageData(funcName, attach->ImageTarget(),
+        if (!tex->InitializeImageData(attach->ImageTarget(),
                                       attach->MipLevel()))
         {
             return false;
         }
     }
 
     if (clearBits) {
         const auto fnDrawBuffers = [&](const std::vector<const WebGLFBAttachPoint*>& src)
@@ -1152,24 +1144,24 @@ WebGLFramebuffer::ResolvedData::Resolved
         if (!fnCommon(attach))
             return;
 
         readSet.insert(WebGLFBAttachPoint::Ordered(attach));
     }
 }
 
 void
-WebGLFramebuffer::InvalidateFramebufferStatus(const char* funcName)
+WebGLFramebuffer::InvalidateFramebufferStatus()
 {
     if (mResolvedCompleteData) {
         mNumFBStatusInvals++;
         if (mNumFBStatusInvals > mContext->mMaxAcceptableFBStatusInvals) {
-            mContext->GeneratePerfWarning("%s: FB was invalidated after being complete %u"
+            mContext->GeneratePerfWarning("FB was invalidated after being complete %u"
                                           " times.",
-                                          funcName, uint32_t(mNumFBStatusInvals));
+                                          uint32_t(mNumFBStatusInvals));
         }
     }
 
     mResolvedCompleteData = nullptr;
 }
 
 void
 WebGLFramebuffer::RefreshResolvedData()
@@ -1178,17 +1170,17 @@ WebGLFramebuffer::RefreshResolvedData()
         mResolvedCompleteData.reset(new ResolvedData(*this));
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Entrypoints
 
 FBStatus
-WebGLFramebuffer::CheckFramebufferStatus(const char* const funcName) const
+WebGLFramebuffer::CheckFramebufferStatus() const
 {
     if (IsResolvedComplete())
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 
     // Ok, let's try to resolve it!
 
     nsCString statusInfo;
     FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
@@ -1214,29 +1206,29 @@ WebGLFramebuffer::CheckFramebufferStatus
 
         if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
             const nsPrintfCString text("Bad status according to the driver: 0x%04x",
                                        ret.get());
             statusInfo = text;
             break;
         }
 
-        if (!ResolveAttachmentData(funcName)) {
+        if (!ResolveAttachmentData()) {
             ret = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
             statusInfo.AssignLiteral("Failed to lazily-initialize attachment data.");
             break;
         }
 
         mResolvedCompleteData.reset(new ResolvedData(*this));
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
     } while (false);
 
     MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE);
-    mContext->GenerateWarning("%s: Framebuffer not complete. (status: 0x%04x) %s",
-                              funcName, ret.get(), statusInfo.BeginReading());
+    mContext->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
+                              ret.get(), statusInfo.BeginReading());
     return ret;
 }
 
 ////
 
 void
 WebGLFramebuffer::RefreshDrawBuffers() const
 {
@@ -1276,22 +1268,22 @@ WebGLFramebuffer::RefreshReadBuffer() co
     }
 
     gl->fReadBuffer(driverBuffer);
 }
 
 ////
 
 void
-WebGLFramebuffer::DrawBuffers(const char* funcName, const dom::Sequence<GLenum>& buffers)
+WebGLFramebuffer::DrawBuffers(const dom::Sequence<GLenum>& buffers)
 {
     if (buffers.Length() > mContext->mGLMaxDrawBuffers) {
         // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS."
-        mContext->ErrorInvalidValue("%s: `buffers` must have a length <="
-                                    " MAX_DRAW_BUFFERS.", funcName);
+        mContext->ErrorInvalidValue("`buffers` must have a length <="
+                                    " MAX_DRAW_BUFFERS.");
         return;
     }
 
     std::vector<const WebGLFBAttachPoint*> newColorDrawBuffers;
     newColorDrawBuffers.reserve(buffers.Length());
 
     for (size_t i = 0; i < buffers.Length(); i++) {
         // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
@@ -1309,317 +1301,309 @@ WebGLFramebuffer::DrawBuffers(const char
             const auto& attach = mColorAttachments[i];
             newColorDrawBuffers.push_back(&attach);
         } else if (cur != LOCAL_GL_NONE) {
             const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 &&
                                       cur < mContext->LastColorAttachmentEnum());
             if (cur != LOCAL_GL_BACK &&
                 !isColorEnum)
             {
-                mContext->ErrorInvalidEnum("%s: Unexpected enum in buffers.", funcName);
+                mContext->ErrorInvalidEnum("Unexpected enum in buffers.");
                 return;
             }
 
-            mContext->ErrorInvalidOperation("%s: `buffers[i]` must be NONE or"
-                                            " COLOR_ATTACHMENTi.",
-                                            funcName);
+            mContext->ErrorInvalidOperation("`buffers[i]` must be NONE or"
+                                            " COLOR_ATTACHMENTi.");
             return;
         }
     }
 
     ////
 
     mColorDrawBuffers.swap(newColorDrawBuffers);
     RefreshDrawBuffers(); // Calls glDrawBuffers.
     RefreshResolvedData();
 }
 
 void
-WebGLFramebuffer::ReadBuffer(const char* funcName, GLenum attachPoint)
+WebGLFramebuffer::ReadBuffer(GLenum attachPoint)
 {
     const auto& maybeAttach = GetColorAttachPoint(attachPoint);
     if (!maybeAttach) {
-        const char text[] = "%s: `mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
+        const char text[] = "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
                             " MAX_DRAW_BUFFERS.";
         if (attachPoint == LOCAL_GL_BACK) {
-            mContext->ErrorInvalidOperation(text, funcName);
+            mContext->ErrorInvalidOperation(text);
         } else {
-            mContext->ErrorInvalidEnum(text, funcName);
+            mContext->ErrorInvalidEnum(text);
         }
         return;
     }
     const auto& attach = maybeAttach.value(); // Might be nullptr.
 
     ////
 
     mColorReadBuffer = attach;
     RefreshReadBuffer(); // Calls glReadBuffer.
     RefreshResolvedData();
 }
 
 ////
 
 void
-WebGLFramebuffer::FramebufferRenderbuffer(const char* funcName, GLenum attachEnum,
+WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachEnum,
                                           GLenum rbtarget, WebGLRenderbuffer* rb)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     // `attachment`
     const auto maybeAttach = GetAttachPoint(attachEnum);
     if (!maybeAttach || !maybeAttach.value()) {
-        mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
         return;
     }
     const auto& attach = maybeAttach.value();
 
     // `rbTarget`
     if (rbtarget != LOCAL_GL_RENDERBUFFER) {
-        mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:", rbtarget);
+        mContext->ErrorInvalidEnumInfo("rbtarget", rbtarget);
         return;
     }
 
     // `rb`
     if (rb) {
-        if (!mContext->ValidateObject("framebufferRenderbuffer: rb", *rb))
+        if (!mContext->ValidateObject("rb", *rb))
             return;
 
         if (!rb->mHasBeenBound) {
-            mContext->ErrorInvalidOperation("%s: bindRenderbuffer must be called before"
+            mContext->ErrorInvalidOperation("bindRenderbuffer must be called before"
                                             " attachment to %04x",
-                                            funcName, attachEnum);
+                                            attachEnum);
             return;
       }
     }
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-        mDepthAttachment.SetRenderbuffer(funcName, rb);
-        mStencilAttachment.SetRenderbuffer(funcName, rb);
+        mDepthAttachment.SetRenderbuffer(rb);
+        mStencilAttachment.SetRenderbuffer(rb);
     } else {
-        attach->SetRenderbuffer(funcName, rb);
+        attach->SetRenderbuffer(rb);
     }
 
-    InvalidateFramebufferStatus(funcName);
+    InvalidateFramebufferStatus();
 }
 
 void
-WebGLFramebuffer::FramebufferTexture2D(const char* funcName, GLenum attachEnum,
+WebGLFramebuffer::FramebufferTexture2D(GLenum attachEnum,
                                        GLenum texImageTarget, WebGLTexture* tex,
                                        GLint level)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     // `attachment`
     const auto maybeAttach = GetAttachPoint(attachEnum);
     if (!maybeAttach || !maybeAttach.value()) {
-        mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
         return;
     }
     const auto& attach = maybeAttach.value();
 
     // `texImageTarget`
     if (texImageTarget != LOCAL_GL_TEXTURE_2D &&
         (texImageTarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
          texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
     {
-        mContext->ErrorInvalidEnumInfo("framebufferTexture2D: texImageTarget:",
+        mContext->ErrorInvalidEnumInfo("texImageTarget",
                                        texImageTarget);
         return;
     }
 
     // `texture`
     if (tex) {
-        if (!mContext->ValidateObject("framebufferTexture2D: texture", *tex))
+        if (!mContext->ValidateObject("texture", *tex))
             return;
 
         if (!tex->HasEverBeenBound()) {
-            mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
-                                            funcName);
+            mContext->ErrorInvalidOperation("`texture` has never been bound.");
             return;
         }
 
         const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
         if (tex->Target() != destTexTarget) {
-            mContext->ErrorInvalidOperation("%s: Mismatched texture and texture target.",
-                                            funcName);
+            mContext->ErrorInvalidOperation("Mismatched texture and texture target.");
             return;
         }
     }
 
     // `level`
     if (level < 0)
-        return mContext->ErrorInvalidValue("%s: `level` must not be negative.", funcName);
+        return mContext->ErrorInvalidValue("`level` must not be negative.");
 
     if (mContext->IsWebGL2()) {
         /* GLES 3.0.4 p208:
          *   If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X,
          *   TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z,
          *   TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y,
          *   or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater
          *   than or equal to zero and less than or equal to log2 of the
          *   value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D,
          *   level must be greater than or equal to zero and no larger than
          *   log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an
          *   INVALID_VALUE error is generated.
          */
 
         if (texImageTarget == LOCAL_GL_TEXTURE_2D) {
             if (uint32_t(level) > FloorLog2(mContext->mGLMaxTextureSize))
-                return mContext->ErrorInvalidValue("%s: `level` is too large.", funcName);
+                return mContext->ErrorInvalidValue("`level` is too large.");
         } else {
             MOZ_ASSERT(texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
                        texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
 
             if (uint32_t(level) > FloorLog2(mContext->mGLMaxCubeMapTextureSize))
-                return mContext->ErrorInvalidValue("%s: `level` is too large.", funcName);
+                return mContext->ErrorInvalidValue("`level` is too large.");
         }
     } else if (level != 0) {
-        return mContext->ErrorInvalidValue("%s: `level` must be 0.", funcName);
+        return mContext->ErrorInvalidValue("`level` must be 0.");
     }
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-        mDepthAttachment.SetTexImage(funcName, tex, texImageTarget, level);
-        mStencilAttachment.SetTexImage(funcName, tex, texImageTarget, level);
+        mDepthAttachment.SetTexImage(tex, texImageTarget, level);
+        mStencilAttachment.SetTexImage(tex, texImageTarget, level);
     } else {
-        attach->SetTexImage(funcName, tex, texImageTarget, level);
+        attach->SetTexImage(tex, texImageTarget, level);
     }
 
-    InvalidateFramebufferStatus(funcName);
+    InvalidateFramebufferStatus();
 }
 
 void
-WebGLFramebuffer::FramebufferTextureLayer(const char* funcName, GLenum attachEnum,
+WebGLFramebuffer::FramebufferTextureLayer(GLenum attachEnum,
                                           WebGLTexture* tex, GLint level, GLint layer)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     // `attachment`
     const auto maybeAttach = GetAttachPoint(attachEnum);
     if (!maybeAttach || !maybeAttach.value()) {
-        mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+        mContext->ErrorInvalidEnum("Bad `attachment`: 0x%x.", attachEnum);
         return;
     }
     const auto& attach = maybeAttach.value();
 
     // `level`, `layer`
     if (layer < 0)
-        return mContext->ErrorInvalidValue("%s: `layer` must be >= 0.", funcName);
+        return mContext->ErrorInvalidValue("`layer` must be >= 0.");
 
     if (level < 0)
-        return mContext->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
+        return mContext->ErrorInvalidValue("`level` must be >= 0.");