Bug 789674 - Update about:support to use Troubleshoot.jsm. r=felipe
authorDrew Willcoxon <adw@mozilla.com>
Tue, 18 Sep 2012 13:30:41 -0700
changeset 107530 83925e07123566c5fa002d9d9551432e52ae2c5c
parent 107529 2ec1b483d19baaa8138d40e65ab02dcb8ca37ee7
child 107531 e1850b0b8d4a61bf782c7a883527088f61be41c8
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersfelipe
bugs789674
milestone18.0a1
Bug 789674 - Update about:support to use Troubleshoot.jsm. r=felipe
toolkit/content/aboutSupport.js
toolkit/content/aboutSupport.xhtml
toolkit/locales/en-US/chrome/global/aboutSupport.dtd
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -1,521 +1,256 @@
-# -*- Mode: js2; indent-tabs-mode: nil; js2-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/.
+/* 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Troubleshoot.jsm");
+
+window.addEventListener("load", function onload(event) {
+  window.removeEventListener("load", onload, false);
+  Troubleshoot.snapshot(function (snapshot) {
+    for (let prop in snapshotFormatters)
+      snapshotFormatters[prop](snapshot[prop]);
+  });
+  populateResetBox();
+}, false);
 
-let Cc = Components.classes;
-let Ci = Components.interfaces;
-let Cu = Components.utils;
+// Each property in this object corresponds to a property in Troubleshoot.jsm's
+// snapshot data.  Each function is passed its property's corresponding data,
+// and it's the function's job to update the page with it.
+let snapshotFormatters = {
 
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
+  application: function application(data) {
+    $("application-box").textContent = data.name;
+    $("useragent-box").textContent = data.userAgent;
+    $("supportLink").href = data.supportURL;
+    let version = data.version;
+    if (data.vendor)
+      version += " (" + data.vendor + ")";
+    $("version-box").textContent = version;
+  },
 
-const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
-                                                Ci.nsIPrefLocalizedString).data;
+  extensions: function extensions(data) {
+    $.append($("extensions-tbody"), data.map(function (extension) {
+      return $.new("tr", [
+        $.new("td", extension.name),
+        $.new("td", extension.version),
+        $.new("td", extension.isActive),
+        $.new("td", extension.id),
+      ]);
+    }));
+  },
 
-// We use a preferences whitelist to make sure we only show preferences that
-// are useful for support and won't compromise the user's privacy.  Note that
-// entries are *prefixes*: for example, "accessibility." applies to all prefs
-// under the "accessibility.*" branch.
-const PREFS_WHITELIST = [
-  "accessibility.",
-  "browser.cache.",
-  "browser.display.",
-  "browser.fixup.",
-  "browser.history_expire_",
-  "browser.link.open_newwindow",
-  "browser.places.",
-  "browser.privatebrowsing.",
-  "browser.search.context.loadInBackground",
-  "browser.search.log",
-  "browser.search.openintab",
-  "browser.search.param",
-  "browser.search.searchEnginesURL",
-  "browser.search.suggest.enabled",
-  "browser.search.update",
-  "browser.search.useDBForOrder",
-  "browser.sessionstore.",
-  "browser.startup.homepage",
-  "browser.tabs.",
-  "browser.zoom.",
-  "dom.",
-  "extensions.checkCompatibility",
-  "extensions.lastAppVersion",
-  "font.",
-  "general.autoScroll",
-  "general.useragent.",
-  "gfx.",
-  "html5.",
-  "image.mem.",
-  "javascript.",
-  "keyword.",
-  "layers.",
-  "layout.css.dpi",
-  "media.",
-  "mousewheel.",
-  "network.",
-  "permissions.default.image",
-  "places.",
-  "plugin.",
-  "plugins.",
-  "print.",
-  "privacy.",
-  "security.",
-  "social.active",
-  "social.enabled",
-  "svg.",
-  "toolkit.startup.recent_crashes",
-  "webgl."
-];
+  modifiedPreferences: function modifiedPreferences(data) {
+    $.append($("prefs-tbody"), sortedArrayFromObject(data).map(
+      function ([name, value]) {
+        return $.new("tr", [
+          $.new("td", name, "pref-name"),
+          // Very long preference values can cause users problems when they
+          // copy and paste them into some text editors.  Long values generally
+          // aren't useful anyway, so truncate them to a reasonable length.
+          $.new("td", String(value).substr(0, 120), "pref-value"),
+        ]);
+      }
+    ));
+  },
 
-// The blacklist, unlike the whitelist, is a list of regular expressions.
-const PREFS_BLACKLIST = [
-  /^network[.]proxy[.]/,
-  /[.]print_to_filename$/,
-];
+  graphics: function graphics(data) {
+    // graphics-info-properties tbody
+    if ("info" in data) {
+      let trs = sortedArrayFromObject(data.info).map(function ([prop, val]) {
+        return $.new("tr", [
+          $.new("th", prop, "column"),
+          $.new("td", String(val)),
+        ]);
+      });
+      $.append($("graphics-info-properties"), trs);
+      delete data.info;
+    }
 
-window.onload = function () {
-  // Get the support URL.
-  let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
-                       .getService(Ci.nsIURLFormatter);
-  let supportUrl = urlFormatter.formatURLPref("app.support.baseURL");
+    // graphics-failures-tbody tbody
+    if ("failures" in data) {
+      $.append($("graphics-failures-tbody"), data.failures.map(function (val) {
+        return $.new("tr", [$.new("td", val)]);
+      }));
+      delete data.failures;
+    }
+
+    // graphics-tbody tbody
 
-  // Update the application basics section.
-  document.getElementById("application-box").textContent = Services.appinfo.name;
-  document.getElementById("useragent-box").textContent = navigator.userAgent;
-  document.getElementById("supportLink").href = supportUrl;
-  let version = Services.appinfo.version;
-  try {
-    version += " (" + Services.prefs.getCharPref("app.support.vendor") + ")";
-  } catch (e) {
-  }
-  document.getElementById("version-box").textContent = version;
+    function localizedMsg(msgArray) {
+      let nameOrMsg = msgArray.shift();
+      try {
+        return strings.formatStringFromName(nameOrMsg, msgArray,
+                                            msgArray.length);
+      }
+      catch (err) {}
+      return nameOrMsg;
+    }
+
+    let out = Object.create(data);
+    let strings = stringBundle();
 
-  // Update the other sections.
-  populateResetBox();
-  populatePreferencesSection();
-  populateExtensionsSection();
-  populateGraphicsSection();
-  populateJavaScriptSection();
-  populateAccessibilitySection();
-  populateLibVersionsSection();
-}
+    out.acceleratedWindows =
+      data.numAcceleratedWindows + "/" + data.numTotalWindows;
+    if (data.windowLayerManagerType)
+      out.acceleratedWindows += " " + data.windowLayerManagerType;
+    if (data.numAcceleratedWindowsMessage)
+      out.acceleratedWindows +=
+        " " + localizedMsg(data.numAcceleratedWindowsMessage);
+    delete data.numAcceleratedWindows;
+    delete data.numTotalWindows;
+    delete data.windowLayerManagerType;
+    delete data.numAcceleratedWindowsMessage;
 
-function populateExtensionsSection() {
-  AddonManager.getAddonsByTypes(["extension"], function(extensions) {
-    extensions.sort(function(a,b) {
-      if (a.isActive != b.isActive)
-        return b.isActive ? 1 : -1;
-      let lc = a.name.localeCompare(b.name);
-      if (lc != 0)
-        return lc;
-      if (a.version != b.version)
-        return a.version > b.version ? 1 : -1;
-      return 0;
-    });
-    let trExtensions = [];
-    for (let i = 0; i < extensions.length; i++) {
-      let extension = extensions[i];
-      let tr = createParentElement("tr", [
-        createElement("td", extension.name),
-        createElement("td", extension.version),
-        createElement("td", extension.isActive),
-        createElement("td", extension.id),
-      ]);
-      trExtensions.push(tr);
+    if ("direct2DEnabledMessage" in data) {
+      out.direct2DEnabled = localizedMsg(data.direct2DEnabledMessage);
+      delete data.direct2DEnabledMessage;
+      delete data.direct2DEnabled;
     }
-    appendChildren(document.getElementById("extensions-tbody"), trExtensions);
-  });
-}
 
-function populatePreferencesSection() {
-  let modifiedPrefs = getModifiedPrefs();
+    if ("directWriteEnabled" in data) {
+      out.directWriteEnabled = data.directWriteEnabled;
+      if ("directWriteVersion" in data)
+        out.directWriteEnabled += " (" + data.directWriteVersion + ")";
+      delete data.directWriteEnabled;
+      delete data.directWriteVersion;
+    }
+
+    if ("webglRendererMessage" in data) {
+      out.webglRenderer = localizedMsg(data.webglRendererMessage);
+      delete data.webglRendererMessage;
+      delete data.webglRenderer;
+    }
 
-  function comparePrefs(pref1, pref2) {
-    if (pref1.name < pref2.name)
-      return -1;
-    if (pref1.name > pref2.name)
-      return 1;
-    return 0;
-  }
-
-  let sortedPrefs = modifiedPrefs.sort(comparePrefs);
+    let localizedOut = {};
+    for (let prop in out) {
+      let val = out[prop];
+      if (typeof(val) == "string" && !val)
+        // Ignore properties that are empty strings.
+        continue;
+      try {
+        var localizedName = strings.GetStringFromName(prop);
+      }
+      catch (err) {
+        // This shouldn't happen, but if there's a reported graphics property
+        // that isn't in the string bundle, don't let it break the page.
+        localizedName = prop;
+      }
+      localizedOut[localizedName] = val;
+    }
+    let trs = sortedArrayFromObject(localizedOut).map(function ([prop, val]) {
+      return $.new("tr", [
+        $.new("th", prop, "column"),
+        $.new("td", val),
+      ]);
+    });
+    $.append($("graphics-tbody"), trs);
+  },
 
-  let trPrefs = [];
-  sortedPrefs.forEach(function (pref) {
-    let tdName = createElement("td", pref.name, "pref-name");
-    let tdValue = createElement("td", formatPrefValue(pref.value), "pref-value");
-    let tr = createParentElement("tr", [tdName, tdValue]);
-    trPrefs.push(tr);
-  });
+  javaScript: function javaScript(data) {
+    $("javascript-incremental-gc").textContent = data.incrementalGCEnabled;
+  },
 
-  appendChildren(document.getElementById("prefs-tbody"), trPrefs);
-}
+  accessibility: function accessibility(data) {
+    $("a11y-activated").textContent = data.isActive;
+    $("a11y-force-disabled").textContent = data.forceDisabled || 0;
+  },
 
-function populateLibVersionsSection() {
-  function pushInfoRow(table, name, value, value2)
-  {
-    table.push(createParentElement("tr", [
-      createElement("td", name),
-      createElement("td", value),
-      createElement("td", value2),
-    ]));
-  }
-    
-  var v = null;
-  try { // just to be safe
-    v = Cc["@mozilla.org/security/nssversion;1"].getService(Ci.nsINSSVersion);
-  } catch(e) {}
-  if (!v)
-    return;
-    
-  let bundle = Services.strings.createBundle("chrome://global/locale/aboutSupport.properties");
-  let libversions_tbody = document.getElementById("libversions-tbody");
+  libraryVersions: function libraryVersions(data) {
+    let strings = stringBundle();
+    let trs = [
+      $.new("tr", [
+        $.new("th", ""),
+        $.new("th", strings.GetStringFromName("minLibVersions")),
+        $.new("th", strings.GetStringFromName("loadedLibVersions")),
+      ])
+    ];
+    sortedArrayFromObject(data).forEach(
+      function ([name, val]) {
+        trs.push($.new("tr", [
+          $.new("td", name),
+          $.new("td", val.minVersion),
+          $.new("td", val.version),
+        ]));
+      }
+    );
+    $.append($("libversions-tbody"), trs);
+  },
+};
 
-  let trLibs = [];
-  trLibs.push(createParentElement("tr", [
-    createElement("th", ""),
-    createElement("th", bundle.GetStringFromName("minLibVersions")),
-    createElement("th", bundle.GetStringFromName("loadedLibVersions")),
-  ]));
-  pushInfoRow(trLibs, "NSPR", v.NSPR_MinVersion, v.NSPR_Version);
-  pushInfoRow(trLibs, "NSS", v.NSS_MinVersion, v.NSS_Version);
-  pushInfoRow(trLibs, "NSS Util", v.NSSUTIL_MinVersion, v.NSSUTIL_Version);
-  pushInfoRow(trLibs, "NSS SSL", v.NSSSSL_MinVersion, v.NSSSSL_Version);
-  pushInfoRow(trLibs, "NSS S/MIME", v.NSSSMIME_MinVersion, v.NSSSMIME_Version);
+let $ = document.getElementById.bind(document);
 
-  appendChildren(libversions_tbody, trLibs);
+$.new = function $_new(tag, textContentOrChildren, className) {
+  let elt = document.createElement(tag);
+  if (className)
+    elt.className = className;
+  if (Array.isArray(textContentOrChildren))
+    this.append(elt, textContentOrChildren);
+  else
+    elt.textContent = String(textContentOrChildren);
+  return elt;
+};
+
+$.append = function $_append(parent, children) {
+  children.forEach(function (c) parent.appendChild(c));
+};
+
+function stringBundle() {
+  return Services.strings.createBundle(
+           "chrome://global/locale/aboutSupport.properties");
 }
 
-function populateGraphicsSection() {
-  function createHeader(name)
-  {
-    let elem = createElement("th", name);
-    elem.className = "column";
-    return elem;
-  }
-
-  function pushInfoRow(table, name, value)
-  {
-    if(value) {
-      table.push(createParentElement("tr", [
-        createHeader(bundle.GetStringFromName(name)),
-        createElement("td", value),
-      ]));
-    }
-  }
-  
-  function pushLiteralInfoRow(table, name, value)
-  {
-    table.push(createParentElement("tr", [
-      createHeader(name),
-      createElement("td", value),
-    ]));
-  }
-
-  function errorMessageForFeature(feature) {
-    var errorMessage;
-    var status;
-    try {
-      status = gfxInfo.getFeatureStatus(feature);
-    } catch(e) {}
-    switch (status) {
-      case gfxInfo.FEATURE_BLOCKED_DEVICE:
-      case gfxInfo.FEATURE_DISCOURAGED:
-        errorMessage = bundle.GetStringFromName("blockedGfxCard");
-        break;
-      case gfxInfo.FEATURE_BLOCKED_OS_VERSION:
-        errorMessage = bundle.GetStringFromName("blockedOSVersion");
-        break;
-      case gfxInfo.FEATURE_BLOCKED_DRIVER_VERSION:
-        var suggestedDriverVersion;
-        try {
-          suggestedDriverVersion = gfxInfo.getFeatureSuggestedDriverVersion(feature);
-        } catch(e) {}
-        if (suggestedDriverVersion)
-          errorMessage = bundle.formatStringFromName("tryNewerDriver", [suggestedDriverVersion], 1);
-        else
-          errorMessage = bundle.GetStringFromName("blockedDriver");
-        break;
-    }
-    return errorMessage;
-  }
-
-  function pushFeatureInfoRow(table, name, feature, isEnabled, message) {
-    message = message || isEnabled;
-    if (!isEnabled) {
-      var errorMessage = errorMessageForFeature(feature);
-      if (errorMessage)
-        message = errorMessage;
-    }
-    table.push(createParentElement("tr", [
-      createHeader(bundle.GetStringFromName(name)),
-      createElement("td", message),
-    ]));
-  }
-
-  function hexValueToString(value)
-  {
-    return value
-           ? String('0000' + value.toString(16)).slice(-4)
-           : null;
-  }
-
-  let bundle = Services.strings.createBundle("chrome://global/locale/aboutSupport.properties");
-  let graphics_tbody = document.getElementById("graphics-tbody");
-
-  var gfxInfo = null;
-  try {
-    // nsIGfxInfo is currently only implemented on Windows
-    gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
-  } catch(e) {}
-
-  if (gfxInfo) {
-    let trGraphics = [];
-    pushInfoRow(trGraphics, "adapterDescription", gfxInfo.adapterDescription);
-    pushInfoRow(trGraphics, "adapterVendorID", gfxInfo.adapterVendorID);
-    pushInfoRow(trGraphics, "adapterDeviceID", gfxInfo.adapterDeviceID);
-    pushInfoRow(trGraphics, "adapterRAM", gfxInfo.adapterRAM);
-    pushInfoRow(trGraphics, "adapterDrivers", gfxInfo.adapterDriver);
-    pushInfoRow(trGraphics, "driverVersion", gfxInfo.adapterDriverVersion);
-    pushInfoRow(trGraphics, "driverDate", gfxInfo.adapterDriverDate);
-
-#ifdef XP_WIN
-    pushInfoRow(trGraphics, "adapterDescription2", gfxInfo.adapterDescription2);
-    pushInfoRow(trGraphics, "adapterVendorID2", gfxInfo.adapterVendorID2);
-    pushInfoRow(trGraphics, "adapterDeviceID2", gfxInfo.adapterDeviceID2);
-    pushInfoRow(trGraphics, "adapterRAM2", gfxInfo.adapterRAM2);
-    pushInfoRow(trGraphics, "adapterDrivers2", gfxInfo.adapterDriver2);
-    pushInfoRow(trGraphics, "driverVersion2", gfxInfo.adapterDriverVersion2);
-    pushInfoRow(trGraphics, "driverDate2", gfxInfo.adapterDriverDate2);
-    pushInfoRow(trGraphics, "isGPU2Active", gfxInfo.isGPU2Active);
-
-    var version = Cc["@mozilla.org/system-info;1"]
-                  .getService(Ci.nsIPropertyBag2)
-                  .getProperty("version");
-    var isWindowsVistaOrHigher = (parseFloat(version) >= 6.0);
-    if (isWindowsVistaOrHigher) {
-      var d2dEnabled = "false";
-      try {
-        d2dEnabled = gfxInfo.D2DEnabled;
-      } catch(e) {}
-      pushFeatureInfoRow(trGraphics, "direct2DEnabled", gfxInfo.FEATURE_DIRECT2D, d2dEnabled);
-
-      var dwEnabled = "false";
-      try {
-        dwEnabled = gfxInfo.DWriteEnabled + " (" + gfxInfo.DWriteVersion + ")";
-      } catch(e) {}
-      pushInfoRow(trGraphics, "directWriteEnabled", dwEnabled);  
-
-      var cleartypeParams = "";
-      try {
-        cleartypeParams = gfxInfo.cleartypeParameters;
-      } catch(e) {
-        cleartypeParams = bundle.GetStringFromName("clearTypeParametersNotFound");
-      }
-      pushInfoRow(trGraphics, "clearTypeParameters", cleartypeParams);  
-    }
-
-#endif
-
-    var webglrenderer;
-    var webglenabled;
-    try {
-      webglrenderer = gfxInfo.getWebGLParameter("full-renderer");
-      webglenabled = true;
-    } catch (e) {
-      webglrenderer = false;
-      webglenabled = false;
-    }
-#ifdef XP_WIN
-    // If ANGLE is not available but OpenGL is, we want to report on the OpenGL feature, because that's what's going to get used.
-    // In all other cases we want to report on the ANGLE feature.
-    var webglfeature = gfxInfo.FEATURE_WEBGL_ANGLE;
-    if (gfxInfo.getFeatureStatus(gfxInfo.FEATURE_WEBGL_ANGLE)  != gfxInfo.FEATURE_NO_INFO &&
-        gfxInfo.getFeatureStatus(gfxInfo.FEATURE_WEBGL_OPENGL) == gfxInfo.FEATURE_NO_INFO)
-      webglfeature = gfxInfo.FEATURE_WEBGL_OPENGL;
-#else
-    var webglfeature = gfxInfo.FEATURE_WEBGL_OPENGL;
-#endif
-    pushFeatureInfoRow(trGraphics, "webglRenderer", webglfeature, webglenabled, webglrenderer);
-
-    appendChildren(graphics_tbody, trGraphics);
-    
-    // display registered graphics properties
-    let graphics_info_properties = document.getElementById("graphics-info-properties");
-    var info = gfxInfo.getInfo();
-    let trGraphicsProperties = [];
-    for (var property in info) {
-      pushLiteralInfoRow(trGraphicsProperties, property, info[property]);
-    }
-    appendChildren(graphics_info_properties, trGraphicsProperties);
-   
-    // display any failures that have occurred
-    let graphics_failures_tbody = document.getElementById("graphics-failures-tbody");
-    let trGraphicsFailures = gfxInfo.getFailures().map(function (value)
-        createParentElement("tr", [
-            createElement("td", value)
-        ])
-    );
-    appendChildren(graphics_failures_tbody, trGraphicsFailures);
-
-
-
-  } // end if (gfxInfo)
-
-  let windows = Services.ww.getWindowEnumerator();
-  let acceleratedWindows = 0;
-  let totalWindows = 0;
-  let mgrType;
-  while (windows.hasMoreElements()) {
-    totalWindows++;
-
-    let awindow = windows.getNext().QueryInterface(Ci.nsIInterfaceRequestor);
-    let windowutils = awindow.getInterface(Ci.nsIDOMWindowUtils);
-    if (windowutils.layerManagerType != "Basic") {
-      acceleratedWindows++;
-      mgrType = windowutils.layerManagerType;
-    }
-  }
-
-  let msg = acceleratedWindows;
-  if (acceleratedWindows) {
-    msg += "/" + totalWindows + " " + mgrType;
-  } else {
-#ifdef XP_WIN
-    var feature = gfxInfo.FEATURE_DIRECT3D_9_LAYERS;
-#else
-    var feature = gfxInfo.FEATURE_OPENGL_LAYERS;
-#endif
-    var errMsg = errorMessageForFeature(feature);
-    if (errMsg)
-      msg += ". " + errMsg;
-  }
-
-  appendChildren(graphics_tbody, [
-    createParentElement("tr", [
-      createHeader(bundle.GetStringFromName("acceleratedWindows")),
-      createElement("td", msg),
-    ])
-  ]);
+function sortedArrayFromObject(obj) {
+  let tuples = [];
+  for (let prop in obj)
+    tuples.push([prop, obj[prop]]);
+  tuples.sort(function ([prop1, v1], [prop2, v2]) prop1.localeCompare(prop2));
+  return tuples;
 }
 
-function populateJavaScriptSection() {
-  let enabled = window.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindowUtils)
-        .isIncrementalGCEnabled();
-  document.getElementById("javascript-incremental-gc").textContent = enabled ? "1" : "0";
-}
-
-function populateAccessibilitySection() {
-  var active;
+function copyRawDataToClipboard(button) {
+  if (button)
+    button.disabled = true;
   try {
-    active = Components.manager.QueryInterface(Ci.nsIServiceManager)
-      .isServiceInstantiatedByContractID(
-        "@mozilla.org/accessibilityService;1",
-        Ci.nsISupports);
-  } catch (ex) {
-    active = false;
-  }
-
-  document.getElementById("a11y-activated").textContent = active ? "1" : "0";
-
-  var forceDisabled = 0;
-  forceDisabled = getPrefValue("accessibility.force_disabled").value;
-
-  document.getElementById("a11y-force-disabled").textContent
-    = (forceDisabled == -1) ? "never" :
-	((forceDisabled == 1) ? "1" : "0");
-}
-
-function getPrefValue(aName) {
-  let value = "";
-  let type = Services.prefs.getPrefType(aName);
-  switch (type) {
-    case Ci.nsIPrefBranch.PREF_STRING:
-      value = Services.prefs.getComplexValue(aName, Ci.nsISupportsString).data;
-      break;
-    case Ci.nsIPrefBranch.PREF_BOOL:
-      value = Services.prefs.getBoolPref(aName);
-      break;
-    case Ci.nsIPrefBranch.PREF_INT:
-      value = Services.prefs.getIntPref(aName);
-      break;
+    Troubleshoot.snapshot(function (snapshot) {
+      if (button)
+        button.disabled = false;
+      let str = Cc["@mozilla.org/supports-string;1"].
+                createInstance(Ci.nsISupportsString);
+      str.data = JSON.stringify(snapshot, undefined, 2);
+      let transferable = Cc["@mozilla.org/widget/transferable;1"].
+                         createInstance(Ci.nsITransferable);
+      transferable.init(getLoadContext());
+      transferable.addDataFlavor("text/unicode");
+      transferable.setTransferData("text/unicode", str, str.data.length * 2);
+      Cc["@mozilla.org/widget/clipboard;1"].
+        getService(Ci.nsIClipboard).
+        setData(transferable, null, Ci.nsIClipboard.kGlobalClipboard);
+    });
   }
-
-  return { name: aName, value: value };
-}
-
-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.
-  let maxPrefValueLen = 120;
-  let text = "" + prefValue;
-  if (text.length > maxPrefValueLen)
-    text = text.substring(0, maxPrefValueLen) + ELLIPSIS;
-  return text;
-}
-
-function getModifiedPrefs() {
-  // We use the low-level prefs API to identify prefs that have been
-  // modified, rather that Application.prefs.all since the latter is
-  // much, much slower.  Application.prefs.all also gets slower each
-  // time it's called.  See bug 517312.
-  let prefNames = getWhitelistedPrefNames();
-  let prefs = [getPrefValue(prefName)
-                      for each (prefName in prefNames)
-                          if (Services.prefs.prefHasUserValue(prefName)
-                            && !isBlacklisted(prefName))];
-  return prefs;
-}
-
-function getWhitelistedPrefNames() {
-  let results = [];
-  PREFS_WHITELIST.forEach(function (prefStem) {
-    let prefNames = Services.prefs.getChildList(prefStem);
-    results = results.concat(prefNames);
-  });
-  return results;
-}
-
-function isBlacklisted(prefName) {
-  return PREFS_BLACKLIST.some(function (re) re.test(prefName));
-}
-
-function createParentElement(tagName, childElems) {
-  let elem = document.createElement(tagName);
-  appendChildren(elem, childElems);
-  return elem;
-}
-
-function createElement(tagName, textContent, opt_class) {
-  let elem = document.createElement(tagName);
-  elem.textContent = textContent;
-  elem.className = opt_class || "";
-  return elem;
-}
-
-function appendChildren(parentElem, childNodes) {
-  for (let i = 0; i < childNodes.length; i++)
-    parentElem.appendChild(childNodes[i]);
+  catch (err) {
+    if (button)
+      button.disabled = false;
+    throw err;
+  }
 }
 
 function getLoadContext() {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIWebNavigation)
                .QueryInterface(Ci.nsILoadContext);
 }
 
 function copyContentsToClipboard() {
   // Get the HTML and text representations for the important part of the page.
-  let contentsDiv = document.getElementById("contents");
+  let contentsDiv = $("contents");
   let dataHtml = contentsDiv.innerHTML;
   let dataText = createTextForElement(contentsDiv);
 
   // We can't use plain strings, we have to use nsSupportsString.
   let supportsStringClass = Cc["@mozilla.org/supports-string;1"];
   let ssHtml = supportsStringClass.createInstance(Ci.nsISupportsString);
   let ssText = supportsStringClass.createInstance(Ci.nsISupportsString);
 
@@ -607,17 +342,17 @@ function openProfileDirectory() {
   new nsLocalFile(profileDir).reveal();
 }
 
 /**
  * Profile reset is only supported for the default profile if the appropriate migrator exists.
  */
 function populateResetBox() {
   if (resetSupported())
-    document.getElementById("reset-box").style.visibility = "visible";
+    $("reset-box").style.visibility = "visible";
 }
 
 /**
  * Restart the application to reset the profile.
  */
 function resetProfileAndRestart() {
   let branding = Services.strings.createBundle("chrome://branding/locale/brand.properties");
   let brandShortName = branding.GetStringFromName("brandShortName");
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -39,18 +39,22 @@
       &aboutSupport.pageTitle;
     </h1>
 
     <div class="page-subtitle">
         &aboutSupport.pageSubtitle;
     </div>
 
     <div>
+      <button id="copy-raw-data-to-clipboard"
+              onclick="copyRawDataToClipboard(this);">
+        &aboutSupport.copyRawDataToClipboard.label;
+      </button>
       <button id="copy-to-clipboard" onclick="copyContentsToClipboard()">
-        &aboutSupport.copyToClipboard.label;
+        &aboutSupport.copyTextToClipboard.label;
       </button>
     </div>
 
     <div id="contents">
 
       <!-- - - - - - - - - - - - - - - - - - - - - -->
 
       <h2 class="major-section">
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
@@ -55,9 +55,10 @@ variant of aboutSupport.showDir.label. -
 <!ENTITY aboutSupport.a11yActivated "Activated">
 <!ENTITY aboutSupport.a11yForceDisabled "Prevent Accessibility">
 
 <!ENTITY aboutSupport.libraryVersionsTitle "Library Versions">
 
 <!ENTITY aboutSupport.installationHistoryTitle "Installation History">
 <!ENTITY aboutSupport.updateHistoryTitle "Update History">
 
-<!ENTITY aboutSupport.copyToClipboard.label "Copy all to clipboard">
+<!ENTITY aboutSupport.copyTextToClipboard.label "Copy text to clipboard">
+<!ENTITY aboutSupport.copyRawDataToClipboard.label "Copy raw data to clipboard">