Bug 586046. Export graphics card/driver details to script using GfxInfo. r=gavin
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Fri, 27 Aug 2010 11:49:02 -0400
changeset 51578 0866f70e0bd9495d3882a39f52a0068b00ab5752
parent 51577 e3fe95511fc288d39bd7fc93f33eaa6b90421665
child 51579 1c724924c92d1d132d2b00c0a224a827e0122c1e
push idunknown
push userunknown
push dateunknown
reviewersgavin
bugs586046
milestone2.0b5pre
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 586046. Export graphics card/driver details to script using GfxInfo. r=gavin
toolkit/content/aboutSupport.js
toolkit/content/aboutSupport.xhtml
toolkit/locales/en-US/chrome/global/aboutSupport.dtd
toolkit/locales/en-US/chrome/global/aboutSupport.properties
toolkit/locales/jar.mn
widget/public/nsIGfxInfo.idl
widget/src/windows/GfxInfo.cpp
widget/src/windows/GfxInfo.h
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -136,24 +136,73 @@ function populatePreferencesSection() {
     let tr = createParentElement("tr", [tdName, tdValue]);
     trPrefs.push(tr);
   });
 
   appendChildren(document.getElementById("prefs-tbody"), trPrefs);
 }
 
 function populateGraphicsSection() {
+  function createHeader(name)
+  {
+    let elem = createElement("th", name);
+    elem.className = "column";
+    return elem;
+  }
+  
   try {
     // nsIGfxInfo is currently only implemented on Windows
-    var d2d = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).D2DEnabled;
+    let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+    let trGraphics = [];
+    var SBS = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+    var bundle = SBS.createBundle("chrome://global/locale/aboutSupport.properties");
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("adapterDescription")),
+      createElement("td", gfxInfo.adapterDescription),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("adapterVendorID")),
+      // pad with zeros. (printf would be nicer)
+      createElement("td", String('0000'+gfxInfo.adapterVendorID.toString(16)).slice(-4)),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("adapterDeviceID")),
+      // pad with zeros. (printf would be nicer)
+      createElement("td", String('0000'+gfxInfo.adapterDeviceID.toString(16)).slice(-4)),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("adapterRAM")),
+      createElement("td", gfxInfo.adapterRAM),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("adapterDrivers")),
+      createElement("td", gfxInfo.adapterDriver),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("driverVersion")),
+      createElement("td", gfxInfo.adapterDriverVersion),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("driverDate")),
+      createElement("td", gfxInfo.adapterDriverDate),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("direct2DEnabled")),
+      createElement("td", gfxInfo.D2DEnabled),
+    ]));
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("directWriteEnabled")),
+      createElement("td", gfxInfo.DWriteEnabled),
+    ]));
+
+    appendChildren(document.getElementById("graphics-tbody"), trGraphics);
+
   } catch (e) {
-    d2d = false;
   }
 
-  document.getElementById("direct2d").textContent = d2d;
 }
 
 
 function formatPrefValue(prefValue) {
   // Some pref values are really long and don't have spaces.  This can cause
   // problems when copying and pasting into some WYSIWYG editors.  In general
   // the exact contents of really long pref values aren't particularly useful,
   // so we truncate them to some reasonable length.
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -186,25 +186,17 @@
       </table>
 
       <!-- - - - - - - - - - - - - - - - - - - - - -->
       <h2 class="major-section">
         &aboutSupport.graphicsTitle;
       </h2>
       
       <table>
-        <tbody>
-          <tr>
-            <th class="column">
-              &aboutSupport.graphicsDirect2DEnabled;
-            </th>
-
-            <td id="direct2d">
-            </td>
-          </tr>
+        <tbody id="graphics-tbody">
         </tbody>
       </table>
 
     </div>
 
   </body>
 
 </html>
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
@@ -27,15 +27,13 @@ variant of aboutSupport.show.label.  Thi
 "Finder" terminology on Mac. -->
 <!ENTITY aboutSupport.showMac.label "Show in Finder">
 
 <!ENTITY aboutSupport.modifiedPrefsTitle "Modified Preferences">
 <!ENTITY aboutSupport.modifiedPrefsName "Name">
 <!ENTITY aboutSupport.modifiedPrefsValue "Value">
 
 <!ENTITY aboutSupport.graphicsTitle "Graphics">
-<!-- LOCALIZATION NOTE In the following string, "Direct2D" is a proper noun and should not be translated -->
-<!ENTITY aboutSupport.graphicsDirect2DEnabled "Direct2D Enabled">
 
 <!ENTITY aboutSupport.installationHistoryTitle "Installation History">
 <!ENTITY aboutSupport.updateHistoryTitle "Update History">
 
 <!ENTITY aboutSupport.copyToClipboard.label "Copy all to clipboard">
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -0,0 +1,12 @@
+# LOCALIZATION NOTE In the following string, "Direct2D" is a proper noun and should not be translated.
+# Feel free to leave english strings if there are no good translations, these are only used in about:support
+
+direct2DEnabled = Direct2D Enabled
+directWriteEnabled = DirectWrite Enabled
+adapterDescription = Adapter Description
+adapterVendorID = Vendor ID
+adapterDeviceID = Device ID
+adapterDrivers = Adapter Drivers
+adapterRAM = Adapter RAM
+driverVersion = Driver Version
+driverDate = Driver Date
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -2,16 +2,17 @@
 
 @AB_CD@.jar:
 % locale global @AB_CD@ %locale/@AB_CD@/global/
   locale/@AB_CD@/global/about.dtd                       (%chrome/global/about.dtd)
   locale/@AB_CD@/global/aboutAbout.dtd                  (%chrome/global/aboutAbout.dtd)
   locale/@AB_CD@/global/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/global/aboutRights.properties          (%chrome/global/aboutRights.properties)
   locale/@AB_CD@/global/aboutSupport.dtd                (%chrome/global/aboutSupport.dtd)
+  locale/@AB_CD@/global/aboutSupport.properties         (%chrome/global/aboutSupport.properties)
   locale/@AB_CD@/global/actions.dtd                     (%chrome/global/actions.dtd)
   locale/@AB_CD@/global/appPicker.dtd                   (%chrome/global/appPicker.dtd)
   locale/@AB_CD@/global/brand.dtd                       (generic/chrome/global/brand.dtd)
 + locale/@AB_CD@/global/browser.properties              (%chrome/global/browser.properties)
 + locale/@AB_CD@/global/charsetOverlay.dtd              (%chrome/global/charsetOverlay.dtd)
 + locale/@AB_CD@/global/commonDialog.dtd                (%chrome/global/commonDialog.dtd)
 + locale/@AB_CD@/global/commonDialogs.properties        (%chrome/global/commonDialogs.properties)
 + locale/@AB_CD@/global/config.dtd                      (%chrome/global/config.dtd)
--- a/widget/public/nsIGfxInfo.idl
+++ b/widget/public/nsIGfxInfo.idl
@@ -35,15 +35,37 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 /* NOTE: this interface is completely undesigned, not stable and likely to change */
 
-[scriptable, uuid(5C05E5B7-0FCC-4070-BF6B-58BC33ECA427)]
+[scriptable, uuid(d2bfa0fd-8f73-4660-9609-f999680243b0)]
 interface nsIGfxInfo : nsISupports
 {
   readonly attribute boolean D2DEnabled;
   readonly attribute boolean DWriteEnabled;
+  
+  /**
+   * The name of the display adapter.
+   */
+  readonly attribute DOMString adapterDescription;
+
+  readonly attribute DOMString adapterDriver;
+  
+  /* These types are inspired by DXGI_ADAPTER_DESC */
+  readonly attribute unsigned long adapterVendorID;
+  
+  readonly attribute unsigned long adapterDeviceID;
+  
+  /**
+   * The amount of RAM in MB in the display adapter.
+   */
+  readonly attribute DOMString adapterRAM;
+  
+  readonly attribute DOMString adapterDriverVersion;
+  
+  readonly attribute DOMString adapterDriverDate;
+  
 };
 
--- a/widget/src/windows/GfxInfo.cpp
+++ b/widget/src/windows/GfxInfo.cpp
@@ -15,41 +15,265 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *   Jonathan Griffin <jgriffin@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include <windows.h>
 #include "gfxWindowsPlatform.h"
 #include "GfxInfo.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/FunctionTimer.h"
 
 using namespace mozilla::widget;
 
 NS_IMPL_ISUPPORTS1(GfxInfo, nsIGfxInfo)
 
 nsresult GfxInfo::GetD2DEnabled(PRBool *aEnabled)
 {
   *aEnabled = gfxWindowsPlatform::GetPlatform()->GetRenderMode() == gfxWindowsPlatform::RENDER_DIRECT2D;
   return NS_OK;
 }
 
 nsresult GfxInfo::GetDWriteEnabled(PRBool *aEnabled)
 {
   *aEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
   return NS_OK;
 }
+
+/* XXX: GfxInfo doesn't handle multiple GPUs. We should try to do that. Bug #591057 */
+
+static nsresult GetKeyValue(const TCHAR* keyLocation, const TCHAR* keyName, nsAString& destString, int type)
+{
+  HKEY key;
+  DWORD dwcbData;
+  WCHAR wCharValue[1024];
+  TCHAR tCharValue[1024];
+  DWORD dValue;
+  LONG result;
+  nsresult retval = NS_OK;
+
+  result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
+  if (result != ERROR_SUCCESS) {
+    return NS_ERROR_FAILURE;
+  }
+
+  switch (type) {
+    case REG_DWORD: {
+      // We only use this for vram size
+      dwcbData = sizeof(dValue);
+      result = RegQueryValueExW(key, keyName, NULL, NULL, (LPBYTE)&dValue, &dwcbData);
+      if (result != ERROR_SUCCESS) {
+        retval = NS_ERROR_FAILURE;
+      }
+      dValue = dValue / 1024 / 1024;
+      destString.AppendInt(static_cast<PRInt32>(dValue));
+      break;
+    }
+    case REG_MULTI_SZ: {
+      // A chain of null-separated strings; we convert the nulls to spaces
+      dwcbData = sizeof(tCharValue);
+      result = RegQueryValueExW(key, keyName, NULL, NULL, (LPBYTE)tCharValue, &dwcbData);
+      if (result != ERROR_SUCCESS) {
+        retval = NS_ERROR_FAILURE;
+      }
+      // This bit here could probably be cleaner.
+      for (DWORD i = 0, len = dwcbData/sizeof(tCharValue[0]); i < len; i++) {
+        if (tCharValue[i] == '\0')
+          tCharValue[i] = ' ';
+      }
+      destString = tCharValue;
+      break;
+    }
+  }
+  RegCloseKey(key);
+
+  return retval;
+}
+
+// The driver ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD, possibly
+// followed by &REV_XXXX.  We uppercase the string, and strip the &REV_ part
+// from it, if found.
+static void normalizeDriverId(nsString& driverid) {
+  ToUpperCase(driverid);
+  PRInt32 rev = driverid.Find(NS_LITERAL_CSTRING("&REV_"));
+  if (rev != -1) {
+    driverid.Cut(rev, driverid.Length());
+  }
+}
+
+
+
+/* Other interesting places for info:
+ *   IDXGIAdapter::GetDesc()
+ *   IDirectDraw7::GetAvailableVidMem()
+ *   e->GetAvailableTextureMem()
+ * */
+
+#define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
+void GfxInfo::Init()
+{
+  NS_TIME_FUNCTION;
+
+  DISPLAY_DEVICE lpDisplayDevice;
+  lpDisplayDevice.cb = sizeof(lpDisplayDevice);
+  int deviceIndex = 0;
+
+  while (EnumDisplayDevices(NULL, deviceIndex, &lpDisplayDevice, 0)) {
+    if (lpDisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+      break;
+    deviceIndex++;
+  }
+
+  /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
+  if (wcsncmp(lpDisplayDevice.DeviceKey, DEVICE_KEY_PREFIX, wcslen(DEVICE_KEY_PREFIX)) != 0)
+    return;
+
+  // make sure the string is NULL terminated
+  size_t i;
+  for (i = 0; i < sizeof(lpDisplayDevice.DeviceKey); i++) {
+    if (lpDisplayDevice.DeviceKey[i] == L'\0')
+      break;
+  }
+
+  if (i == sizeof(lpDisplayDevice.DeviceKey)) {
+      // we did not find a NULL
+      return;
+  }
+
+  // chop off DEVICE_KEY_PREFIX
+  mDeviceKey = lpDisplayDevice.DeviceKey + wcslen(DEVICE_KEY_PREFIX);
+
+  mDeviceID = lpDisplayDevice.DeviceID;
+  mDeviceString = lpDisplayDevice.DeviceString;
+
+
+  HKEY key, subkey;
+  LONG result, enumresult;
+  DWORD index = 0;
+  TCHAR subkeyname[64];
+  TCHAR value[128];
+  DWORD dwcbData = sizeof(subkeyname);
+
+  // "{4D36E968-E325-11CE-BFC1-08002BE10318}" is the display class
+  result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                        L"System\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}", 
+                        0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &key);
+  if (result != ERROR_SUCCESS) {
+    return;
+  }
+
+  nsAutoString wantedDriverId(mDeviceID);
+  normalizeDriverId(wantedDriverId);
+
+  while ((enumresult = RegEnumKeyExW(key, index, subkeyname, &dwcbData, NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS) {
+    result = RegOpenKeyExW(key, subkeyname, 0, KEY_QUERY_VALUE, &subkey);
+    if (result == ERROR_SUCCESS) {
+      dwcbData = sizeof(value);
+      result = RegQueryValueExW(subkey, L"MatchingDeviceId", NULL, NULL, (LPBYTE)value, &dwcbData);
+      if (result == ERROR_SUCCESS) {
+        nsAutoString matchingDeviceId(value);
+        normalizeDriverId(matchingDeviceId);
+        if (wantedDriverId.Find(matchingDeviceId) > -1) {
+          /* we've found the driver we're looking for */
+          result = RegQueryValueExW(subkey, L"DriverVersion", NULL, NULL, (LPBYTE)value, &dwcbData);
+          if (result == ERROR_SUCCESS)
+            mDriverVersion = value;
+          result = RegQueryValueExW(subkey, L"DriverDate", NULL, NULL, (LPBYTE)value, &dwcbData);
+          if (result == ERROR_SUCCESS)
+            mDriverDate = value;
+          break;
+        }
+      }
+      RegCloseKey(subkey);
+    }
+    index++;
+    dwcbData = sizeof(subkeyname);
+  }
+
+  RegCloseKey(key);
+}
+
+/* readonly attribute DOMString adapterDescription; */
+NS_IMETHODIMP GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription)
+{
+  aAdapterDescription = mDeviceString;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString adapterRAM; */
+NS_IMETHODIMP GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM)
+{
+  if (NS_FAILED(GetKeyValue(mDeviceKey.BeginReading(), L"HardwareInformation.MemorySize", aAdapterRAM, REG_DWORD)))
+    aAdapterRAM = L"Unknown";
+  return NS_OK;
+}
+
+/* readonly attribute DOMString adapterDriver; */
+NS_IMETHODIMP GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver)
+{
+  if (NS_FAILED(GetKeyValue(mDeviceKey.BeginReading(), L"InstalledDisplayDrivers", aAdapterDriver, REG_MULTI_SZ)))
+    aAdapterDriver = L"Unknown";
+  return NS_OK;
+}
+
+/* readonly attribute DOMString adapterDriverVersion; */
+NS_IMETHODIMP GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion)
+{
+  aAdapterDriverVersion = mDriverVersion;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString adapterDriverDate; */
+NS_IMETHODIMP GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate)
+{
+  aAdapterDriverDate = mDriverDate;
+  return NS_OK;
+}
+
+/* readonly attribute unsigned long adapterVendorID; */
+NS_IMETHODIMP GfxInfo::GetAdapterVendorID(PRUint32 *aAdapterVendorID)
+{
+  nsAutoString vendor(mDeviceID);
+  ToUpperCase(vendor);
+  PRInt32 start = vendor.Find(NS_LITERAL_CSTRING("VEN_"));
+  if (start != -1) {
+    vendor.Cut(0, start + strlen("VEN_"));
+    vendor.Truncate(4);
+  }
+  nsresult err;
+  *aAdapterVendorID = vendor.ToInteger(&err, 16);
+  return NS_OK;
+}
+
+/* readonly attribute unsigned long adapterDeviceID; */
+NS_IMETHODIMP GfxInfo::GetAdapterDeviceID(PRUint32 *aAdapterDeviceID)
+{
+  nsAutoString device(mDeviceID);
+  ToUpperCase(device);
+  PRInt32 start = device.Find(NS_LITERAL_CSTRING("&DEV_"));
+  if (start != -1) {
+    device.Cut(0, start + strlen("&DEV_"));
+    device.Truncate(4);
+  }
+  nsresult err;
+  *aAdapterDeviceID = device.ToInteger(&err, 16);
+  return NS_OK;
+}
--- a/widget/src/windows/GfxInfo.h
+++ b/widget/src/windows/GfxInfo.h
@@ -43,20 +43,27 @@
 #include <nsIGfxInfo.h>
 
 namespace mozilla {
 namespace widget {
 
 class GfxInfo : public nsIGfxInfo
 {
 public:
-  GfxInfo() {}
+  GfxInfo() {Init();}
   virtual ~GfxInfo() {}
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIGFXINFO
+private:
 
+  void Init();
+  nsString mDeviceString;
+  nsString mDeviceID;
+  nsString mDriverVersion;
+  nsString mDriverDate;
+  nsString mDeviceKey;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* __mozilla_widget_GfxInfo_h__ */