Bug 1478909 - Make funcName implicit for WebGL calls. - r=kvark
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 26 Jul 2018 21:46:33 -0700
changeset 486013 b807f63147aeb40dc4139b258d630cc1739662e0
parent 486012 423c08523aa92bcbd5587771ca1dd6720c6d2884
child 486014 e51868670f3cd7c2c029cd0843e9919a5664db78
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1478909
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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();
 }
 
@@ -372,16 +370,19 @@ IsFeatureInBlacklist(const nsCOMPtr<nsIG
 
     return status != nsIGfxInfo::FEATURE_STATUS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options,
                                 ErrorResult& aRvForDictionaryInit)
 {
+    const FuncScope funcScope(*this, "getContext");
+    (void)IsContextLost(); // Ignore this.
+
     if (options.isNullOrUndefined() && mOptionsFrozen)
         return NS_OK;
 
     WebGLContextAttributes attributes;
     if (!attributes.Init(cx, options)) {
       aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
       return NS_ERROR_UNEXPECTED;
     }
@@ -519,17 +520,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 +657,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 +694,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 +743,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 +922,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 +1040,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 +1230,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 +1255,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 +1316,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 +1460,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 +1532,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 +1675,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 +1693,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 +1720,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 +1752,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 +1796,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 +1883,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 +1924,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 +2296,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 +2350,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 +2379,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 +2425,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,78 @@ 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)
 {
+    const FuncScope funcScope(*this, "drawArraysInstanced");
     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 +584,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 +629,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 +655,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 +754,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 +806,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 +852,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 +888,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 +964,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);