r?jrmuizel - Record context creation failure reason. draft
authorJeff Gilbert <jdashg@gmail.com>
Fri, 08 Apr 2016 15:11:05 -0700
changeset 365992 81bc18932d05fbb7e46ef71de1e6058e03bfed25
parent 365991 37c71056817a63e31deb6eaf6d28f20cf60b38c7
child 365993 1fb5a5295c0295d486d51d9aa419608c14ef7ab5
push id17876
push userjgilbert@mozilla.com
push dateWed, 11 May 2016 19:35:30 +0000
milestone49.0a1
r?jrmuizel - Record context creation failure reason. From 7a6961b658c5d0affce6b69d3259e725220df7f0 Mon Sep 17 00:00:00 2001 --- dom/canvas/WebGL2Context.cpp | 17 ++++-- dom/canvas/WebGLContext.cpp | 109 ++++++++++++++++++++++++++---------- dom/canvas/WebGLContext.h | 13 +++-- dom/canvas/WebGLContextValidate.cpp | 35 +++++++----- 4 files changed, 119 insertions(+), 55 deletions(-) MozReview-Commit-ID: F4Uo5EUIPRf
dom/canvas/WebGL2Context.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextValidate.cpp
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -92,53 +92,58 @@ static const gl::GLFeature kRequiredFeat
     gl::GLFeature::texture_storage,
     gl::GLFeature::transform_feedback2,
     gl::GLFeature::uniform_buffer_object,
     gl::GLFeature::uniform_matrix_nonsquare,
     gl::GLFeature::vertex_array_object
 };
 
 bool
-WebGLContext::InitWebGL2()
+WebGLContext::InitWebGL2(nsACString* const out_failReason)
 {
     MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!");
 
     // check OpenGL features
     if (!gl->IsSupported(gl::GLFeature::occlusion_query) &&
         !gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
     {
         // On desktop, we fake occlusion_query_boolean with occlusion_query if
         // necessary. (See WebGL2ContextQueries.cpp)
-        GenerateWarning("WebGL 2 unavailable. Requires occlusion queries.");
+        out_failReason->AssignASCII("WebGL 2 requires occlusion query support.");
         return false;
     }
 
     std::vector<gl::GLFeature> missingList;
 
     for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
-        if (!gl->IsSupported(kRequiredFeatures[i]))
+        if (!gl->IsSupported(kRequiredFeatures[i])) {
             missingList.push_back(kRequiredFeatures[i]);
+        }
     }
 
 #ifdef XP_MACOSX
     // On OSX, GL core profile is used. This requires texture swizzle
     // support to emulate legacy texture formats: ALPHA, LUMINANCE,
     // and LUMINANCE_ALPHA.
-    if (!gl->IsSupported(gl::GLFeature::texture_swizzle))
+    if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
         missingList.push_back(gl::GLFeature::texture_swizzle);
+    }
 #endif
 
     if (missingList.size()) {
         nsAutoCString exts;
         for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
             exts.AppendLiteral("\n  ");
             exts.Append(gl::GLContext::GetFeatureName(*itr));
         }
-        GenerateWarning("WebGL 2 unavailable. The following required features are"
-                        " unavailible: %s", exts.BeginReading());
+
+        const nsPrintfCString reason("WebGL 2 requires support for the following"
+                                     " features: %s",
+                                     exts.BeginReading());
+        out_failReason->Assign(reason);
         return false;
     }
 
     // we initialise WebGL 2 related stuff.
     gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
                      &mGLMaxTransformFeedbackSeparateAttribs);
     gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
                      &mGLMaxUniformBufferBindings);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -21,16 +21,17 @@
 #include "ImageContainer.h"
 #include "ImageEncoder.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/WebGLContextEvent.h"
 #include "mozilla/EnumeratedArrayCycleCollection.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessPriorityManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsError.h"
@@ -571,117 +572,128 @@ BaseCaps(const WebGLContextOptions& opti
 
     return baseCaps;
 }
 
 ////////////////////////////////////////
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                WebGLContext* webgl)
+                WebGLContext* webgl, nsACString* const out_failReason)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
                                                                      flags);
-
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        webgl->GenerateWarning("Error during EGL OpenGL init.");
+        if (out_failReason->Length()) {
+            out_failReason->AppendLiteral("\n");
+        }
+        out_failReason->AppendLiteral("Error during EGL OpenGL init.");
         return nullptr;
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                  WebGLContext* webgl)
+                  WebGLContext* webgl, nsACString* const out_failReason)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
                                                                      flags);
-
     if (gl && !gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        webgl->GenerateWarning("Error during ANGLE OpenGL init.");
+        if (out_failReason->Length()) {
+            out_failReason->AppendLiteral("\n");
+        }
+        out_failReason->AppendLiteral("Error during ANGLE OpenGL init.");
         return nullptr;
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                    WebGLContext* webgl)
+                    WebGLContext* webgl, nsACString* const out_failReason)
 {
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
 
     if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
         IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
     {
-        webgl->GenerateWarning("Refused to create native OpenGL context because of"
-                               " blacklisting.");
+        if (out_failReason->Length()) {
+            out_failReason->AppendASCII("\n");
+        }
+        out_failReason->AppendASCII("Refused to create native OpenGL context because of"
+                                    " blacklisting.");
         return nullptr;
     }
 
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps, flags);
 
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        webgl->GenerateWarning("Error during native OpenGL init.");
+        if (out_failReason->Length()) {
+            out_failReason->AppendASCII("\n");
+        }
+        out_failReason->AppendASCII("Error during native OpenGL init.");
         return nullptr;
     }
 
     return gl.forget();
 }
 
 ////////////////////////////////////////
 
 bool
 WebGLContext::CreateAndInitGLWith(FnCreateGL_T fnCreateGL,
                                   const gl::SurfaceCaps& baseCaps,
-                                  gl::CreateContextFlags flags)
+                                  gl::CreateContextFlags flags,
+                                  nsACString* const out_failReason)
 {
     std::queue<gl::SurfaceCaps> fallbackCaps;
     PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
 
     MOZ_RELEASE_ASSERT(!gl);
     gl = nullptr;
     while (!fallbackCaps.empty()) {
         gl::SurfaceCaps& caps = fallbackCaps.front();
 
-        gl = fnCreateGL(caps, flags, this);
+        gl = fnCreateGL(caps, flags, this, out_failReason);
         if (gl)
             break;
 
         fallbackCaps.pop();
     }
     if (!gl)
         return false;
 
-    if (!InitAndValidateGL()) {
+    if (!InitAndValidateGL(out_failReason)) {
         gl = nullptr;
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLContext::CreateAndInitGL(bool forceEnabled)
+WebGLContext::CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason)
 {
     const bool useEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
 
     bool useANGLE = false;
 #ifdef XP_WIN
     const bool disableANGLE = (gfxPrefs::WebGLDisableANGLE() ||
                                PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"));
     useANGLE = !disableANGLE;
@@ -690,22 +702,22 @@ WebGLContext::CreateAndInitGL(bool force
     gl::CreateContextFlags flags = gl::CreateContextFlags::NONE;
     if (forceEnabled) flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
     if (!IsWebGL2())  flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
     if (IsWebGL2())   flags |= gl::CreateContextFlags::PREFER_ES3;
 
     const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
 
     if (useEGL)
-        return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags);
+        return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags, out_failReason);
 
     if (useANGLE)
-        return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags);
+        return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReason);
 
-    return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags);
+    return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReason);
 }
 
 // Fallback for resizes:
 bool
 WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
                                uint32_t requestedHeight)
 {
     uint32_t width = requestedWidth;
@@ -740,16 +752,43 @@ WebGLContext::ResizeBackbuffer(uint32_t 
         GenerateWarning("Requested size %dx%d was too large, but resize"
                           " to %dx%d succeeded.",
                         requestedWidth, requestedHeight,
                         width, height);
     }
     return true;
 }
 
+void
+WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
+{
+    RefPtr<EventTarget> target = mCanvasElement;
+    if (!target) {
+        target = mOffscreenCanvas;
+    }
+
+    const auto kEventName = NS_LITERAL_STRING("webglcontextcreationerror");
+
+    WebGLContextEventInit eventInit;
+    // eventInit.mCancelable = true; // The spec says this, but it's silly.
+    eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text);
+
+    const RefPtr<WebGLContextEvent> event = WebGLContextEvent::Constructor(target,
+                                                                           kEventName,
+                                                                           eventInit);
+    event->SetTrusted(true);
+
+    bool didPreventDefault;
+    target->DispatchEvent(event, &didPreventDefault);
+
+    //////
+
+    GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
+}
+
 NS_IMETHODIMP
 WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
 {
     if (signedWidth < 0 || signedHeight < 0) {
         GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
@@ -819,63 +858,71 @@ WebGLContext::SetDimensions(int32_t sign
     // generation is not 0 right now (that is, if this isn't the first
     // context we're creating), we may have to dispatch a context lost
     // event.
 
     // If incrementing the generation would cause overflow,
     // don't allow it.  Allowing this would allow us to use
     // resource handles created from older context generations.
     if (!(mGeneration + 1).isValid()) {
-        GenerateWarning("Too many WebGL contexts created this run.");
-        return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
+        // exit without changing the value of mGeneration
+        const nsLiteralCString text("Too many WebGL contexts created this run.");
+        ThrowEvent_WebGLContextCreationError(text);
+        return NS_ERROR_FAILURE;
     }
 
     // increment the generation number - Do this early because later
     // in CreateOffscreenGL(), "default" objects are created that will
     // pick up the old generation.
     ++mGeneration;
 
     bool disabled = gfxPrefs::WebGLDisabled();
 
     // TODO: When we have software webgl support we should use that instead.
     disabled |= gfxPlatform::InSafeMode();
 
     if (disabled) {
-        GenerateWarning("WebGL creation is disabled, and so disallowed here.");
+        const nsLiteralCString text("WebGL is currently disabled.");
+        ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
     bool failIfPerfCaveat = mOptions.failIfMajorPerformanceCaveat;
     if (gfxPrefs::WebGLDisableFailIfMajorPerformanceCaveat())
         failIfPerfCaveat = false;
 
     if (failIfPerfCaveat) {
         nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
         if (!HasAcceleratedLayers(gfxInfo)) {
-            GenerateWarning("failIfMajorPerformanceCaveat: Compositor is not"
-                            " hardware-accelerated.");
+            const nsLiteralCString text("failIfMajorPerformanceCaveat: Compositor is not"
+                                        " hardware-accelerated.");
+            ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
     }
 
     // Alright, now let's start trying.
     bool forceEnabled = gfxPrefs::WebGLForceEnabled();
     ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
 
     MOZ_ASSERT(!gl);
-    if (!CreateAndInitGL(forceEnabled)) {
-        GenerateWarning("WebGL creation failed.");
+    nsCString failReason;
+    if (!CreateAndInitGL(forceEnabled, &failReason)) {
+        const nsPrintfCString text("WebGL creation failed: %s",
+                                   failReason.BeginReading());
+        ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
 
     MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
 
     if (!ResizeBackbuffer(width, height)) {
-        GenerateWarning("Initializing WebGL backbuffer failed.");
+        const nsLiteralCString text("Initializing WebGL backbuffer failed.");
+        ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
 #ifdef DEBUG
     if (gl->DebugMode())
         printf_stderr("--- WebGL context created: %p\n", gl.get());
 #endif
 
@@ -1602,31 +1649,33 @@ WebGLContext::UpdateContextLossStatus()
         }
 
         // Fall through.
     }
 
     if (mContextStatus == ContextLostAwaitingEvent) {
         // 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 bool kCanBubble = true;
+        const bool kIsCancelable = true;
         bool useDefaultHandler;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
                 static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
-                NS_LITERAL_STRING("webglcontextlost"),
-                true,
-                true,
+                kEventName,
+                kCanBubble,
+                kIsCancelable,
                 &useDefaultHandler);
         } else {
             // OffscreenCanvas case
             RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
-            event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
+            event->InitEvent(kEventName, kCanBubble, kIsCancelable);
             event->SetTrusted(true);
             mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
         }
 
         // We sent the callback, so we're just 'regular lost' now.
         mContextStatus = ContextLost;
         // 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
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1196,31 +1196,34 @@ protected:
     nsTArray<GLenum> mCompressedTextureFormats;
 
     // -------------------------------------------------------------------------
     // WebGL 2 specifics (implemented in WebGL2Context.cpp)
 public:
     virtual bool IsWebGL2() const = 0;
 
 protected:
-    bool InitWebGL2();
+    bool InitWebGL2(nsACString* const out_failReason);
 
-    bool CreateAndInitGL(bool forceEnabled);
+    bool CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason);
     bool ResizeBackbuffer(uint32_t width, uint32_t height);
 
     typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
                                                          gl::CreateContextFlags flags,
-                                                         WebGLContext* webgl);
+                                                         WebGLContext* webgl,
+                                                         nsACString* const out_failReason);
 
     bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
-                             gl::CreateContextFlags flags);
+                             gl::CreateContextFlags flags,
+                             nsACString* const out_failReason);
+    void ThrowEvent_WebGLContextCreationError(const nsACString& text);
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
-    bool InitAndValidateGL();
+    bool InitAndValidateGL(nsACString* const out_failReason);
     bool ValidateBlendEquationEnum(GLenum cap, const char* info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
                                              const char* info);
     bool ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info);
     bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
     bool ValidateTextureTargetEnum(GLenum target, const char* info);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -640,32 +640,35 @@ FloorPOT(int32_t x)
         if (x < pot*2)
             break;
         pot *= 2;
     }
     return pot;
 }
 
 bool
-WebGLContext::InitAndValidateGL()
+WebGLContext::InitAndValidateGL(nsACString* const out_failReason)
 {
-    if (!gl)
-        return false;
+    MOZ_RELEASE_ASSERT(gl);
 
     // 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);
-    if (!mFormatUsage)
+    if (!mFormatUsage) {
+        out_failReason->AssignLiteral("Failed to create mFormatUsage.");
         return false;
+    }
 
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
-        GenerateWarning("GL error 0x%x occurred during OpenGL context"
-                        " initialization, before WebGL initialization!", error);
+        const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
+                                     " initialization, before WebGL initialization!",
+                                     error);
+        out_failReason->Assign(reason);
         return false;
     }
 
     mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
     mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
@@ -744,32 +747,34 @@ WebGLContext::InitAndValidateGL()
         gl->fEnableVertexAttribArray(0);
 
     if (MinCapabilityMode())
         mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
     else
         gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
 
     if (mGLMaxVertexAttribs < 8) {
-        GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
-                        mGLMaxVertexAttribs);
+        const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
+                                     mGLMaxVertexAttribs);
+        out_failReason->Assign(reason);
         return false;
     }
 
     // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
     // even though the hardware supports much more.  The
     // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
     if (MinCapabilityMode())
         mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
     else
         gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);
 
     if (mGLMaxTextureUnits < 8) {
-        GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
-                        mGLMaxTextureUnits);
+        const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
+                                     mGLMaxTextureUnits);
+        out_failReason->Assign(reason);
         return false;
     }
 
     mBound2DTextures.SetLength(mGLMaxTextureUnits);
     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
     mBound3DTextures.SetLength(mGLMaxTextureUnits);
     mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
     mBoundSamplers.SetLength(mGLMaxTextureUnits);
@@ -915,37 +920,39 @@ WebGLContext::InitAndValidateGL()
         gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
     }
 
     // Check the shader validator pref
     mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
 
     // initialize shader translator
     if (!ShInitialize()) {
-        GenerateWarning("GLSL translator initialization failed!");
+        out_failReason->AssignLiteral("GLSL translator initialization failed!");
         return false;
     }
 
     // Mesa can only be detected with the GL_VERSION string, of the form
     // "2.1 Mesa 7.11.0"
     const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
     mIsMesa = strstr(versionStr, "Mesa");
 
     // Notice that the point of calling fGetError here is not only to check for
     // errors, but also to reset the error flags so that a subsequent WebGL
     // getError call will give the correct result.
     error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
-        GenerateWarning("GL error 0x%x occurred during WebGL context"
-                        " initialization!", error);
+        const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
+                                     " initialization!",
+                                     error);
+        out_failReason->Assign(reason);
         return false;
     }
 
     if (IsWebGL2() &&
-        !InitWebGL2())
+        !InitWebGL2(&*out_failReason))
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
     // Default value for all disabled vertex attributes is [0, 0, 0, 1]
     mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
     for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {