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
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__ */