Bug 1335296 - Expand about:support WebGL information. - r=qdot,daoshengmu,jrmuizel
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 30 Jan 2017 18:58:52 -0800
changeset 332309 9462a96e05f43437536c4c8f1bb50544fd0ea6a2
parent 332308 f3793c0f52fa02dc0b61afb8e069ec341c208c79
child 332310 e470a4166e739501181dbb6a918e5c4f8a4003ec
push id86502
push userjgilbert@mozilla.com
push dateFri, 03 Feb 2017 00:51:28 +0000
treeherdermozilla-inbound@9462a96e05f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, daoshengmu, jrmuizel
bugs1335296
milestone54.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 1335296 - Expand about:support WebGL information. - r=qdot,daoshengmu,jrmuizel MozReview-Commit-ID: 166aNL6VSOa
dom/bindings/Bindings.conf
dom/canvas/WebGLContextExtensions.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextUtils.h
dom/canvas/WebGLExtensionDebugGet.cpp
dom/canvas/WebGLExtensions.h
dom/canvas/WebGLTypes.h
dom/canvas/moz.build
dom/webidl/WebGLRenderingContext.webidl
gfx/gl/GLContext.h
gfx/gl/GLContextCGL.h
gfx/gl/GLContextEAGL.h
gfx/gl/GLContextEGL.h
gfx/gl/GLContextGLX.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEAGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/gl/GLContextWGL.h
toolkit/content/aboutSupport.js
toolkit/locales/en-US/chrome/global/aboutSupport.properties
toolkit/modules/Troubleshoot.jsm
toolkit/modules/tests/browser/browser_Troubleshoot.js
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1286,16 +1286,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLExtensions.h'
 },
 
 'EXT_disjoint_timer_query': {
     'nativeType': 'mozilla::WebGLExtensionDisjointTimerQuery',
     'headerFile': 'WebGLExtensions.h'
 },
 
+'MOZ_debug_get': {
+    'nativeType': 'mozilla::WebGLExtensionDebugGet',
+    'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLFramebuffer': {
     'nativeType': 'mozilla::WebGLFramebuffer',
     'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
     'nativeType': 'mozilla::WebGLProgram',
     'headerFile': 'WebGLProgram.h'
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -35,16 +35,17 @@ WebGLContext::GetExtensionString(WebGLEx
         WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
         WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
         WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
         WEBGL_EXTENSION_IDENTIFIER(EXT_disjoint_timer_query)
+        WEBGL_EXTENSION_IDENTIFIER(MOZ_debug_get)
         WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
@@ -85,16 +86,18 @@ bool WebGLContext::IsExtensionSupported(
     }
 
     if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
         allowPrivilegedExts = true;
     }
 
     if (allowPrivilegedExts) {
         switch (ext) {
+        case WebGLExtensionID::MOZ_debug_get:
+            return true;
         case WebGLExtensionID::WEBGL_debug_renderer_info:
             return true;
         case WebGLExtensionID::WEBGL_debug_shaders:
             return true;
         default:
             // For warnings-as-errors.
             break;
         }
@@ -372,16 +375,21 @@ WebGLContext::EnableExtension(WebGLExten
         break;
     case WebGLExtensionID::EXT_sRGB:
         obj = new WebGLExtensionSRGB(this);
         break;
     case WebGLExtensionID::EXT_texture_filter_anisotropic:
         obj = new WebGLExtensionTextureFilterAnisotropic(this);
         break;
 
+    // MOZ_
+    case WebGLExtensionID::MOZ_debug_get:
+        obj = new WebGLExtensionDebugGet(this);
+        break;
+
     // OES_
     case WebGLExtensionID::OES_element_index_uint:
         obj = new WebGLExtensionElementIndexUint(this);
         break;
     case WebGLExtensionID::OES_standard_derivatives:
         obj = new WebGLExtensionStandardDerivatives(this);
         break;
     case WebGLExtensionID::OES_texture_float:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -56,28 +56,16 @@ WebGLContext::Enable(GLenum cap)
     {
         *trackingSlot = 1;
     }
 
     MakeContextCurrent();
     gl->fEnable(cap);
 }
 
-static JS::Value
-StringValue(JSContext* cx, const nsAString& str, ErrorResult& rv)
-{
-    JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length());
-    if (!jsStr) {
-        rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-        return JS::NullValue();
-    }
-
-    return JS::StringValue(jsStr);
-}
-
 bool
 WebGLContext::GetStencilBits(GLint* const out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -906,9 +906,23 @@ InfoFrom(WebGLTexImageFunc func, WebGLTe
         default:
             MOZ_CRASH("GFX: invalid 3D TexDimensions");
         }
     default:
         MOZ_CRASH("GFX: invalid TexDimensions");
     }
 }
 
+////
+
+JS::Value
+StringValue(JSContext* cx, const nsAString& str, ErrorResult& er)
+{
+    JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length());
+    if (!jsStr) {
+        er.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return JS::NullValue();
+    }
+
+    return JS::StringValue(jsStr);
+}
+
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -89,11 +89,13 @@ WebGLContext::WebGLObjectAsJSObject(JSCo
 }
 
 /**
  * Return the displayable name for the texture function that is the
  * source for validation.
  */
 const char* InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims);
 
+JS::Value StringValue(JSContext* cx, const nsAString& str, ErrorResult& er);
+
 } // namespace mozilla
 
 #endif // WEBGL_CONTEXT_UTILS_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLExtensionDebugGet.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLExtensions.h"
+
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "WebGLContext.h"
+#include "WebGLContextUtils.h"
+
+namespace mozilla {
+
+WebGLExtensionDebugGet::WebGLExtensionDebugGet(WebGLContext* webgl)
+    : WebGLExtensionBase(webgl)
+{
+}
+
+WebGLExtensionDebugGet::~WebGLExtensionDebugGet()
+{
+}
+
+void
+WebGLExtensionDebugGet::GetParameter(JSContext* cx, GLenum pname,
+                                     JS::MutableHandle<JS::Value> retval,
+                                     ErrorResult& er) const
+{
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+
+    switch (pname) {
+    case LOCAL_GL_EXTENSIONS:
+        {
+            nsString ret;
+            if (!gl->IsCoreProfile()) {
+                const auto rawExts = (const char*)gl->fGetString(LOCAL_GL_EXTENSIONS);
+                ret = NS_ConvertUTF8toUTF16(rawExts);
+            } else {
+                const auto& numExts = gl->GetIntAs<GLuint>(LOCAL_GL_NUM_EXTENSIONS);
+                for (GLuint i = 0; i < numExts; i++) {
+                    const auto rawExt = (const char*)gl->fGetStringi(LOCAL_GL_EXTENSIONS,
+                                                                     i);
+                    if (i > 0) {
+                        ret.AppendLiteral(" ");
+                    }
+                    ret.Append(NS_ConvertUTF8toUTF16(rawExt));
+                }
+            }
+            retval.set(StringValue(cx, ret, er));
+            return;
+        }
+
+    case LOCAL_GL_RENDERER:
+    case LOCAL_GL_VENDOR:
+    case LOCAL_GL_VERSION:
+        {
+            const auto raw = (const char*)gl->fGetString(pname);
+            retval.set(StringValue(cx, NS_ConvertUTF8toUTF16(raw), er));
+            return;
+        }
+
+    case 0x10000: // "WSI_INFO"
+        {
+            nsCString info;
+            gl->GetWSIInfo(&info);
+            retval.set(StringValue(cx, NS_ConvertUTF8toUTF16(info), er));
+            return;
+        }
+
+    default:
+        mContext->ErrorInvalidEnumArg("MOZ_debug_get.getParameter", "pname", pname);
+        retval.set(JS::NullValue());
+        return;
+    }
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugGet, MOZ_debug_get)
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -9,16 +9,17 @@
 #include "mozilla/AlreadyAddRefed.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
+class ErrorResult;
 
 namespace dom {
 template<typename> struct Nullable;
 template<typename> class Sequence;
 } // namespace dom
 
 namespace webgl {
 class FormatUsageAuthority;
@@ -398,11 +399,24 @@ public:
     void GetQueryObjectEXT(JSContext* cx, const WebGLQuery& query,
                            GLenum pname, JS::MutableHandleValue retval) const;
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDebugGet final
+    : public WebGLExtensionBase
+{
+public:
+    explicit WebGLExtensionDebugGet(WebGLContext* webgl);
+    virtual ~WebGLExtensionDebugGet();
+
+    void GetParameter(JSContext* cx, GLenum pname,
+                      JS::MutableHandle<JS::Value> retval, ErrorResult& er) const;
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 } // namespace mozilla
 
 #endif // WEBGL_EXTENSIONS_H_
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -144,16 +144,17 @@ enum class WebGLExtensionID : uint8_t {
     EXT_blend_minmax,
     EXT_color_buffer_float,
     EXT_color_buffer_half_float,
     EXT_frag_depth,
     EXT_sRGB,
     EXT_shader_texture_lod,
     EXT_texture_filter_anisotropic,
     EXT_disjoint_timer_query,
+    MOZ_debug_get,
     OES_element_index_uint,
     OES_standard_derivatives,
     OES_texture_float,
     OES_texture_float_linear,
     OES_texture_half_float,
     OES_texture_half_float_linear,
     OES_vertex_array_object,
     WEBGL_color_buffer_float,
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -132,16 +132,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionColorBufferFloat.cpp',
     'WebGLExtensionColorBufferHalfFloat.cpp',
     'WebGLExtensionCompressedTextureASTC.cpp',
     'WebGLExtensionCompressedTextureATC.cpp',
     'WebGLExtensionCompressedTextureES3.cpp',
     'WebGLExtensionCompressedTextureETC1.cpp',
     'WebGLExtensionCompressedTexturePVRTC.cpp',
     'WebGLExtensionCompressedTextureS3TC.cpp',
+    'WebGLExtensionDebugGet.cpp',
     'WebGLExtensionDebugRendererInfo.cpp',
     'WebGLExtensionDebugShaders.cpp',
     'WebGLExtensionDepthTexture.cpp',
     'WebGLExtensionDisjointTimerQuery.cpp',
     'WebGLExtensionDrawBuffers.cpp',
     'WebGLExtensionElementIndexUint.cpp',
     'WebGLExtensionEXTColorBufferFloat.cpp',
     'WebGLExtensionFragDepth.cpp',
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -1078,8 +1078,17 @@ interface EXT_disjoint_timer_query {
     void deleteQueryEXT(WebGLQuery? query);
     [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLQuery? query);
     void beginQueryEXT(GLenum target, WebGLQuery query);
     void endQueryEXT(GLenum target);
     void queryCounterEXT(WebGLQuery query, GLenum target);
     any getQueryEXT(GLenum target, GLenum pname);
     any getQueryObjectEXT(WebGLQuery query, GLenum pname);
 };
+
+[NoInterfaceObject]
+interface MOZ_debug_get {
+    const GLenum EXTENSIONS = 0x1F03;
+    const GLenum WSI_INFO   = 0x10000;
+
+    [Throws]
+    any getParameter(GLenum pname);
+};
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -218,16 +218,18 @@ public:
     /**
     * Returns true if the context is using WARP. This should only be overridden
     * for an ANGLE implementation.
     */
     virtual bool IsWARP() const {
         return false;
     }
 
+    virtual void GetWSIInfo(nsCString* const out) const = 0;
+
     /**
      * Return true if we are running on a OpenGL core profile context
      */
     inline bool IsCoreProfile() const {
         MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
 
         return mProfile == ContextProfile::OpenGLCore;
     }
--- a/gfx/gl/GLContextCGL.h
+++ b/gfx/gl/GLContextCGL.h
@@ -53,14 +53,16 @@ public:
 
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
     virtual bool SupportsRobustness() const override { return false; }
 
     virtual bool SwapBuffers() override;
+
+    virtual void GetWSIInfo(nsCString* const out) const override;
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif // GLCONTEXTCGL_H_
--- a/gfx/gl/GLContextEAGL.h
+++ b/gfx/gl/GLContextEAGL.h
@@ -50,16 +50,18 @@ public:
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
     virtual bool SupportsRobustness() const override { return false; }
 
     virtual bool SwapBuffers() override;
 
+    virtual void GetWSIInfo(nsCString* const out) const override;
+
     virtual GLuint GetDefaultFramebuffer() override {
         return mBackbufferFB;
     }
 
     virtual bool RenewSurface(nsIWidget* aWidget) override {
         // FIXME: should use the passed widget instead of the existing one.
         return RecreateRB();
     }
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -83,16 +83,18 @@ public:
     virtual bool RenewSurface(widget::CompositorWidget* aWidget) override;
 
     virtual void ReleaseSurface() override;
 
     virtual bool SetupLookupFunction() override;
 
     virtual bool SwapBuffers() override;
 
+    virtual void GetWSIInfo(nsCString* const out) const override;
+
     // hold a reference to the given surface
     // for the lifetime of this context.
     void HoldSurface(gfxASurface* aSurf);
 
     EGLSurface GetEGLSurface() const {
         return mSurface;
     }
 
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -54,16 +54,18 @@ public:
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
     virtual bool SupportsRobustness() const override;
 
     virtual bool SwapBuffers() override;
 
+    virtual void GetWSIInfo(nsCString* const out) const override;
+
     // Overrides the current GLXDrawable backing the context and makes the
     // context current.
     bool OverrideDrawable(GLXDrawable drawable);
 
     // Undoes the effect of a drawable override.
     bool RestoreDrawable();
 
 private:
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -161,16 +161,21 @@ GLContextCGL::SwapBuffers()
 {
   PROFILER_LABEL("GLContextCGL", "SwapBuffers",
     js::ProfileEntry::Category::GRAPHICS);
 
   [mContext flushBuffer];
   return true;
 }
 
+void
+GLContextCGL::GetWSIInfo(nsCString* const out) const
+{
+    out->AppendLiteral("CGL");
+}
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
--- a/gfx/gl/GLContextProviderEAGL.mm
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -150,16 +150,21 @@ GLContextEAGL::SwapBuffers()
 {
   PROFILER_LABEL("GLContextEAGL", "SwapBuffers",
     js::ProfileEntry::Category::GRAPHICS);
 
   [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER];
   return true;
 }
 
+void
+GLContextEAGL::GetWSIInfo(nsCString* const out) const
+{
+    out->AppendLiteral("EAGL");
+}
 
 already_AddRefed<GLContext>
 GLContextProviderEAGL::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 static GLContextEAGL*
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -427,16 +427,34 @@ GLContextEGL::SwapBuffers()
                           : mSurface;
     if (surface) {
         return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), surface);
     } else {
         return false;
     }
 }
 
+void
+GLContextEGL::GetWSIInfo(nsCString* const out) const
+{
+    out->AppendLiteral("EGL_VENDOR: ");
+    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VENDOR));
+
+    out->AppendLiteral("\nEGL_VERSION: ");
+    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_VERSION));
+
+    out->AppendLiteral("\nEGL_EXTENSIONS: ");
+    out->Append((const char*)sEGLLibrary.fQueryString(EGL_DISPLAY(), LOCAL_EGL_EXTENSIONS));
+
+#ifndef ANDROID // This query will crash some old android.
+    out->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
+    out->Append((const char*)sEGLLibrary.fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
+#endif
+}
+
 // hold a reference to the given surface
 // for the lifetime of this context.
 void
 GLContextEGL::HoldSurface(gfxASurface* aSurf) {
     mThebesSurface = aSurf;
 }
 
 already_AddRefed<GLContextEGL>
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -993,16 +993,37 @@ bool
 GLContextGLX::SwapBuffers()
 {
     if (!mDoubleBuffered)
         return false;
     mGLX->xSwapBuffers(mDisplay, mDrawable);
     return true;
 }
 
+void
+GLContextGLX::GetWSIInfo(nsCString* const out) const
+{
+    Display* display = DefaultXDisplay();
+    int screen = DefaultScreen(display);
+
+    int majorVersion, minorVersion;
+    sGLXLibrary.xQueryVersion(display, &majorVersion, &minorVersion);
+
+    out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
+
+    out->AppendLiteral("\nGLX_VENDOR(client): ");
+    out->Append(sGLXLibrary.xGetClientString(display, LOCAL_GLX_VENDOR));
+
+    out->AppendLiteral("\nGLX_VENDOR(server): ");
+    out->Append(sGLXLibrary.xQueryServerString(display, screen, LOCAL_GLX_VENDOR));
+
+    out->AppendLiteral("\nExtensions: ");
+    out->Append(sGLXLibrary.xQueryExtensionsString(display, screen));
+}
+
 bool
 GLContextGLX::OverrideDrawable(GLXDrawable drawable)
 {
     if (Screen())
         Screen()->AssureBlitted();
     Bool result = mGLX->xMakeCurrent(mDisplay, drawable, mContext);
     return result;
 }
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -372,16 +372,23 @@ GLContextWGL::SupportsRobustness() const
 
 bool
 GLContextWGL::SwapBuffers() {
     if (!mIsDoubleBuffered)
         return false;
     return ::SwapBuffers(mDC);
 }
 
+void
+GLContextWGL::GetWSIInfo(nsCString* const out) const
+{
+    out->AppendLiteral("wglGetExtensionsString: ");
+    out->Append(sWGLLib.fGetExtensionsString(mDC));
+}
+
 bool
 GLContextWGL::SetupLookupFunction()
 {
     // Make sure that we have a ref to the OGL library;
     // when run under CodeXL, wglGetProcAddress won't return
     // the right thing for some core functions.
     MOZ_ASSERT(mLibrary == nullptr);
 
--- a/gfx/gl/GLContextWGL.h
+++ b/gfx/gl/GLContextWGL.h
@@ -54,16 +54,18 @@ public:
     virtual bool IsDoubleBuffered() const override;
 
     virtual bool SupportsRobustness() const override;
 
     virtual bool SwapBuffers() override;
 
     virtual bool SetupLookupFunction() override;
 
+    virtual void GetWSIInfo(nsCString* const out) const override;
+
     HGLRC Context() { return mContext; }
 
 protected:
     friend class GLContextProviderWGL;
 
     HDC mDC;
     HGLRC mContext;
     HWND mWnd;
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -242,19 +242,22 @@ var snapshotFormatters = {
         title = key.substr(1);
       } else {
         try {
           title = strings.GetStringFromName(key);
         } catch (e) {
           title = key;
         }
       }
+      let td = $.new("td", value);
+      td.style["white-space"] = "pre-wrap";
+
       return $.new("tr", [
         $.new("th", title, "column"),
-        $.new("td", value),
+        td,
       ]);
     }
 
     // @where    The name in "graphics-<name>-tbody", of the element to append to.
     // @trs      Array of row elements.
     function addRows(where, trs) {
       $.append($("graphics-" + where + "-tbody"), trs);
     }
@@ -368,18 +371,25 @@ var snapshotFormatters = {
     delete data.numTotalWindows;
     delete data.numAcceleratedWindows;
     delete data.numAcceleratedWindowsMessage;
 
     addRow("features", "asyncPanZoom",
            apzInfo.length
            ? apzInfo.join("; ")
            : localizedMsg(["apzNone"]));
-    addRowFromKey("features", "webglRenderer");
+    addRowFromKey("features", "webgl1Renderer");
+    addRowFromKey("features", "webgl1Version");
+    addRowFromKey("features", "webgl1Extensions");
+    addRowFromKey("features", "webgl1WSIInfo");
     addRowFromKey("features", "webgl2Renderer");
+    addRowFromKey("features", "webgl2Version");
+    addRowFromKey("features", "webgl2Extensions");
+    addRowFromKey("features", "webgl2WSIInfo");
+    addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
     addRowFromKey("features", "currentAudioBackend", "audioBackend");
     addRowFromKey("features", "direct2DEnabled", "#Direct2D");
 
     if ("directWriteEnabled" in data) {
       let message = data.directWriteEnabled;
       if ("directWriteVersion" in data)
         message += " (" + data.directWriteVersion + ")";
       addRow("features", "#DirectWrite", message);
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -66,18 +66,24 @@ gpuDescription = Description
 gpuVendorID = Vendor ID
 gpuDeviceID = Device ID
 gpuSubsysID = Subsys ID
 gpuDrivers = Drivers
 gpuRAM = RAM
 gpuDriverVersion = Driver Version
 gpuDriverDate = Driver Date
 gpuActive = Active
-webglRenderer = WebGL Renderer
-webgl2Renderer = WebGL2 Renderer
+webgl1Renderer = WebGL 1 Renderer
+webgl1Version = WebGL 1 GL Version
+webgl1Extensions = WebGL 1 GL Extensions
+webgl1WSIInfo = WebGL 1 WSI Info
+webgl2Renderer = WebGL 2 Renderer
+webgl2Version = WebGL 2 GL Version
+webgl2Extensions = WebGL 2 GL Extensions
+webgl2WSIInfo = WebGL 2 WSI Info
 GPU1 = GPU #1
 GPU2 = GPU #2
 blocklistedBug = Blocklisted due to known issues
 # LOCALIZATION NOTE %1$S will be replaced with a bug number string.
 bugLink = bug %1$S
 # LOCALIZATION NOTE %1$S will be replaced with an arbitrary identifier
 # string that can be searched on DXR/MXR or grepped in the source tree.
 unknownFailure = Blocklisted; failure code %1$S
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -408,63 +408,74 @@ var dataProviders = {
         statusMsgForFeature(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
 
 
     let doc =
       Cc["@mozilla.org/xmlextras/domparser;1"]
       .createInstance(Ci.nsIDOMParser)
       .parseFromString("<html/>", "text/html");
 
-    function GetWebGLInfo(contextType) {
+    function GetWebGLInfo(data, keyPrefix, contextType) {
+        data[keyPrefix + "Renderer"] = "-";
+        data[keyPrefix + "Version"] = "-";
+        data[keyPrefix + "Extensions"] = "-";
+        data[keyPrefix + "WSIInfo"] = "-";
+
+        ////
+
         let canvas = doc.createElement("canvas");
         canvas.width = 1;
         canvas.height = 1;
 
+        ////
 
         let creationError = null;
 
         canvas.addEventListener(
             "webglcontextcreationerror",
 
             function(e) {
                 creationError = e.statusMessage;
             }
         );
 
         let gl = null;
         try {
-          gl = canvas.getContext(contextType);
+            gl = canvas.getContext(contextType);
         } catch (e) {
-          if (!creationError) {
-            creationError = e.toString();
-          }
+            if (!creationError) {
+                creationError = e.toString();
+            }
         }
-        if (!gl)
-            return creationError || "(no info)";
+        if (!gl) {
+            data[keyPrefix + "Renderer"] = creationError || "(no creation error info)";
+            return;
+        }
 
+        ////
 
-        let infoExt = gl.getExtension("WEBGL_debug_renderer_info");
+        let ext = gl.getExtension("MOZ_debug_get");
         // This extension is unconditionally available to chrome. No need to check.
-        let vendor = gl.getParameter(infoExt.UNMASKED_VENDOR_WEBGL);
-        let renderer = gl.getParameter(infoExt.UNMASKED_RENDERER_WEBGL);
+        let vendor = ext.getParameter(gl.VENDOR);
+        let renderer = ext.getParameter(gl.RENDERER);
 
-        let contextInfo = vendor + " -- " + renderer;
+        data[keyPrefix + "Renderer"] = vendor + " -- " + renderer;
+        data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION);
+        data[keyPrefix + "Extensions"] = ext.getParameter(ext.EXTENSIONS);
+        data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO);
 
+        ////
 
         // Eagerly free resources.
         let loseExt = gl.getExtension("WEBGL_lose_context");
         loseExt.loseContext();
-
-
-        return contextInfo;
     }
 
-
-    data.webglRenderer = GetWebGLInfo("webgl");
-    data.webgl2Renderer = GetWebGLInfo("webgl2");
+    GetWebGLInfo(data, "webgl1", "webgl");
+    GetWebGLInfo(data, "webgl2", "webgl2");
 
 
     let infoInfo = gfxInfo.getInfo();
     if (infoInfo)
       data.info = infoInfo;
 
     let failureCount = {};
     let failureIndices = {};
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -289,22 +289,40 @@ const SNAPSHOT_SCHEMA = {
           type: "boolean",
         },
         directWriteVersion: {
           type: "string",
         },
         clearTypeParameters: {
           type: "string",
         },
-        webglRenderer: {
+        webgl1Renderer: {
+          type: "string",
+        },
+        webgl1Version: {
+          type: "string",
+        },
+        webgl1Extensions: {
+          type: "string",
+        },
+        webgl1WSIInfo: {
           type: "string",
         },
         webgl2Renderer: {
           type: "string",
         },
+        webgl2Version: {
+          type: "string",
+        },
+        webgl2Extensions: {
+          type: "string",
+        },
+        webgl2WSIInfo: {
+          type: "string",
+        },
         info: {
           type: "object",
         },
         failures: {
           type: "array",
           items: {
             type: "string",
           },