Bug 1162530 - Part 1: Add versioning to graphics blocklist. r=jmuizelaar
authorMilan Sreckovic <milan@mozilla.com>
Fri, 15 May 2015 10:42:10 -0400
changeset 244803 93fe551e8a557bc81c6798a8e4f494cfbb2d7fd6
parent 244802 23611294fcd654307bd98d8776bb40d6c9a71fa6
child 244804 edb5b81dd9e76fb76c0a8d38e214c9593a0f9c0a
push id13061
push userkwierso@gmail.com
push dateThu, 21 May 2015 01:32:04 +0000
treeherderfx-team@d0a72997ef97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmuizelaar
bugs1162530
milestone41.0a1
Bug 1162530 - Part 1: Add versioning to graphics blocklist. r=jmuizelaar
gfx/tests/gtest/TestGfxWidgets.cpp
gfx/tests/gtest/moz.build
widget/GfxInfoBase.cpp
widget/GfxInfoBase.h
new file mode 100644
--- /dev/null
+++ b/gfx/tests/gtest/TestGfxWidgets.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "gtest/gtest.h"
+#include "GfxDriverInfo.h"
+#include "nsVersionComparator.h"
+
+using namespace mozilla::widget;
+
+TEST(GfxWidgets, Split) {
+  char aStr[8], bStr[8], cStr[8], dStr[8];
+
+  ASSERT_TRUE(SplitDriverVersion("33.4.3.22", aStr, bStr, cStr, dStr));
+  ASSERT_TRUE(atoi(aStr) == 33 && atoi(bStr) == 4 && atoi(cStr) == 3 && atoi(dStr) == 22);
+
+  ASSERT_TRUE(SplitDriverVersion("28.74.0.0", aStr, bStr, cStr, dStr));
+  ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 74 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+  ASSERT_TRUE(SplitDriverVersion("132.0.0.0", aStr, bStr, cStr, dStr));
+  ASSERT_TRUE(atoi(aStr) == 132 && atoi(bStr) == 0 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+  ASSERT_TRUE(SplitDriverVersion("2.3.0.0", aStr, bStr, cStr, dStr));
+  ASSERT_TRUE(atoi(aStr) == 2 && atoi(bStr) == 3 && atoi(cStr) == 0 && atoi(dStr) == 0);
+
+  ASSERT_TRUE(SplitDriverVersion("25.4.0.8", aStr, bStr, cStr, dStr));
+  ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 && atoi(dStr) == 8);
+
+}
+
+TEST(GfxWidgets, Versioning) {
+  ASSERT_TRUE(mozilla::Version("0") < mozilla::Version("41.0a1"));
+  ASSERT_TRUE(mozilla::Version("39.0.5b7") < mozilla::Version("41.0a1"));
+  ASSERT_TRUE(mozilla::Version("18.0.5b7") < mozilla::Version("18.2"));
+  ASSERT_TRUE(mozilla::Version("30.0.5b7") < mozilla::Version("41.0b9"));
+  ASSERT_TRUE(mozilla::Version("100") > mozilla::Version("43.0a1"));
+  ASSERT_FALSE(mozilla::Version("42.0") < mozilla::Version("42.0"));
+  ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42.0"));
+  ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42"));
+  ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("43.0a1"));
+  ASSERT_TRUE(mozilla::Version("42") < mozilla::Version("43.0a1"));
+  ASSERT_TRUE(mozilla::Version("42.0") < mozilla::Version("43.0a1"));
+  ASSERT_TRUE(mozilla::Version("42.0.5") < mozilla::Version("43.0a1"));
+  ASSERT_TRUE(mozilla::Version("42.1") < mozilla::Version("43.0a1"));
+  ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42"));
+  ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0.5"));
+  ASSERT_TRUE(mozilla::Version("42.0b7") < mozilla::Version("42.0.5"));
+  ASSERT_TRUE(mozilla::Version("") == mozilla::Version("0"));
+
+  // Note these two; one would expect for 42.0b1 and 42b1 to compare the
+  // same, but they do not.  If this ever changes, we want to know, so
+  // leave the test here to fail.
+  ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0b2"));
+  ASSERT_FALSE(mozilla::Version("42.0a1") < mozilla::Version("42b2"));
+}
+
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -8,16 +8,17 @@ UNIFIED_SOURCES += [
     'gfxSurfaceRefCountTest.cpp',
     # Disabled on suspicion of causing bug 904227
     #'gfxWordCacheTest.cpp',
     'TestAsyncPanZoomController.cpp',
     'TestBufferRotation.cpp',
     'TestColorNames.cpp',
     'TestCompositor.cpp',
     'TestGfxPrefs.cpp',
+    'TestGfxWidgets.cpp',
     'TestLayers.cpp',
     'TestRegion.cpp',
     'TestSkipChars.cpp',
     # Hangs on linux in ApplyGdkScreenFontOptions
     #'gfxFontSelectionTest.cpp',
     'TestTextures.cpp',
     # Test works but it doesn't assert anything
     #'gfxTextRunPerfTest.cpp',
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -11,26 +11,28 @@
 
 #include "GfxInfoWebGL.h"
 #include "GfxDriverInfo.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
+#include "nsVersionComparator.h"
 #include "mozilla/Services.h"
 #include "mozilla/Observer.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
+#include "nsIXULAppInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
@@ -212,16 +214,39 @@ BlacklistNodeToTextValue(nsIDOMNode *aBl
     return false;
 
   value.Trim(" \t\r\n");
   aValue = value;
 
   return true;
 }
 
+// <foo attr=Hello/> finds "Hello" if the aAttrName is "attr".
+static bool
+BlacklistAttrToTextValue(nsIDOMNode *aBlacklistNode,
+                         const nsAString& aAttrName,
+                         nsAString& aValue)
+{
+  nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistNode);
+  if (!element) {
+    return false;
+  }
+
+  nsAutoString value;
+  if (NS_FAILED(element->GetAttribute(aAttrName, value))) {
+    return false;
+  }
+
+  value.Trim(" \t\r\n");
+  aValue = value;
+
+  return true;
+}
+
+
 static OperatingSystem
 BlacklistOSToOperatingSystem(const nsAString& os)
 {
   if (os.EqualsLiteral("WINNT 5.1"))
     return DRIVER_OS_WINDOWS_XP;
   else if (os.EqualsLiteral("WINNT 5.2"))
     return DRIVER_OS_WINDOWS_SERVER_2003;
   else if (os.EqualsLiteral("WINNT 6.0"))
@@ -403,16 +428,52 @@ BlacklistEntryToDriverInfo(nsIDOMNode* a
 
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistEntry);
   if (!element)
     return false;
 
   nsCOMPtr<nsIDOMNode> dataNode;
   nsAutoString dataValue;
 
+  // If we get an application version to be zero, something is not working
+  // and we are not going to bother checking the blocklist versions.
+  // See TestGfxWidgets.cpp for how version comparison works.
+  // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
+  static mozilla::Version zeroV("0");
+  static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
+  if (appV <= zeroV) {
+      gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Invalid application version " << GfxInfoBase::GetApplicationVersion().get();
+  } else if (BlacklistNodeGetChildByName(element,
+                                         NS_LITERAL_STRING("versionRange"),
+                                         getter_AddRefs(dataNode))) {
+    if (BlacklistAttrToTextValue(dataNode,
+                                 NS_LITERAL_STRING("minVersion"),
+                                 dataValue)) {
+      mozilla::Version minV(NS_ConvertUTF16toUTF8(dataValue).get());
+      if (minV > zeroV && appV < minV) {
+        // The version of the application is less than the minimal version
+        // this blocklist entry applies to, so we can just ignore it by
+        // returning false and letting the caller deal with it.
+        return false;
+      }
+    }
+
+    if (BlacklistAttrToTextValue(dataNode,
+                                 NS_LITERAL_STRING("maxVersion"),
+                                 dataValue)) {
+      mozilla::Version maxV(NS_ConvertUTF16toUTF8(dataValue).get());
+      if (maxV > zeroV && appV > maxV) {
+        // The version of the application is more than the maximal version
+        // this blocklist entry applies to, so we can just ignore it by
+        // returning false and letting the caller deal with it.
+        return false;
+      }
+    }
+  }
+
   // <os>WINNT 6.0</os>
   if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("os"),
                                   getter_AddRefs(dataNode))) {
     BlacklistNodeToTextValue(dataNode, dataValue);
     aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
   }
 
   // <osversion>14</osversion> currently only used for Android
@@ -527,19 +588,19 @@ BlacklistEntriesToDriverInfo(nsIDOMHTMLC
   for (uint32_t i = 0; i < length; ++i) {
     nsCOMPtr<nsIDOMNode> blacklistEntry;
     if (NS_SUCCEEDED(aBlacklistEntries->Item(i,
                                              getter_AddRefs(blacklistEntry))) &&
         blacklistEntry) {
       GfxDriverInfo di;
       if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
         aDriverInfo[i] = di;
+        // Prevent di falling out of scope from destroying the devices.
+        di.mDeleteDevices = false;
       }
-      // Prevent di falling out of scope from destroying the devices.
-      di.mDeleteDevices = false;
     }
   }
 }
 
 NS_IMETHODIMP
 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
                      const char16_t* aData)
 {
@@ -608,16 +669,30 @@ GfxInfoBase::FindBlocklistedDeviceInList
                                          nsAString& aSuggestedVersion,
                                          int32_t aFeature,
                                          OperatingSystem os)
 {
   int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
 
   uint32_t i = 0;
   for (; i < info.Length(); i++) {
+    // Do the operating system check first, no point in getting the driver
+    // info if we won't need to use it. Note that this also catches the
+    // application version mismatches that would leave operating system
+    // set to unknown.
+    if (info[i].mOperatingSystem != DRIVER_OS_ALL &&
+        info[i].mOperatingSystem != os)
+    {
+      continue;
+    }
+
+    if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
+        continue;
+    }
+
     // XXX: it would be better not to do this everytime round the loop
     nsAutoString adapterVendorID;
     nsAutoString adapterDeviceID;
     nsAutoString adapterDriverVersionString;
     if (info[i].mGpu2) {
       if (NS_FAILED(GetAdapterVendorID2(adapterVendorID)) ||
           NS_FAILED(GetAdapterDeviceID2(adapterDeviceID)) ||
           NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString)))
@@ -633,27 +708,16 @@ GfxInfoBase::FindBlocklistedDeviceInList
       }
     }
 
 #if defined(XP_WIN) || defined(ANDROID)
     uint64_t driverVersion;
     ParseDriverVersion(adapterDriverVersionString, &driverVersion);
 #endif
 
-
-    if (info[i].mOperatingSystem != DRIVER_OS_ALL &&
-        info[i].mOperatingSystem != os)
-    {
-      continue;
-    }
-
-    if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
-        continue;
-    }
-
     if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
         !info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
       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++) {
@@ -1014,16 +1078,34 @@ nsresult GfxInfoBase::GetInfo(JSContext*
   if (!obj.mOk) {
     return NS_ERROR_FAILURE;
   }
 
   aResult.setObject(*obj.mObj);
   return NS_OK;
 }
 
+const nsCString&
+GfxInfoBase::GetApplicationVersion()
+{
+  static nsAutoCString version;
+  static bool versionInitialized = false;
+  if (!versionInitialized) {
+    // If we fail to get the version, we will not try again.
+    versionInitialized = true;
+
+    // Get the version from xpcom/system/nsIXULAppInfo.idl
+    nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
+    if (app) {
+      app->GetVersion(version);
+    }
+  }
+  return version;
+}
+
 void
 GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector)
 {
   InitCollectors();
   sCollectors->AppendElement(collector);
 }
 
 void
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -77,16 +77,19 @@ public:
   static bool mDriverInfoObserverInitialized;
 
   virtual nsString Model() { return EmptyString(); }
   virtual nsString Hardware() { return EmptyString(); }
   virtual nsString Product() { return EmptyString(); }
   virtual nsString Manufacturer() { return EmptyString(); }
   virtual uint32_t OperatingSystemVersion() { return 0; }
 
+  // Convenience to get the application version
+  static const nsCString& GetApplicationVersion();
+
 protected:
 
   virtual ~GfxInfoBase();
 
   virtual nsresult GetFeatureStatusImpl(int32_t aFeature, int32_t* aStatus,
                                         nsAString& aSuggestedDriverVersion,
                                         const nsTArray<GfxDriverInfo>& aDriverInfo,
                                         OperatingSystem* aOS = nullptr);