Bug 1294232 - Refactor blocklisting on Linux to support the downloadable blocklist. r?jrmuizel draft
authorAndrew Comminos <andrew@comminos.com>
Thu, 04 Aug 2016 17:02:14 -0400
changeset 405641 dafab915208be951602c830651cd692953c8c633
parent 405457 7963ebdd52b93f96b812eff2eab8d94097147b9c
child 529500 df808969baa90eb3b81296a6d8cfac8989801e49
push id27551
push userbmo:andrew@comminos.com
push dateThu, 25 Aug 2016 21:20:06 +0000
reviewersjrmuizel
bugs1294232
milestone51.0a1
Bug 1294232 - Refactor blocklisting on Linux to support the downloadable blocklist. r?jrmuizel MozReview-Commit-ID: ESJY9kkqXR8
toolkit/xre/glxtest.cpp
widget/GfxDriverInfo.cpp
widget/GfxDriverInfo.h
widget/GfxInfoBase.cpp
widget/GfxInfoBase.h
widget/GfxInfoX11.cpp
widget/GfxInfoX11.h
--- a/toolkit/xre/glxtest.cpp
+++ b/toolkit/xre/glxtest.cpp
@@ -57,16 +57,30 @@ typedef XID GLXPbuffer;
 
 // stuff from gl.h
 typedef uint8_t GLubyte;
 typedef uint32_t GLenum;
 #define GL_VENDOR       0x1F00
 #define GL_RENDERER     0x1F01
 #define GL_VERSION      0x1F02
 
+// GLX_MESA_query_renderer
+#define GLX_RENDERER_VENDOR_ID_MESA                            0x8183
+#define GLX_RENDERER_DEVICE_ID_MESA                            0x8184
+#define GLX_RENDERER_VERSION_MESA                              0x8185
+#define GLX_RENDERER_ACCELERATED_MESA                          0x8186
+#define GLX_RENDERER_VIDEO_MEMORY_MESA                         0x8187
+#define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA          0x8188
+#define GLX_RENDERER_PREFERRED_PROFILE_MESA                    0x8189
+#define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA          0x818A
+#define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
+#define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA            0x818C
+#define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA           0x818D
+#define GLX_RENDERER_ID_MESA                                   0x818E
+
 namespace mozilla {
 namespace widget {
 // the read end of the pipe, which will be used by GfxInfo
 extern int glxtest_pipe;
 // the PID of the glxtest process, to pass to waitpid()
 extern pid_t glxtest_pid;
 }
 }
@@ -247,34 +261,75 @@ void glxtest()
   ///// Get a GL context and make it current //////
   GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
   glXMakeCurrent(dpy, window, context);
 
   ///// Look for this symbol to determine texture_from_pixmap support /////
   void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT"); 
 
   ///// Get GL vendor/renderer/versions strings /////
-  enum { bufsize = 1024 };
+  enum { bufsize = 2048 };
   char buf[bufsize];
-  const GLubyte *vendorString = glGetString(GL_VENDOR);
-  const GLubyte *rendererString = glGetString(GL_RENDERER);
-  const GLubyte *versionString = glGetString(GL_VERSION);
 
-  if (!vendorString || !rendererString || !versionString)
+  const GLubyte* versionString = glGetString(GL_VERSION);
+  const GLubyte* vendorString = glGetString(GL_VENDOR);
+  const GLubyte* rendererString = glGetString(GL_RENDERER);
+
+  if (!versionString || !vendorString || !rendererString)
     fatal_error("glGetString returned null");
 
   int length = snprintf(buf, bufsize,
                         "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
                         vendorString,
                         rendererString,
                         versionString,
                         glXBindTexImageEXT ? "TRUE" : "FALSE");
   if (length >= bufsize)
     fatal_error("GL strings length too large for buffer size");
 
+  // If GLX_MESA_query_renderer is available, populate additional data.
+  typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int* value);
+  PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESAProc =
+    cast<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC>(glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
+  if (glXQueryCurrentRendererIntegerMESAProc) {
+    unsigned int vendorId, deviceId, accelerated, videoMemoryMB;
+    glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA, &vendorId);
+    glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA, &deviceId);
+    glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA, &accelerated);
+    glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA, &videoMemoryMB);
+
+    // Truncate IDs to 4 digits- that's all PCI IDs are.
+    vendorId &= 0xFFFF;
+    deviceId &= 0xFFFF;
+
+    length += snprintf(buf + length, bufsize,
+                       "MESA_VENDOR_ID\n0x%04x\n"
+                       "MESA_DEVICE_ID\n0x%04x\n"
+                       "MESA_ACCELERATED\n%s\n"
+                       "MESA_VRAM\n%dMB\n",
+                       vendorId, deviceId, accelerated ? "TRUE" : "FALSE",
+                       videoMemoryMB);
+
+    if (length >= bufsize)
+      fatal_error("GL strings length too large for buffer size");
+  }
+
+  // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
+  typedef const char * (* PFNGLXGETSCREENDRIVERPROC) (Display *dpy, int scrNum);
+  PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc =
+    cast<PFNGLXGETSCREENDRIVERPROC>(glXGetProcAddress("glXGetScreenDriver"));
+  if (glXGetScreenDriverProc) {
+    const char* driDriver = glXGetScreenDriverProc(dpy, DefaultScreen(dpy));
+    if (driDriver) {
+      length += snprintf(buf + length, bufsize, "DRI_DRIVER\n%s\n", driDriver);
+      if (length >= bufsize)
+        fatal_error("GL strings length too large for buffer size");
+    }
+  }
+
   ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
   ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
   ///// possible. Also we want to check that we're able to do that too without generating X errors.
   glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
   glXDestroyContext(dpy, context);
   XDestroyWindow(dpy, window);
   XFreeColormap(dpy, swa.colormap);
 
--- a/widget/GfxDriverInfo.cpp
+++ b/widget/GfxDriverInfo.cpp
@@ -301,14 +301,19 @@ const nsAString& GfxDriverInfo::GetDevic
 
   switch (id) {
     DECLARE_VENDOR_ID(VendorAll, "");
     DECLARE_VENDOR_ID(VendorIntel, "0x8086");
     DECLARE_VENDOR_ID(VendorNVIDIA, "0x10de");
     DECLARE_VENDOR_ID(VendorAMD, "0x1022");
     DECLARE_VENDOR_ID(VendorATI, "0x1002");
     DECLARE_VENDOR_ID(VendorMicrosoft, "0x1414");
+    DECLARE_VENDOR_ID(VendorMesaAll, "mesa/all");
+    DECLARE_VENDOR_ID(VendorMesaLLVMPipe, "mesa/llvmpipe");
+    DECLARE_VENDOR_ID(VendorMesaSoftPipe, "mesa/softpipe");
+    DECLARE_VENDOR_ID(VendorMesaSWRast, "mesa/swrast");
+    DECLARE_VENDOR_ID(VendorMesaUnknown, "mesa/unknown");
     // Suppress a warning.
     DECLARE_VENDOR_ID(DeviceVendorMax, "");
   }
 
   return *mDeviceVendors[id];
 }
--- a/widget/GfxDriverInfo.h
+++ b/widget/GfxDriverInfo.h
@@ -100,16 +100,28 @@ enum DeviceFamily {
 
 enum DeviceVendor {
   VendorAll,
   VendorIntel,
   VendorNVIDIA,
   VendorAMD,
   VendorATI,
   VendorMicrosoft,
+
+  // Wildcard for all Mesa drivers.
+  VendorMesaAll,
+  // Note that the following list of Mesa drivers is not comprehensive; we pull
+  // the DRI driver at runtime. These drivers are provided for convenience when
+  // populating the local blocklist.
+  VendorMesaLLVMPipe,
+  VendorMesaSoftPipe,
+  VendorMesaSWRast,
+  // A generic ID to be provided when we can't determine the DRI driver on Mesa.
+  VendorMesaUnknown,
+
   DeviceVendorMax
 };
 
 /* Array of devices to match, or an empty array for all devices */
 typedef nsTArray<nsString> GfxDeviceFamily;
 
 struct GfxDriverInfo
 {
@@ -253,17 +265,17 @@ inline void PadDriverDecimal(char *aStri
   aString[4] = 0;
 }
 
 inline bool
 ParseDriverVersion(const nsAString& aVersion, uint64_t *aNumericVersion)
 {
   *aNumericVersion = 0;
 
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(MOZ_X11)
   int a, b, c, d;
   char aStr[8], bStr[8], cStr[8], dStr[8];
   /* honestly, why do I even bother */
   if (!SplitDriverVersion(NS_LossyConvertUTF16toASCII(aVersion).get(), aStr, bStr, cStr, dStr))
     return false;
 
   PadDriverDecimal(bStr);
   PadDriverDecimal(cStr);
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -685,23 +685,22 @@ GfxInfoBase::FindBlocklistedDeviceInList
       if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
           NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
           NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
       {
         return 0;
       }
     }
 
-#if defined(XP_WIN) || defined(ANDROID)
+#if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_X11)
     uint64_t driverVersion;
     ParseDriverVersion(adapterDriverVersionString, &driverVersion);
 #endif
 
-    if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
-        !info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
+    if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID)) {
       continue;
     }
 
     if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) {
         bool deviceMatches = false;
         for (uint32_t j = 0; j < info[i].mDevices->Length(); j++) {
             if ((*info[i].mDevices)[j].Equals(adapterDeviceID, nsCaseInsensitiveStringComparator())) {
                 deviceMatches = true;
@@ -724,17 +723,17 @@ GfxInfoBase::FindBlocklistedDeviceInList
     }
     if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
         continue;
     }
     if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
         continue;
     }
 
-#if defined(XP_WIN) || defined(ANDROID)
+#if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_X11)
     switch (info[i].mComparisonOp) {
     case DRIVER_LESS_THAN:
       match = driverVersion < info[i].mDriverVersion;
       break;
     case DRIVER_BUILD_ID_LESS_THAN:
       match = (driverVersion & 0xFFFF) < info[i].mDriverVersion;
       break;
     case DRIVER_LESS_THAN_OR_EQUAL:
@@ -828,16 +827,24 @@ GfxInfoBase::FindBlocklistedDeviceInList
                                       (info[i].mDriverVersion & 0x000000000000ffff));
     }
   }
 #endif
 
   return status;
 }
 
+bool
+GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
+                             const nsAString& aAdapterVendor)
+{
+  return aBlocklistVendor.Equals(aAdapterVendor, nsCaseInsensitiveStringComparator()) ||
+         aBlocklistVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator());
+}
+
 nsresult
 GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature,
                                   int32_t* aStatus,
                                   nsAString& aSuggestedVersion,
                                   const nsTArray<GfxDriverInfo>& aDriverInfo,
                                   nsACString& aFailureId,
                                   OperatingSystem* aOS /* = nullptr */)
 {
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -100,16 +100,20 @@ protected:
                                         nsACString& aFailureId,
                                         OperatingSystem* aOS = nullptr);
 
   // Gets the driver info table. Used by GfxInfoBase to check for general cases
   // (while subclasses check for more specific ones).
   virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo() = 0;
 
   virtual void DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> obj);
+
+  virtual bool DoesVendorMatch(const nsAString& aBlocklistVendor,
+                               const nsAString& aAdapterVendor);
+
   bool InitFeatureObject(
     JSContext* aCx,
     JS::Handle<JSObject*> aContainer,
     const char* aName,
     int32_t aFeature,
     Maybe<mozilla::gfx::FeatureStatus> aKnownStatus,
     JS::MutableHandle<JSObject*> aOutObj);
 
--- a/widget/GfxInfoX11.cpp
+++ b/widget/GfxInfoX11.cpp
@@ -7,16 +7,18 @@
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <errno.h>
 #include <sys/utsname.h>
 #include "nsCRTGlue.h"
 #include "prenv.h"
+#include "nsPrintfCString.h"
+#include "nsWhitespaceTokenizer.h"
 
 #include "GfxInfoX11.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #include "nsICrashReporter.h"
 #endif
 
@@ -30,27 +32,20 @@ NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, Gfx
 // these global variables will be set when firing the glxtest process
 int glxtest_pipe = 0;
 pid_t glxtest_pid = 0;
 
 nsresult
 GfxInfo::Init()
 {
     mGLMajorVersion = 0;
-    mMajorVersion = 0;
-    mMinorVersion = 0;
-    mRevisionVersion = 0;
+    mGLMinorVersion = 0;
+    mHasTextureFromPixmap = false;
     mIsMesa = false;
-    mIsNVIDIA = false;
-    mIsFGLRX = false;
-    mIsNouveau = false;
-    mIsIntel = false;
-    mIsOldSwrast = false;
-    mIsLlvmpipe = false;
-    mHasTextureFromPixmap = false;
+    mIsAccelerated = true;
     return GfxInfoBase::Init();
 }
 
 void
 GfxInfo::GetData()
 {
     // to understand this function, see bug 639842. We retrieve the OpenGL driver information in a
     // separate process to protect against bad drivers.
@@ -101,36 +96,57 @@ GfxInfo::GetData()
     bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
                                   WIFEXITED(glxtest_status) && 
                                   WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
     bool received_signal = !waiting_for_glxtest_process_failed &&
                            WIFSIGNALED(glxtest_status);
 
     bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
 
-    nsCString textureFromPixmap; 
+    nsCString glVendor;
+    nsCString glRenderer;
+    nsCString glVersion;
+    nsCString textureFromPixmap;
+
+    // Available if GLX_MESA_query_renderer is supported.
+    nsCString mesaVendor;
+    nsCString mesaDevice;
+    nsCString mesaAccelerated;
+    // Available if using a DRI-based libGL stack.
+    nsCString driDriver;
+
     nsCString *stringToFill = nullptr;
     char *bufptr = buf;
     if (!error) {
         while(true) {
             char *line = NS_strtok("\n", &bufptr);
             if (!line)
                 break;
             if (stringToFill) {
                 stringToFill->Assign(line);
                 stringToFill = nullptr;
             }
             else if(!strcmp(line, "VENDOR"))
-                stringToFill = &mVendor;
+                stringToFill = &glVendor;
             else if(!strcmp(line, "RENDERER"))
-                stringToFill = &mRenderer;
+                stringToFill = &glRenderer;
             else if(!strcmp(line, "VERSION"))
-                stringToFill = &mVersion;
+                stringToFill = &glVersion;
             else if(!strcmp(line, "TFP"))
                 stringToFill = &textureFromPixmap;
+            else if(!strcmp(line, "MESA_VENDOR_ID"))
+                stringToFill = &mesaVendor;
+            else if(!strcmp(line, "MESA_DEVICE_ID"))
+                stringToFill = &mesaDevice;
+            else if(!strcmp(line, "MESA_ACCELERATED"))
+                stringToFill = &mesaAccelerated;
+            else if(!strcmp(line, "MESA_VRAM"))
+                stringToFill = &mAdapterRAM;
+            else if(!strcmp(line, "DRI_DRIVER"))
+                stringToFill = &driDriver;
         }
     }
 
     if (!strcmp(textureFromPixmap.get(), "TRUE"))
         mHasTextureFromPixmap = true;
 
     // only useful for Linux kernel version check for FGLRX driver.
     // assumes X client == X server, which is sad.
@@ -138,34 +154,34 @@ GfxInfo::GetData()
     if (!uname(&unameobj))
     {
       mOS.Assign(unameobj.sysname);
       mOSRelease.Assign(unameobj.release);
     }
 
     const char *spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
     if (spoofedVendor)
-        mVendor.Assign(spoofedVendor);
+        glVendor.Assign(spoofedVendor);
     const char *spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
     if (spoofedRenderer)
-        mRenderer.Assign(spoofedRenderer);
+        glRenderer.Assign(spoofedRenderer);
     const char *spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
     if (spoofedVersion)
-        mVersion.Assign(spoofedVersion);
+        glVersion.Assign(spoofedVersion);
     const char *spoofedOS = PR_GetEnv("MOZ_GFX_SPOOF_OS");
     if (spoofedOS)
         mOS.Assign(spoofedOS);
     const char *spoofedOSRelease = PR_GetEnv("MOZ_GFX_SPOOF_OS_RELEASE");
     if (spoofedOSRelease)
         mOSRelease.Assign(spoofedOSRelease);
 
     if (error ||
-        mVendor.IsEmpty() ||
-        mRenderer.IsEmpty() ||
-        mVersion.IsEmpty() ||
+        glVendor.IsEmpty() ||
+        glRenderer.IsEmpty() ||
+        glVersion.IsEmpty() ||
         mOS.IsEmpty() ||
         mOSRelease.IsEmpty())
     {
         mAdapterDescription.AppendLiteral("GLXtest process failed");
         if (waiting_for_glxtest_process_failed)
             mAdapterDescription.AppendPrintf(" (waitpid failed with errno=%d for pid %d)", waitpid_errno, glxtest_pid);
         if (exited_with_error_code)
             mAdapterDescription.AppendPrintf(" (exited with status %d)", WEXITSTATUS(glxtest_status));
@@ -177,104 +193,145 @@ GfxInfo::GetData()
             mAdapterDescription.Append('\n');
         }
 #ifdef MOZ_CRASHREPORTER
         CrashReporter::AppendAppNotesToCrashReport(mAdapterDescription);
 #endif
         return;
     }
 
-    mAdapterDescription.Append(mVendor);
-    mAdapterDescription.AppendLiteral(" -- ");
-    mAdapterDescription.Append(mRenderer);
+    // Scan the GL_VERSION string for the GL and driver versions.
+    nsCWhitespaceTokenizer tokenizer(glVersion);
+    while (tokenizer.hasMoreTokens()) {
+      nsCString token(tokenizer.nextToken());
+      unsigned int major = 0, minor = 0, revision = 0, patch = 0;
+      if (sscanf(token.get(), "%u.%u.%u.%u",
+                 &major, &minor, &revision, &patch) >= 2)
+      {
+        // A survey of GL_VENDOR strings indicates that the first version is
+        // always the GL version, the second is usually the driver version.
+        if (mGLMajorVersion == 0) {
+          mGLMajorVersion = major;
+          mGLMinorVersion = minor;
+        } else {
+          mDriverVersion = nsPrintfCString("%u.%u.%u.%u", major, minor, revision, patch);
+        }
+      }
+    }
+
+    if (mGLMajorVersion == 0) {
+      NS_WARNING("Failed to parse GL version!");
+      return;
+    }
+
+    // Mesa always exposes itself in the GL_VERSION string, but not always the
+    // GL_VENDOR string.
+    mIsMesa = glVersion.Find("Mesa") != -1;
+
+    // We need to use custom vendor IDs for mesa so we can treat them
+    // differently than the proprietary drivers.
+    if (mIsMesa) {
+      mIsAccelerated = !mesaAccelerated.Equals("FALSE");
+      // Process software rasterizers before the DRI driver string; we may be
+      // forcing software rasterization on a DRI-accelerated X server by using
+      // LIBGL_ALWAYS_SOFTWARE or a similar restriction.
+      if (strcasestr(glRenderer.get(), "llvmpipe")) {
+        CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorMesaLLVMPipe), mVendorId);
+        mIsAccelerated = false;
+      } else if (strcasestr(glRenderer.get(), "softpipe")) {
+        CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorMesaSoftPipe), mVendorId);
+        mIsAccelerated = false;
+      } else if (strcasestr(glRenderer.get(), "software rasterizer") ||
+                 !mIsAccelerated) {
+        // Fallback to reporting swrast if GLX_MESA_query_renderer tells us
+        // we're using an unaccelerated context.
+        CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorMesaSWRast), mVendorId);
+        mIsAccelerated = false;
+      } else if (!driDriver.IsEmpty()) {
+        mVendorId = nsPrintfCString("mesa/%s", driDriver.get());
+      } else {
+        // Some other mesa configuration where we couldn't get enough info.
+        NS_WARNING("Failed to detect Mesa driver being used!");
+        CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorMesaUnknown), mVendorId);
+      }
+
+      if (!mesaDevice.IsEmpty()) {
+        mDeviceId = mesaDevice;
+      } else {
+        NS_WARNING("Failed to get Mesa device ID! GLX_MESA_query_renderer unsupported?");
+      }
+    } else if (glVendor.EqualsLiteral("NVIDIA Corporation")) {
+      CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), mVendorId);
+      // TODO: Use NV-CONTROL X11 extension to query Device ID and VRAM.
+    } else if (glVendor.EqualsLiteral("ATI Technologies Inc.")) {
+      CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorATI), mVendorId);
+      // TODO: Look into ways to find the device ID on FGLRX.
+    } else {
+      NS_WARNING("Failed to detect GL vendor!");
+    }
+
+    // Fallback to GL_VENDOR and GL_RENDERER.
+    if (mVendorId.IsEmpty())
+      mVendorId.Assign(glVendor.get());
+    if (mDeviceId.IsEmpty())
+      mDeviceId.Assign(glRenderer.get());
+
+    mAdapterDescription.Assign(glRenderer);
 
     nsAutoCString note;
-    note.AppendLiteral("OpenGL: ");
-    note.Append(mAdapterDescription);
+    note.AppendLiteral("\nOpenGL: ");
+    note.Append(glRenderer);
     note.AppendLiteral(" -- ");
-    note.Append(mVersion);
+    note.Append(glVersion);
     if (mHasTextureFromPixmap)
         note.AppendLiteral(" -- texture_from_pixmap");
     note.Append('\n');
 #ifdef MOZ_CRASHREPORTER
     CrashReporter::AppendAppNotesToCrashReport(note);
 #endif
-
-    // determine the major OpenGL version. That's the first integer in the version string.
-    mGLMajorVersion = strtol(mVersion.get(), 0, 10);
-
-    // determine driver type (vendor) and where in the version string
-    // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
-    const char *whereToReadVersionNumbers = nullptr;
-    const char *Mesa_in_version_string = strstr(mVersion.get(), "Mesa");
-    if (Mesa_in_version_string) {
-        mIsMesa = true;
-        // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
-        // there is no actual driver version info.
-        whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
-        if (strcasestr(mVendor.get(), "nouveau"))
-            mIsNouveau = true;
-        if (strcasestr(mRenderer.get(), "intel")) // yes, intel is in the renderer string
-            mIsIntel = true;
-        if (strcasestr(mRenderer.get(), "llvmpipe"))
-            mIsLlvmpipe = true;
-        if (strcasestr(mRenderer.get(), "software rasterizer"))
-            mIsOldSwrast = true;
-    } else if (strstr(mVendor.get(), "NVIDIA Corporation")) {
-        mIsNVIDIA = true;
-        // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
-        // note that here the vendor and version strings behave differently, that's why we don't put this above
-        // alongside Mesa_in_version_string.
-        const char *NVIDIA_in_version_string = strstr(mVersion.get(), "NVIDIA");
-        if (NVIDIA_in_version_string)
-            whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
-    } else if (strstr(mVendor.get(), "ATI Technologies Inc")) {
-        mIsFGLRX = true;
-        // with the FGLRX driver, the version string only gives a OpenGL version :/ so let's return that.
-        // that can at least give a rough idea of how old the driver is.
-        whereToReadVersionNumbers = mVersion.get();
-    }
-
-    // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
-    if (whereToReadVersionNumbers) {
-        // copy into writable buffer, for tokenization
-        strncpy(buf, whereToReadVersionNumbers, buf_size);
-        bufptr = buf;
-
-        // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
-        // been initialized as 0 anyways
-        char *token = NS_strtok(".", &bufptr);
-        if (token) {
-            mMajorVersion = strtol(token, 0, 10);
-            token = NS_strtok(".", &bufptr);
-            if (token) {
-                mMinorVersion = strtol(token, 0, 10);
-                token = NS_strtok(".", &bufptr);
-                if (token)
-                    mRevisionVersion = strtol(token, 0, 10);
-            }
-        }
-    }
-}
-
-static inline uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
-{
-    return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
 }
 
 const nsTArray<GfxDriverInfo>&
 GfxInfo::GetGfxDriverInfo()
 {
-  // Nothing here yet.
-  //if (!mDriverInfo->Length()) {
-  //
-  //}
+  if (!mDriverInfo->Length()) {
+    // Mesa 10.0 provides the GLX_MESA_query_renderer extension, which allows us
+    // to query device IDs backing a GL context for blacklisting.
+    APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Linux,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorMesaAll), GfxDriverInfo::allDevices,
+      GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(10,0,0,0), "FEATURE_FAILURE_OLD_MESA", "Mesa 10.0");
+
+    // NVIDIA baseline (ported from old blocklist)
+    APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Linux,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(257,21,0,0), "FEATURE_FAILURE_OLD_NVIDIA", "NVIDIA 257.21");
+
+    // fglrx baseline (chosen arbitrarily as 2013-07-22 release).
+    APPEND_TO_DRIVER_BLOCKLIST(OperatingSystem::Linux,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
+      GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(13,15,100,1), "FEATURE_FAILURE_OLD_FGLRX", "fglrx 13.15.100.1");
+  }
   return *mDriverInfo;
 }
 
+bool
+GfxInfo::DoesVendorMatch(const nsAString& aBlocklistVendor,
+                         const nsAString& aAdapterVendor)
+{
+  if (mIsMesa && aBlocklistVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorMesaAll),
+                 nsCaseInsensitiveStringComparator()))
+  {
+    return true;
+  }
+  return GfxInfoBase::DoesVendorMatch(aBlocklistVendor, aAdapterVendor);
+}
+
 nsresult
 GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
                               int32_t *aStatus,
                               nsAString & aSuggestedDriverVersion,
                               const nsTArray<GfxDriverInfo>& aDriverInfo,
                               nsACString& aFailureId,
                               OperatingSystem* aOS /* = nullptr */)
 
@@ -283,114 +340,41 @@ GfxInfo::GetFeatureStatusImpl(int32_t aF
 
   NS_ENSURE_ARG_POINTER(aStatus);
   *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
   aSuggestedDriverVersion.SetIsVoid(true);
   OperatingSystem os = OperatingSystem::Linux;
   if (aOS)
     *aOS = os;
 
+  if (mGLMajorVersion == 0) {
+    // If we failed to get a GL version, glxtest failed.
+    *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+    aFailureId = "FEATURE_FAILURE_GLXTEST_FAILED";
+    return NS_OK;
+  }
+
   if (mGLMajorVersion == 1) {
     // We're on OpenGL 1. In most cases that indicates really old hardware.
     // We better block them, rather than rely on them to fail gracefully, because they don't!
     // see bug 696636
     *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
     aFailureId = "FEATURE_FAILURE_OPENGL_1";
     return NS_OK;
   }
 
-  // Don't evaluate any special cases if we're checking the downloaded blocklist.
-  if (!aDriverInfo.Length()) {
-    // Blacklist software GL implementations from using layers acceleration.
-    // On the test infrastructure, we'll force-enable layers acceleration.
-    if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS &&
-        (mIsLlvmpipe || mIsOldSwrast) &&
-        !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL"))
-    {
-      *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
-      aFailureId = "FEATURE_FAILURE_SOFTWARE_GL";
-      return NS_OK;
-    }
-
-    // Only check features relevant to Linux.
-    if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS ||
-        aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL ||
-        aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) {
-
-      // whitelist the linux test slaves' current configuration.
-      // this is necessary as they're still using the slightly outdated 190.42 driver.
-      // this isn't a huge risk, as at least this is the exact setting in which we do continuous testing,
-      // and this only affects GeForce 9400 cards on linux on this precise driver version, which is very few users.
-      // We do the same thing on Windows XP, see in widget/windows/GfxInfo.cpp
-      if (mIsNVIDIA &&
-          !strcmp(mRenderer.get(), "GeForce 9400/PCI/SSE2") &&
-          !strcmp(mVersion.get(), "3.2.0 NVIDIA 190.42"))
-      {
-        *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
-        return NS_OK;
-      }
-
-      if (mIsMesa) {
-        if (mIsNouveau && version(mMajorVersion, mMinorVersion) < version(8,0)) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_MESA_1";
-          aSuggestedDriverVersion.AssignLiteral("Mesa 8.0");
-        }
-        else if (version(mMajorVersion, mMinorVersion, mRevisionVersion) < version(7,10,3)) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_MESA_2";
-          aSuggestedDriverVersion.AssignLiteral("Mesa 7.10.3");
-        }
-        else if (mIsOldSwrast) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_SW_RAST";
-        }
-        else if (mIsLlvmpipe && version(mMajorVersion, mMinorVersion) < version(9, 1)) {
-          // bug 791905, Mesa bug 57733, fixed in Mesa 9.1 according to
-          // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_MESA_3";
-        }
-        else if (aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA)
-        {
-          if (mIsIntel && version(mMajorVersion, mMinorVersion) < version(8,1)) {
-            *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-            aFailureId = "FEATURE_FAILURE_MESA_4";
-            aSuggestedDriverVersion.AssignLiteral("Mesa 8.1");
-          }
-        }
-
-      } else if (mIsNVIDIA) {
-        if (version(mMajorVersion, mMinorVersion, mRevisionVersion) < version(257,21)) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_OLD_NV";
-          aSuggestedDriverVersion.AssignLiteral("NVIDIA 257.21");
-        }
-      } else if (mIsFGLRX) {
-        // FGLRX does not report a driver version number, so we have the OpenGL version instead.
-        // by requiring OpenGL 3, we effectively require recent drivers.
-        if (version(mMajorVersion, mMinorVersion, mRevisionVersion) < version(3, 0)) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
-          aFailureId = "FEATURE_FAILURE_OLD_FGLRX";
-          aSuggestedDriverVersion.AssignLiteral("<Something recent>");
-        }
-        // Bug 724640: FGLRX + Linux 2.6.32 is a crashy combo
-        bool unknownOS = mOS.IsEmpty() || mOSRelease.IsEmpty();
-        bool badOS = mOS.Find("Linux", true) != -1 &&
-                     mOSRelease.Find("2.6.32") != -1;
-        if (unknownOS || badOS) {
-          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
-          aFailureId = "FEATURE_FAILURE_OLD_OS";
-        }
-      } else {
-        // like on windows, let's block unknown vendors. Think of virtual machines.
-        // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
-        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
-      }
-    }
+  // Blacklist software GL implementations from using layers acceleration.
+  // On the test infrastructure, we'll force-enable layers acceleration.
+  if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS &&
+      !mIsAccelerated &&
+      !PR_GetEnv("MOZ_LAYERS_ALLOW_SOFTWARE_GL"))
+  {
+    *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
+    aFailureId = "FEATURE_FAILURE_SOFTWARE_GL";
+    return NS_OK;
   }
 
   return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
 }
 
 
 NS_IMETHODIMP
 GfxInfo::GetD2DEnabled(bool *aEnabled)
@@ -428,17 +412,18 @@ NS_IMETHODIMP
 GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription)
 {
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM)
 {
-  aAdapterRAM.Truncate();
+  GetData();
+  CopyUTF8toUTF16(mAdapterRAM, aAdapterRAM);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM)
 {
   return NS_ERROR_FAILURE;
 }
@@ -455,17 +440,17 @@ GfxInfo::GetAdapterDriver2(nsAString & a
 {
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion)
 {
   GetData();
-  CopyASCIItoUTF16(mVersion, aAdapterDriverVersion);
+  CopyUTF8toUTF16(mDriverVersion, aAdapterDriverVersion);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion)
 {
   return NS_ERROR_FAILURE;
 }
@@ -482,31 +467,31 @@ GfxInfo::GetAdapterDriverDate2(nsAString
 {
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID)
 {
   GetData();
-  CopyUTF8toUTF16(mVendor, aAdapterVendorID);
+  CopyUTF8toUTF16(mVendorId, aAdapterVendorID);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID)
 {
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID)
 {
   GetData();
-  CopyUTF8toUTF16(mRenderer, aAdapterDeviceID);
+  CopyUTF8toUTF16(mDeviceId, aAdapterDeviceID);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID)
 {
   return NS_ERROR_FAILURE;
 }
@@ -531,29 +516,29 @@ GfxInfo::GetIsGPU2Active(bool* aIsGPU2Ac
 
 #ifdef DEBUG
 
 // Implement nsIGfxInfoDebug
 // We don't support spoofing anything on Linux
 
 NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
 {
-  CopyUTF16toUTF8(aVendorID, mVendor);
+  CopyUTF16toUTF8(aVendorID, mVendorId);
   return NS_OK;
 }
 
 NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
 {
-  CopyUTF16toUTF8(aDeviceID, mRenderer);
+  CopyUTF16toUTF8(aDeviceID, mDeviceId);
   return NS_OK;
 }
 
 NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
 {
-  CopyUTF16toUTF8(aDriverVersion, mVersion);
+  CopyUTF16toUTF8(aDriverVersion, mDriverVersion);
   return NS_OK;
 }
 
 NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion)
 {
   // We don't support OS versioning on Linux. There's just "Linux".
   return NS_OK;
 }
--- a/widget/GfxInfoX11.h
+++ b/widget/GfxInfoX11.h
@@ -4,16 +4,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __GfxInfoX11_h__
 #define __GfxInfoX11_h__
 
 #include "GfxInfoBase.h"
+#include "nsString.h"
 
 namespace mozilla {
 namespace widget {
 
 class GfxInfo final : public GfxInfoBase
 {
 public:
 
@@ -59,26 +60,32 @@ protected:
   virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
                                         int32_t *aStatus,
                                         nsAString & aSuggestedDriverVersion,
                                         const nsTArray<GfxDriverInfo>& aDriverInfo,
                                         nsACString& aFailureId,
                                         OperatingSystem* aOS = nullptr) override;
   virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo() override;
 
+protected:
+  virtual bool DoesVendorMatch(const nsAString& aBlocklistVendor,
+                               const nsAString& aAdapterVendor) override;
+
 private:
-  nsCString mVendor;
-  nsCString mRenderer;
-  nsCString mVersion;
+  nsCString mVendorId;
+  nsCString mDeviceId;
+  nsCString mDriverVersion;
   nsCString mAdapterDescription;
+  nsCString mAdapterRAM;
   nsCString mOS;
   nsCString mOSRelease;
-  bool mIsMesa, mIsNVIDIA, mIsFGLRX, mIsNouveau, mIsIntel, mIsOldSwrast, mIsLlvmpipe;
   bool mHasTextureFromPixmap;
-  int mGLMajorVersion, mMajorVersion, mMinorVersion, mRevisionVersion;
+  unsigned int mGLMajorVersion, mGLMinorVersion;
+  bool mIsMesa;
+  bool mIsAccelerated;
 
   void AddCrashReportAnnotations();
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* __GfxInfoX11_h__ */