Bug 1294232 - Refactor blocklisting on Linux to support the downloadable blocklist. r=jrmuizel
☠☠ backed out by e2d2897e4a74 ☠ ☠
authorAndrew Comminos <andrew@comminos.com>
Thu, 04 Aug 2016 17:02:14 -0400
changeset 357393 cf43cacdb26279b1c258ac9b06517f93ebb58056
parent 357392 0806dc4faa450dd33da4eaf21687260cc54ad379
child 357394 100b44ed0e01bc55654d6e9a8eba8d0d5998ff58
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1294232
milestone52.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 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
@@ -101,16 +101,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
 {
@@ -254,17 +266,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
@@ -689,23 +689,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;
@@ -728,17 +727,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:
@@ -835,16 +834,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
@@ -101,16 +101,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__ */