Bug 624108 - Land about:support in the tree. r=bwinton, sr=Standard8, ui-r=clarkbw
authorSiddharth Agarwal <sid.bugzilla@gmail.com>
Sat, 15 Jan 2011 00:30:37 +0530
changeset 6810 285ca4c14963a68aeadde9743306b2ca5dbd47cb
parent 6809 21421642db18e4c316eb9276e4a0e8f40190e75e
child 6811 8aceaf24369903ac240048fa32e112d6ee3f4d6f
push idunknown
push userunknown
push dateunknown
reviewersbwinton, Standard8, clarkbw
bugs624108
Bug 624108 - Land about:support in the tree. r=bwinton, sr=Standard8, ui-r=clarkbw about:support is a useful troubleshooting tool. The idea is to have a list of account settings, modified prefs and installed extensions in one spot, and to provide easy and safe means of communicating these settings to people who can help troubleshoot an issue.
mail/components/Makefile.in
mail/components/about-support/Makefile.in
mail/components/about-support/aboutSupport.js
mail/components/about-support/aboutSupportMac.js
mail/components/about-support/aboutSupportUnix.js
mail/components/about-support/aboutSupportWin32.js
mail/components/about-support/content/aboutSupport.xhtml
mail/components/about-support/content/accounts.js
mail/components/about-support/content/export.js
mail/components/about-support/content/extensions.js
mail/components/about-support/content/gfx.js
mail/components/about-support/content/hide-private.css
mail/components/about-support/content/init.js
mail/components/about-support/content/overlay.js
mail/components/about-support/content/overlay.xul
mail/components/about-support/content/prefs.js
mail/components/about-support/content/show-private.css
mail/components/about-support/jar.mn
mail/components/aboutRedirector.js
mail/components/mailComponents.manifest
mail/locales/en-US/chrome/messenger/aboutSupportMail.dtd
mail/locales/en-US/chrome/messenger/aboutSupportMail.properties
mail/locales/en-US/chrome/messenger/baseMenuOverlay.dtd
mail/locales/jar.mn
mail/test/mozmill/content-tabs/test-about-support.js
mail/test/mozmill/shared-modules/test-content-tab-helpers.js
mail/themes/gnomestripe/jar.mn
mail/themes/pinstripe/jar.mn
mail/themes/qute/jar.mn
mail/themes/qute/mail/aboutSupport.css
--- a/mail/components/Makefile.in
+++ b/mail/components/Makefile.in
@@ -39,17 +39,17 @@ DEPTH   = ../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # Only Mac and Windows have search integration components, but we include at
 # least one module from search/ on all platforms
-DIRS    = compose preferences addrbook migration activity search
+DIRS    = compose preferences addrbook migration activity search about-support
 
 ifneq (,$(filter windows gtk2 cocoa, $(MOZ_WIDGET_TOOLKIT)))
 DIRS += shell
 endif
 
 ifdef MOZ_SAFE_BROWSING
 DIRS += phishing 
 endif
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/Makefile.in
@@ -0,0 +1,21 @@
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_JS_MODULES = aboutSupport.js
+
+# Include the platform-specific module
+ifeq ($(MOZ_WIDGET_TOOLKIT), windows)
+EXTRA_JS_MODULES += aboutSupportWin32.js
+else
+ifeq ($(MOZ_WIDGET_TOOLKIT), cocoa)
+EXTRA_JS_MODULES += aboutSupportMac.js
+else
+EXTRA_JS_MODULES += aboutSupportUnix.js
+endif
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/aboutSupport.js
@@ -0,0 +1,186 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var EXPORTED_SYMBOLS = ["AboutSupport"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+// Platform-specific includes
+if ("@mozilla.org/windows-registry-key;1" in Components.classes)
+  Components.utils.import("resource:///modules/aboutSupportWin32.js");
+else if ("nsILocalFileMac" in Components.interfaces)
+  Components.utils.import("resource:///modules/aboutSupportMac.js");
+else
+  Components.utils.import("resource:///modules/aboutSupportUnix.js");
+
+Components.utils.import("resource:///modules/iteratorUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gMessengerBundle = Services.strings.createBundle(
+  "chrome://messenger/locale/messenger.properties");
+
+var gSMTPService = Cc["@mozilla.org/messengercompose/smtp;1"]
+                     .getService(Ci.nsISmtpService);
+
+var gSocketTypes = {};
+for (let [str, index] in Iterator(Ci.nsMsgSocketType))
+  gSocketTypes[index] = str;
+
+var gAuthMethods = {};
+for (let [str, index] in Iterator(Ci.nsMsgAuthMethod))
+  gAuthMethods[index] = str;
+
+// l10n properties in messenger.properties corresponding to each auth method
+var gAuthMethodProperties = {
+  "1": "authOld",
+  "2": "authPasswordCleartextInsecurely",
+  "3": "authPasswordCleartextViaSSL",
+  "4": "authPasswordEncrypted",
+  "5": "authKerberos",
+  "6": "authNTLM",
+  "8": "authAnySecure"
+};
+
+var AboutSupport = {
+  __proto__: AboutSupportPlatform,
+
+  /**
+   * Gets details about SMTP servers for a given nsIMsgAccount.
+   *
+   * @returns A list of records, each record containing the name and other details
+   *          about one SMTP server.
+   */
+  _getSMTPDetails: function AboutSupport__getSMTPDetails(aAccount) {
+    let identities = aAccount.identities;
+    let defaultIdentity = aAccount.defaultIdentity;
+    let smtpDetails = [];
+
+    for each (let identity in fixIterator(identities, Ci.nsIMsgIdentity)) {
+      let isDefault = identity == defaultIdentity;
+      let smtpServer = {};
+      gSMTPService.GetSmtpServerByIdentity(identity, smtpServer);
+      smtpDetails.push({name: smtpServer.value.displayname,
+                        authMethod: smtpServer.value.authMethod,
+                        socketType: smtpServer.value.socketType,
+                        isDefault: isDefault});
+    }
+
+    return smtpDetails;
+  },
+
+  /**
+   * Returns account details as a list of records.
+   */
+  getAccountDetails: function AboutSupport_getAccountDetails() {
+    let accountDetails = [];
+    let accountManager = Cc["@mozilla.org/messenger/account-manager;1"]
+                           .getService(Ci.nsIMsgAccountManager);
+    let accounts = accountManager.accounts;
+
+    for (let account in fixIterator(accounts, Ci.nsIMsgAccount)) {
+      let server = account.incomingServer;
+      accountDetails.push({
+        key: account.key,
+        name: server.prettyName,
+        hostDetails: "(" + server.type + ") " + server.hostName +
+                     (server.port != -1 ? (":" + server.port) : ""),
+        socketType: server.socketType,
+        authMethod: server.authMethod,
+        smtpServers: this._getSMTPDetails(account),
+      });
+    }
+
+    function idCompare(accountA, accountB) {
+      let regex = /^account([0-9]+)$/;
+      let regexA = regex.exec(accountA.key);
+      let regexB = regex.exec(accountB.key);
+      // There's an off chance that the account ID isn't in the standard
+      // accountN form. If so, use the standard string compare against a fixed
+      // string ("account") to avoid correctness issues.
+      if (!regexA || !regexB) {
+        let keyA = regexA ? "account" : accountA.key;
+        let keyB = regexB ? "account" : accountB.key;
+        return keyA.localeCompare(keyB);
+      }
+      let idA = parseInt(regexA[1]);
+      let idB = parseInt(regexB[1]);
+      return idB - idA;
+    }
+
+    // Sort accountDetails by account ID.
+    accountDetails.sort(idCompare);
+    return accountDetails;
+  },
+
+  /**
+   * Returns the corresponding text for a given socket type index. The text is
+   * returned as a record with "localized" and "neutral" entries.
+   */
+  getSocketTypeText: function AboutSupport_getSocketTypeText(aIndex) {
+    let plainSocketType = (aIndex in gSocketTypes ?
+                           gSocketTypes[aIndex] : aIndex);
+    let prettySocketType;
+    try {
+      prettySocketType = gMessengerBundle.GetStringFromName(
+        "smtpServer-ConnectionSecurityType-" + aIndex);
+    }
+    catch (e if e.result == Components.results.NS_ERROR_FAILURE) {
+      // The string wasn't found in the bundle. Make do without it.
+      prettySocketType = plainSocketType;
+    }
+    return {localized: prettySocketType, neutral: plainSocketType};
+  },
+
+  /**
+   * Returns the corresponding text for a given authentication method index. The
+   * text is returned as a record with "localized" and "neutral" entries.
+   */
+  getAuthMethodText: function AboutSupport_getAuthMethodText(aIndex) {
+    let prettyAuthMethod;
+    let plainAuthMethod = (aIndex in gAuthMethods ?
+                           gAuthMethods[aIndex] : aIndex);
+    if (aIndex in gAuthMethodProperties) {
+      prettyAuthMethod =
+        gMessengerBundle.GetStringFromName(gAuthMethodProperties[aIndex]);
+    }
+    else {
+      prettyAuthMethod = plainAuthMethod;
+    }
+    return {localized: prettyAuthMethod, neutral: plainAuthMethod};
+  }
+};
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/aboutSupportMac.js
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var EXPORTED_SYMBOLS = ["AboutSupportPlatform"];
+
+var AboutSupportPlatform = {
+  /**
+   * Given an nsIFile, gets the file system type. The type is returned as a
+   * string. Possible values are "network", "local", "unknown" and null.
+   */
+  getFileSystemType: function ASPMac_getFileSystemType(aFile) {
+    // Not implemented
+    return null;
+  },
+};
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/aboutSupportUnix.js
@@ -0,0 +1,148 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var EXPORTED_SYMBOLS = ["AboutSupportPlatform"];
+
+// JS ctypes are needed to get at the data we need
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+const GFile = ctypes.StructType("GFile");
+const GFileInfo = ctypes.StructType("GFileInfo");
+const GError = ctypes.StructType("GError");
+const GCancellable = ctypes.StructType("GCancellable");
+
+const G_FILE_ATTRIBUTE_FILESYSTEM_TYPE = "filesystem::type";
+
+const kNetworkFilesystems = ["afs", "cifs", "nfs", "smb"];
+
+// GC is responsible for closing these libraries
+var glib = ctypes.open("libglib-2.0.so");
+var gobject = ctypes.open("libgobject-2.0.so");
+
+var g_free = glib.declare(
+  "g_free",
+  ctypes.default_abi,
+  ctypes.void_t,
+  ctypes.voidptr_t
+);
+
+var g_object_unref = gobject.declare(
+  "g_object_unref",
+  ctypes.default_abi,
+  ctypes.void_t,
+  ctypes.voidptr_t
+);
+
+var AboutSupportPlatform = {
+  /**
+   * Given an nsIFile, gets the file system type. The type is returned as a
+   * string. Possible values are "network", "local", "unknown" and null.
+   */
+  getFileSystemType: function ASPUnix_getFileSystemType(aFile) {
+    let gio = ctypes.open("libgio-2.0.so");
+    try {
+      // Given a UTF-8 string, converts it to the current Glib locale.
+      let g_filename_from_utf8 = glib.declare(
+        "g_filename_from_utf8",
+        ctypes.default_abi,
+        ctypes.char.ptr,   // return type: glib locale string
+        ctypes.char.ptr,   // in: utf8string
+        ctypes.ssize_t,    // in: len
+        ctypes.size_t.ptr, // out: bytes_read
+        ctypes.size_t.ptr, // out: bytes_written
+        GError.ptr         // out: error
+      );
+      // Yes, we want function scoping for variables we need to free in the
+      // finally block. I think this is better than declaring lots of variables
+      // on top.
+      var filePath = g_filename_from_utf8(aFile.path, -1, null, null, null);
+      if (filePath.isNull()) {
+        throw new Error("Unable to convert " + aFile.path +
+                        " into GLib encoding");
+      }
+
+      // Given a path, creates a new GFile for it.
+      let g_file_new_for_path = gio.declare(
+        "g_file_new_for_path",
+        ctypes.default_abi,
+        GFile.ptr,      // return type: a newly-allocated GFile
+        ctypes.char.ptr // in: path
+      );
+      var glibFile = g_file_new_for_path(filePath);
+
+      // Given a GFile, queries the given attributes and returns them
+      // as a GFileInfo.
+      let g_file_query_filesystem_info = gio.declare(
+        "g_file_query_filesystem_info",
+        ctypes.default_abi,
+        GFileInfo.ptr,    // return type
+        GFile.ptr,        // in: file
+        ctypes.char.ptr,  // in: attributes
+        GCancellable.ptr, // in: cancellable
+        GError.ptr        // out: error
+      );
+      var glibFileInfo = g_file_query_filesystem_info(
+        glibFile, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, null, null);
+      if (glibFileInfo.isNull())
+        throw new Error("Unabled to retrieve GLib file info for " + aFile.path);
+
+      let g_file_info_get_attribute_string = gio.declare(
+        "g_file_info_get_attribute_string",
+        ctypes.default_abi,
+        ctypes.char.ptr, // return type: file system type (do not free)
+        GFileInfo.ptr,   // in: info
+        ctypes.char.ptr  // in: attribute
+      );
+      let fsType = g_file_info_get_attribute_string(
+        glibFileInfo, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
+      if (fsType.isNull())
+        return "unknown";
+      else if (kNetworkFilesystems.indexOf(fsType.readString()) != -1)
+        return "network";
+      else
+        return "local";
+    }
+    finally {
+      if (filePath)
+        g_free(filePath);
+      if (glibFile && !glibFile.isNull())
+        g_object_unref(glibFile);
+      if (glibFileInfo && !glibFileInfo.isNull())
+        g_object_unref(glibFileInfo);
+      gio.close();
+    }
+  },
+};
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/aboutSupportWin32.js
@@ -0,0 +1,105 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var EXPORTED_SYMBOLS = ["AboutSupportPlatform"];
+
+// JS ctypes are needed to get at the data we need
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+
+const BOOL = ctypes.int32_t;
+const DRIVE_UNKNOWN = 0;
+const DRIVE_NETWORK = 4;
+
+var AboutSupportPlatform = {
+  /**
+   * Given an nsIFile, gets the file system type. The type is returned as a
+   * string. Possible values are "network", "local", "unknown" and null.
+   */
+  getFileSystemType: function ASPWin32_getFileSystemType(aFile) {
+    let kernel32 = ctypes.open("kernel32.dll");
+
+    try {
+      // Returns the path of the volume a file is on.
+      let GetVolumePathName = kernel32.declare(
+        "GetVolumePathNameW",
+        ctypes.winapi_abi,
+        BOOL,              // return type: 1 indicates success, 0 failure
+        ctypes.jschar.ptr, // in: lpszFileName
+        ctypes.jschar.ptr, // out: lpszVolumePathName
+        ctypes.uint32_t    // in: cchBufferLength
+      );
+
+      // Returns the last error.
+      let GetLastError = kernel32.declare(
+        "GetLastError",
+        ctypes.winapi_abi,
+        ctypes.uint32_t // return type: the last error
+      );
+
+      let filePath = aFile.path;
+      // The volume path should be at most 1 greater than than the length of the
+      // path -- add 1 for a trailing backslash if necessary, and 1 for the
+      // terminating null character. Note that the parentheses around the type are
+      // necessary for new to apply correctly.
+      let volumePath = new (ctypes.jschar.array(filePath.length + 2));
+
+      if (!GetVolumePathName(filePath, volumePath, volumePath.length)) {
+        throw new Error("Unable to get volume path for " + filePath + ", error " +
+                        GetLastError());
+      }
+
+      // Returns the type of the drive.
+      let GetDriveType = kernel32.declare(
+        "GetDriveTypeW",
+        ctypes.winapi_abi,
+        ctypes.uint32_t,  // return type: the drive type
+        ctypes.jschar.ptr // in: lpRootPathName
+      );
+      let type = GetDriveType(volumePath);
+      // http://msdn.microsoft.com/en-us/library/aa364939
+      if (type == DRIVE_UNKNOWN)
+        return "unknown";
+      else if (type == DRIVE_NETWORK)
+        return "network";
+      else
+        return "local";
+    }
+    finally {
+      kernel32.close();
+    }
+  },
+};
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/aboutSupport.xhtml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+  -   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  -
+  -  The contents of this file are subject to the Mozilla Public License Version
+  -  1.1 (the "License"); you may not use this file except in compliance with
+  -  the License. You may obtain a copy of the License at
+  -  http://www.mozilla.org/MPL/
+  -
+  -  Software distributed under the License is distributed on an "AS IS" basis,
+  -  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  -  for the specific language governing rights and limitations under the
+  -  License.
+  -
+  -  The Original Code is aboutSupport.xhtml.
+  -
+  -  The Initial Developer of the Original Code is
+  -  Mozilla Foundation
+  -  Portions created by the Initial Developer are Copyright (C) 2009
+  -  the Initial Developer. All Rights Reserved.
+  -
+  -  Contributor(s):
+  -    Curtis Bartley <cbartley@mozilla.com>
+  -    Siddharth Agarwal <sid.bugzilla@gmail.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 ***** -->
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
+  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
+  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
+  <!ENTITY % aboutSupportDTD SYSTEM "chrome://global/locale/aboutSupport.dtd"> %aboutSupportDTD;
+  <!ENTITY % aboutSupportMailDTD SYSTEM "chrome://messenger/locale/aboutSupportMail.dtd"> %aboutSupportMailDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>&aboutSupport.pageTitle;</title>
+
+    <link rel="stylesheet" href="chrome://global/skin/aboutSupport.css"
+          type="text/css"/>
+    <link rel="stylesheet" href="chrome://messenger/skin/aboutSupport.css"
+          type="text/css"/>
+    <link rel="stylesheet" href="chrome://messenger/content/about-support/show-private.css"
+          type="text/css"/>
+    <!-- Private data is hidden by default. -->
+    <link rel="stylesheet" href="chrome://messenger/content/about-support/hide-private.css"
+          type="text/css" id="about-support-private"/>
+
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/init.js"/>
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/accounts.js"/>
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/extensions.js"/>
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/prefs.js"/>
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/gfx.js"/>
+    <script type="application/javascript;version=1.7"
+            src="chrome://messenger/content/about-support/export.js"/>
+  </head>
+
+  <body dir="&locale.dir;">
+
+    <h1>
+      &aboutSupport.pageTitle;
+    </h1>
+
+    <div class="page-subtitle">
+        &aboutSupport.pageSubtitle;
+    </div>
+
+    <div>
+      <button onclick="copyToClipboard()">
+        &aboutSupport.copyToClipboard.label;
+      </button>
+      <button onclick="sendViaEmail()">
+        &aboutSupport.sendViaEmail.label;
+      </button>
+      <input type="checkbox" id="check-show-private-data" class="data-uionly"
+             onchange="onShowPrivateDataChange(this);" />
+      <label for="check-show-private-data">&aboutSupport.showPrivateData.mainText;
+      <span class="gray-text">&aboutSupport.showPrivateData.explanationText;</span></label>
+    </div>
+
+    <div id="contents">
+
+      <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+      <h2 class="major-section">
+        &aboutSupport.appBasicsTitle;
+      </h2>
+
+      <table>
+        <tbody>
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsName;
+            </th>
+
+            <td id="application-box">
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsVersion;
+            </th>
+
+            <td id="version-box">
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsUserAgent;
+            </th>
+
+            <td id="useragent-box">
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsProfileDir;
+            </th>
+
+            <td>
+              <div id="profile-dir-button-box" class="data-public">
+                <button onclick="openProfileDirectory()">
+#ifdef XP_MACOSX
+                  &aboutSupport.showMac.label;
+#else
+                  &aboutSupport.show.label;
+#endif
+                </button>
+              </div>
+              <span id="profile-dir-box" class="data-private"></span>
+              <span id="profile-fs-type-box"></span>
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsAppBuildID;
+            </th>
+
+            <td id="buildid-box">
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsEnabledPlugins;
+            </th>
+
+            <td>
+              <a href="about:plugins" target="_blank">about:plugins</a>
+            </td>
+          </tr>
+
+          <tr>
+            <th class="column">
+              &aboutSupport.appBasicsBuildConfig;
+            </th>
+
+            <td>
+              <a href="about:buildconfig" target="_blank">about:buildconfig</a>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+
+      <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+      <h2 class="major-section">
+        &aboutSupport.accountsTitle;
+      </h2>
+
+      <table id="accounts-table">
+        <thead>
+          <tr>
+            <th rowspan="2">
+              &aboutSupport.accountsID;
+            </th>
+
+            <th rowspan="2" class="data-private">
+              &aboutSupport.accountsName;
+            </th>
+
+            <th colspan="3">
+              &aboutSupport.accountsIncomingServer;
+            </th>
+
+            <th colspan="4">
+              &aboutSupport.accountsOutgoingServers;
+            </th>
+          </tr>
+          <tr class="thead-level2">
+            <!-- Incoming server -->
+            <th>
+              &aboutSupport.accountsName;
+            </th>
+
+            <th>
+              &aboutSupport.accountsConnSecurity;
+            </th>
+
+            <th>
+              &aboutSupport.accountsAuthMethod;
+            </th>
+
+            <!-- Outgoing servers -->
+            <th>
+              &aboutSupport.accountsName;
+            </th>
+
+            <th>
+              &aboutSupport.accountsConnSecurity;
+            </th>
+
+            <th>
+              &aboutSupport.accountsAuthMethod;
+            </th>
+
+            <th>
+              &aboutSupport.accountsDefault;
+            </th>
+          </tr>
+        </thead>
+
+        <tbody id="accounts-tbody">
+        </tbody>
+      </table>
+
+      <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+      <h2 class="major-section">
+        &aboutSupport.extensionsTitle;
+      </h2>
+
+      <table id="extensions-table">
+        <thead>
+          <tr>
+            <th>
+              &aboutSupport.extensionName;
+            </th>
+            <th>
+              &aboutSupport.extensionVersion;
+            </th>
+            <th>
+              &aboutSupport.extensionEnabled;
+            </th>
+            <th>
+              &aboutSupport.extensionId;
+            </th>
+          </tr>
+        </thead>
+        <tbody id="extensions-tbody">
+        </tbody>
+      </table>
+
+      <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+      <h2 class="major-section">
+        &aboutSupport.modifiedPrefsTitle;
+      </h2>
+
+      <table class="prefs-table">
+        <thead>
+          <tr>
+            <th class="name">
+              &aboutSupport.modifiedPrefsName;
+            </th>
+
+            <th class="value">
+              &aboutSupport.modifiedPrefsValue;
+            </th>
+          </tr>
+        </thead>
+
+        <tbody id="prefs-tbody">
+        </tbody>
+      </table>
+
+      <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+      <h2 class="major-section">
+        &aboutSupport.graphicsTitle;
+      </h2>
+
+      <table>
+        <tbody id="graphics-tbody">
+        </tbody>
+      </table>
+
+    </div>
+
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/accounts.js
@@ -0,0 +1,160 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+/**
+ * Coerces x into a string.
+ */
+function toStr(x) {
+  return "" + x;
+}
+
+/**
+ * Marks x as private (see below).
+ */
+function toPrivate(x) {
+  return {localized: x, neutral: x, isPrivate: true};
+}
+
+/**
+ * A list of fields for the incoming server of an account. Each element of the
+ * list is a pair of [property name, transforming function]. The transforming
+ * function should take the property and return either a string or an object
+ * with the following properties:
+ * - localized: the data in (possibly) localized form
+ * - neutral: the data in language-neutral form
+ * - isPrivate (optional): true if the data is private-only, false if public-only,
+ *                         not stated otherwise
+ */
+var gIncomingDetails = [
+  ["key", toStr],
+  ["name", toPrivate],
+  ["hostDetails", toStr],
+  ["socketType", AboutSupport.getSocketTypeText.bind(AboutSupport)],
+  ["authMethod", AboutSupport.getAuthMethodText.bind(AboutSupport)],
+];
+
+/**
+ * A list of fields for the outgoing servers associated with an account. This is
+ * similar to gIncomingDetails above.
+ */
+var gOutgoingDetails = [
+  ["name", toStr],
+  ["socketType", AboutSupport.getSocketTypeText.bind(AboutSupport)],
+  ["authMethod", AboutSupport.getAuthMethodText.bind(AboutSupport)],
+  ["isDefault", toStr],
+];
+
+/**
+ * A list of account details.
+ */
+XPCOMUtils.defineLazyGetter(window, "gAccountDetails",
+                            function () AboutSupport.getAccountDetails());
+
+function populateAccountsSection() {
+  let trAccounts = [];
+
+  function createTD(data, rowSpan) {
+    let text = (typeof data == "string") ? data : data.localized;
+    let copyData = (typeof data == "string") ? null : data.neutral;
+    let attributes = {rowspan: rowSpan};
+    if (typeof data == "object" && "isPrivate" in data)
+      attributes.class = data.isPrivate ? CLASS_DATA_PRIVATE : CLASS_DATA_PUBLIC;
+
+    return createElement("td", text, attributes, copyData);
+  }
+
+  for (let [, account] in Iterator(gAccountDetails)) {
+    // We want a minimum rowspan of 1
+    let rowSpan = account.smtpServers.length || 1;
+    // incomingTDs is a list of TDs
+    let incomingTDs = [createTD(fn(account[prop]), rowSpan)
+                       for ([, [prop, fn]] in Iterator(gIncomingDetails))];
+    // outgoingTDs is a list of list of TDs
+    let outgoingTDs = [[createTD(fn(smtp[prop]), 1)
+                        for ([, [prop, fn]] in Iterator(gOutgoingDetails))]
+                       for ([, smtp] in Iterator(account.smtpServers))];
+
+    // If there are no SMTP servers, add a dummy element to make life easier below
+    if (outgoingTDs.length == 0)
+      outgoingTDs = [[]];
+
+    // Add the first SMTP server to this tr.
+    let tr = createParentElement("tr", incomingTDs.concat(outgoingTDs[0]));
+    trAccounts.push(tr);
+    // Add the remaining SMTP servers as separate trs
+    for each (let [, tds] in Iterator(outgoingTDs.slice(1)))
+      trAccounts.push(createParentElement("tr", tds));
+  }
+
+  appendChildren(document.getElementById("accounts-tbody"), trAccounts);
+}
+
+/**
+ * Returns a plaintext representation of the accounts data.
+ */
+function getAccountsText(aHidePrivateData, aIndent) {
+  let accumulator = [];
+
+  // Given a string or object, converts it into a language-neutral form
+  function neutralizer(data) {
+    if (typeof data == "string")
+      return data;
+    if ("isPrivate" in data && (aHidePrivateData == data.isPrivate))
+      return "";
+    return data.neutral;
+  }
+
+  for (let [, account] in Iterator(gAccountDetails)) {
+    accumulator.push(aIndent + account.key + ":");
+    // incomingData is a list of strings
+    let incomingData = [neutralizer(fn(account[prop]))
+                        for ([, [prop, fn]] in Iterator(gIncomingDetails))];
+    accumulator.push(aIndent + "  INCOMING: " + incomingData.join(", "));
+
+    // outgoingData is a list of list of strings
+    let outgoingData = [[neutralizer(fn(smtp[prop]))
+                         for ([, [prop, fn]] in Iterator(gOutgoingDetails))]
+                        for ([, smtp] in Iterator(account.smtpServers))];
+    for (let [, data] in Iterator(outgoingData))
+      accumulator.push(aIndent + "  OUTGOING: " + data.join(", "));
+
+    accumulator.push("");
+  }
+
+  return accumulator.join("\n");
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/export.js
@@ -0,0 +1,257 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+
+/**
+ * Create warning text to add to any private data.
+ * @returns A HTML paragraph node containing the warning.
+ */
+function createWarning() {
+  let bundle = Services.strings.createBundle(
+    "chrome://messenger/locale/aboutSupportMail.properties");
+  return createParentElement("p", [
+    createElement("strong", bundle.GetStringFromName("warningLabel")),
+    // Add some whitespace between the label and the text
+    document.createTextNode(" "),
+    document.createTextNode(bundle.GetStringFromName("warningText")),
+  ]);
+}
+
+function getClipboardTransferable() {
+  // Get the HTML and text representations for the important part of the page.
+  let hidePrivateData = !document.getElementById("check-show-private-data").checked;
+  let contentsDiv = createCleanedUpContents(hidePrivateData);
+  let dataHtml = contentsDiv.innerHTML;
+  let dataText = createTextForElement(contentsDiv, hidePrivateData);
+
+  // 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);
+
+  let transferable = Cc["@mozilla.org/widget/transferable;1"]
+                       .createInstance(Ci.nsITransferable);
+
+  // Add the HTML flavor.
+  transferable.addDataFlavor("text/html");
+  ssHtml.data = dataHtml;
+  transferable.setTransferData("text/html", ssHtml, dataHtml.length * 2);
+
+  // Add the plain text flavor.
+  transferable.addDataFlavor("text/unicode");
+  ssText.data = dataText;
+  transferable.setTransferData("text/unicode", ssText, dataText.length * 2);
+
+  return transferable;
+}
+
+function copyToClipboard() {
+  let transferable = getClipboardTransferable();
+  // Store the data into the clipboard.
+  let clipboard = Cc["@mozilla.org/widget/clipboard;1"]
+                    .getService(Ci.nsIClipboard);
+  clipboard.setData(transferable, null, clipboard.kGlobalClipboard);
+}
+
+function sendViaEmail() {
+  // Get the HTML representation for the important part of the page.
+  let hidePrivateData = !document.getElementById("check-show-private-data").checked;
+  let contentsDiv = createCleanedUpContents(hidePrivateData);
+  let dataHtml = contentsDiv.innerHTML;
+  // The editor considers whitespace to be significant, so replace all
+  // whitespace with a single space.
+  dataHtml = dataHtml.replace(/\s+/g, " ");
+
+  // Set up parameters and fields to use for the compose window.
+  let params = Cc["@mozilla.org/messengercompose/composeparams;1"]
+                 .createInstance(Ci.nsIMsgComposeParams);
+  params.type = Ci.nsIMsgCompType.New;
+  params.format = Ci.nsIMsgCompFormat.HTML;
+
+  let fields = Cc["@mozilla.org/messengercompose/composefields;1"]
+                 .createInstance(Ci.nsIMsgCompFields);
+  fields.forcePlainText = false;
+  fields.body = dataHtml;
+  // In general we can have non-ASCII characters, and compose's charset
+  // detection doesn't seem to work when the HTML part is pure ASCII but the
+  // text isn't. So take the easy way out and force UTF-8.
+  fields.characterSet = "UTF-8";
+  fields.bodyIsAsciiOnly = false;
+  params.composeFields = fields;
+
+  // Our params are set up. Now open a compose window.
+  let composeService = Cc["@mozilla.org/messengercompose;1"]
+                         .getService(Ci.nsIMsgComposeService);
+  composeService.OpenComposeWindowWithParams(null, params);
+}
+
+function createCleanedUpContents(aHidePrivateData) {
+  // Get the important part of the page.
+  let contentsDiv = document.getElementById("contents");
+  // Deep-clone the entire div.
+  let clonedDiv = contentsDiv.cloneNode(true);
+  // Go in and replace text with the text we actually want to copy.
+  // (this mutates the cloned node)
+  cleanUpText(clonedDiv, aHidePrivateData);
+  // Insert a warning if we need to
+  if (!aHidePrivateData)
+    clonedDiv.insertBefore(createWarning(), clonedDiv.firstChild);
+  return clonedDiv;
+}
+
+function cleanUpText(aElem, aHidePrivateData) {
+  let node = aElem.firstChild;
+  while (node) {
+    let className = ("className" in node && node.className) || "";
+    // Delete uionly nodes.
+    if (className.indexOf(CLASS_DATA_UIONLY) != -1) {
+      // Advance to the next node before removing the current node, since
+      // node.nextSibling is null after removeChild
+      let nextNode = node.nextSibling;
+      aElem.removeChild(node);
+      node = nextNode;
+      continue;
+    }
+    // Replace private data with a blank string
+    else if (aHidePrivateData && className.indexOf(CLASS_DATA_PRIVATE) != -1) {
+      node.textContent = "";
+    }
+    // Replace public data with a blank string
+    else if (!aHidePrivateData && className.indexOf(CLASS_DATA_PUBLIC) != -1) {
+      node.textContent = "";
+    }
+    else {
+      // Replace localized text with non-localized text
+      let copyData = node.getUserData("copyData");
+      if (copyData != null)
+        node.textContent = copyData;
+    }
+
+    if (node.nodeType == Node.ELEMENT_NODE)
+      cleanUpText(node, aHidePrivateData);
+
+    // Advance!
+    node = node.nextSibling;
+  }
+}
+
+// Return the plain text representation of an element.  Do a little bit
+// of pretty-printing to make it human-readable.
+function createTextForElement(elem, aHidePrivateData) {
+  // Generate the initial text.
+  let textFragmentAccumulator = [];
+  generateTextForElement(elem, aHidePrivateData, "", textFragmentAccumulator);
+  let text = textFragmentAccumulator.join("");
+
+  // Trim extraneous whitespace before newlines, then squash extraneous
+  // blank lines.
+  text = text.replace(/[ \t]+\n/g, "\n");
+  text = text.replace(/\n{3,}/g, "\n\n");
+
+  // Actual CR/LF pairs are needed for some Windows text editors.
+  if ("@mozilla.org/windows-registry-key;1" in Cc)
+    text = text.replace(/\n/g, "\r\n");
+
+  return text;
+}
+
+/**
+ * Elements to replace entirely with custom text. Keys are element ids, values
+ * are functions that return the text. The functions themselves are defined in
+ * the files for their respective sections.
+ */
+var gElementsToReplace = {
+  "accounts-table": getAccountsText,
+  "extensions-table": getExtensionsText,
+};
+
+function generateTextForElement(elem, aHidePrivateData, indent,
+                                textFragmentAccumulator) {
+  // Add a little extra spacing around most elements.
+  if (["td", "th", "span", "a"].indexOf(elem.tagName) == -1)
+    textFragmentAccumulator.push("\n");
+
+  // If this element is one of our elements to replace with text, do it.
+  if (elem.id in gElementsToReplace) {
+    let replaceFn = gElementsToReplace[elem.id];
+    textFragmentAccumulator.push(replaceFn(aHidePrivateData, indent + "  "));
+    return;
+  };
+
+  let childCount = elem.childElementCount;
+
+  // We're not going to spread a two-column <tr> across multiple lines, so
+  // handle that separately.
+  if (elem.tagName == "tr" && childCount == 2) {
+    textFragmentAccumulator.push(indent);
+    textFragmentAccumulator.push(elem.children[0].textContent.trim() + ": " +
+                                 elem.children[1].textContent.trim());
+    return;
+  }
+
+  // Generate the text representation for each child node.
+  let node = elem.firstChild;
+  while (node) {
+    if (node.nodeType == Node.TEXT_NODE) {
+      // Text belonging to this element uses its indentation level.
+      generateTextForTextNode(node, indent, textFragmentAccumulator);
+    }
+    else if (node.nodeType == Node.ELEMENT_NODE) {
+      // Recurse on the child element with an extra level of indentation (but
+      // only if there's more than one child).
+      generateTextForElement(node, aHidePrivateData,
+                             indent + (childCount > 1 ? "  " : ""),
+                             textFragmentAccumulator);
+    }
+    // Advance!
+    node = node.nextSibling;
+  }
+}
+
+function generateTextForTextNode(node, indent, textFragmentAccumulator) {
+  // If the text node is the first of a run of text nodes, then start
+  // a new line and add the initial indentation.
+  let prevNode = node.previousSibling;
+  if (!prevNode || prevNode.nodeType == Node.TEXT_NODE)
+    textFragmentAccumulator.push("\n" + indent);
+
+  // Trim the text node's text content and add proper indentation after
+  // any internal line breaks.
+  let text = node.textContent.trim().replace("\n", "\n" + indent, "g");
+  textFragmentAccumulator.push(text);
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/extensions.js
@@ -0,0 +1,75 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+/**
+ * A list of extensions. This is assigned to by
+ * populateExtensionsSection. There's a potential race condition here, but it's
+ * not really going to happen in practice.
+ */
+var gExtensions;
+
+/**
+ * A list of fields for each extension.
+ */
+var gExtensionDetails = ["name", "version", "isActive", "id"];
+
+function populateExtensionsSection() {
+  AddonManager.getAddonsByTypes(["extension"], function (extensions) {
+    gExtensions = extensions;
+    let trExtensions = [];
+    for (let i = 0; i < extensions.length; i++) {
+      let extension = extensions[i];
+      let extensionTDs = [createElement("td", extension[prop])
+                          for ([, prop] in Iterator(gExtensionDetails))];
+      let tr = createParentElement("tr", extensionTDs);
+      trExtensions.push(tr);
+    }
+    appendChildren(document.getElementById("extensions-tbody"), trExtensions);
+  });
+}
+
+/**
+ * Returns a plaintext representation of extension data.
+ */
+function getExtensionsText(aHidePrivateData, aIndent) {
+  let extensionData = [aIndent +
+                       [extension[prop]
+                        for ([, prop] in Iterator(gExtensionDetails))].join(", ")
+                       for ([, extension] in Iterator(gExtensions))];
+  return extensionData.join("\n");
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/gfx.js
@@ -0,0 +1,172 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+function populateGraphicsSection() {
+  function createHeader(name)
+  {
+    let elem = createElement("th", name);
+    elem.className = "column";
+    return elem;
+  }
+
+  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 = [];
+    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),
+    ]));
+
+    var d2dEnabled = false;
+    try {
+      d2dEnabled = gfxInfo.D2DEnabled;
+    } catch(e) {}
+    var d2dMessage = d2dEnabled;
+    if (!d2dEnabled) {
+      var d2dStatus = -1; // different from any status value defined in the IDL
+      try {
+        d2dStatus = gfxInfo.getFeatureStatus(gfxInfo.FEATURE_DIRECT2D);
+      } catch(e) {
+        window.dump(e + '\n');
+      }
+      if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DEVICE ||
+          d2dStatus == gfxInfo.FEATURE_DISCOURAGED)
+      {
+        d2dMessage = bundle.GetStringFromName("blockedGraphicsCard");
+      }
+      else if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DRIVER_VERSION)
+      {
+        var d2dSuggestedDriverVersion = null;
+        try {
+          d2dSuggestedDriverVersion = gfxInfo.getFeatureSuggestedDriverVersion(gfxInfo.FEATURE_DIRECT2D);
+        } catch(e) {
+          window.dump(e + '\n');
+        }
+        if (d2dSuggestedDriverVersion) {
+          d2dMessage = bundle.GetStringFromName("tryNewerDriverVersion").replace("%1", d2dSuggestedDriverVersion);
+        }
+      }
+    }
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("direct2DEnabled")),
+      createElement("td", d2dMessage),
+    ]));
+
+    var dwEnabled = false;
+    try {
+      dwEnabled = gfxInfo.DWriteEnabled;
+    } catch(e) {}
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("directWriteEnabled")),
+      createElement("td", dwEnabled),
+    ]));
+
+    var webglrenderer;
+    try {
+      webglrenderer = gfxInfo.getWebGLParameter("full-renderer");
+    } catch (e) {
+      webglrenderer = "(WebGL unavailable)";
+    }
+    trGraphics.push(createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("webglRenderer")),
+      createElement("td", webglrenderer)
+    ]));
+
+    appendChildren(graphics_tbody, trGraphics);
+  } // 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 + "/" + totalWindows;
+  if (acceleratedWindows)
+    msg += " " + mgrType;
+
+  appendChildren(graphics_tbody, [
+    createParentElement("tr", [
+      createHeader(bundle.GetStringFromName("acceleratedWindows")),
+      createElement("td", msg),
+    ])
+  ]);
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/hide-private.css
@@ -0,0 +1,44 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+.data-private {
+  display: none;
+}
+
+.data-public {
+  display: inline;
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/init.js
@@ -0,0 +1,153 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Components.utils.import("resource:///modules/iteratorUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+Components.utils.import("resource:///modules/aboutSupport.js");
+
+/* Node classes. All of these are mutually exclusive. */
+
+// Any nodes marked with this class will be considered part of the UI only,
+// and therefore will not be copied.
+const CLASS_DATA_UIONLY = "data-uionly";
+
+// Any nodes marked with this class will be considered private and will be
+// hidden if the user requests only public data to be shown or copied.
+const CLASS_DATA_PRIVATE = "data-private";
+
+// Any nodes marked with this class will only be displayed when the user chooses
+// to not display private data.
+const CLASS_DATA_PUBLIC = "data-public";
+
+window.onload = function () {
+  // Get the support URL.
+  let supportUrl = Services.urlFormatter.formatURLPref("app.support.baseURL");
+
+  // Update the application basics section.
+  document.getElementById("application-box").textContent = Application.name;
+  document.getElementById("version-box").textContent = Application.version;
+  document.getElementById("useragent-box").textContent = navigator.userAgent;
+  document.getElementById("supportLink").href = supportUrl;
+  let currProfD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+  appendChildren(document.getElementById("profile-dir-box"),
+    [createElement("a", currProfD.path,
+      {"href": Services.io.newFileURI(currProfD).spec,
+       "onclick": "openProfileDirectory(); event.preventDefault();"
+      })]);
+
+  let fsType;
+  try {
+    fsType = AboutSupport.getFileSystemType(currProfD);
+  }
+  catch (x) {
+    Components.utils.reportError(x);
+  }
+
+  if (fsType) {
+    let bundle = Services.strings.createBundle(
+      "chrome://messenger/locale/aboutSupportMail.properties");
+    let fsText = bundle.GetStringFromName("fsType." + fsType);
+    document.getElementById("profile-fs-type-box").textContent = fsText;
+  }
+
+  document.getElementById("buildid-box").textContent = Services.appinfo.appBuildID;
+
+  // Update the other sections.
+  populateAccountsSection();
+  populatePreferencesSection();
+  populateExtensionsSection();
+  populateGraphicsSection();
+}
+
+function userDataHandler(aOp, aKey, aData, aSrc, aDest) {
+  if (aOp == UserDataHandler.NODE_CLONED || aOp == UserDataHandler.NODE_IMPORTED)
+    aDest.setUserData(aKey, aData, userDataHandler);
+}
+
+function onShowPrivateDataChange(aCheckbox) {
+  document.getElementById("about-support-private").disabled = aCheckbox.checked;
+}
+
+function createParentElement(tagName, childElems) {
+  let elem = document.createElement(tagName);
+  appendChildren(elem, childElems);
+  return elem;
+}
+
+function createElement(tagName, textContent, opt_attributes, opt_copyData) {
+  if (opt_attributes == null)
+    opt_attributes = [];
+  let elem = document.createElement(tagName);
+  elem.textContent = textContent;
+  for each (let [key, value] in Iterator(opt_attributes))
+    elem.setAttribute(key, "" + value);
+
+  if (opt_copyData != null) {
+    // Look for the (only) text node.
+    let textNode = elem.firstChild;
+    while (textNode && textNode.nodeType != Node.TEXT_NODE)
+      textNode = textNode.nextSibling;
+    // XXX warn here if textNode not found
+    if (textNode)
+      textNode.setUserData("copyData", opt_copyData, userDataHandler);
+  }
+
+  return elem;
+}
+
+function appendChildren(parentElem, childNodes) {
+  for (let i = 0; i < childNodes.length; i++)
+    parentElem.appendChild(childNodes[i]);
+}
+
+function openProfileDirectory() {
+  // Get the profile directory.
+  let currProfD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+  let profileDir = currProfD.path;
+
+  // Show the profile directory.
+  let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
+                                           "nsILocalFile", "initWithPath");
+  new nsLocalFile(profileDir).reveal();
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/overlay.js
@@ -0,0 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var AboutSupportOverlay = {
+  openInNewTab: function AboutSupportOverlay_openInNewTab() {
+    let tabmail = document.getElementById("tabmail");
+    tabmail.openTab("contentTab",
+                    {contentPage: "about:support",
+                     clickHandler: "specialTabs.aboutClickHandler(event);" });
+  }
+};
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/overlay.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+  -   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  -
+  -  The contents of this file are subject to the Mozilla Public License Version
+  -  1.1 (the "License"); you may not use this file except in compliance with
+  -  the License. You may obtain a copy of the License at
+  -  http://www.mozilla.org/MPL/
+  -
+  -  Software distributed under the License is distributed on an "AS IS" basis,
+  -  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  -  for the specific language governing rights and limitations under the
+  -  License.
+  -
+  -  The Original Code is Thunderbird about:support.
+  -
+  -  The Initial Developer of the Original Code is
+  -  the Mozilla Foundation.
+  -  Portions created by the Initial Developer are Copyright (C) 2010
+  -  the Initial Developer. All Rights Reserved.
+  -
+  -  Contributor(s):
+  -    Siddharth Agarwal <sid.bugzilla@gmail.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 ***** -->
+
+<!DOCTYPE overlay [
+<!ENTITY % baseMenuOverlayDTD SYSTEM "chrome://messenger/locale/baseMenuOverlay.dtd"> %baseMenuOverlayDTD;
+]>
+
+<overlay id="tb-about-support-overlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/x-javascript" src="chrome://messenger/content/about-support/overlay.js" />
+  <menupopup id="menu_HelpPopup">
+    <menuitem id="aboutsupport_open"
+              label="&helpTroubleshootingInfo.label;"
+              accesskey="&helpTroubleshootingInfo.accesskey;"
+              insertafter="menu_openHelp"
+              oncommand="AboutSupportOverlay.openInNewTab();" />
+  </menupopup>
+
+</overlay>
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/prefs.js
@@ -0,0 +1,141 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.xhtml.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Curtis Bartley <cbartley@mozilla.com>
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+
+const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
+                                                Ci.nsIPrefLocalizedString).data;
+
+// 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.fixup.",
+  "browser.history_expire_",
+  "browser.link.open_newwindow",
+  "browser.mousewheel.",
+  "browser.places.",
+  "browser.startup.homepage",
+  "browser.tabs.",
+  "browser.zoom.",
+  "dom.",
+  "extensions.checkCompatibility",
+  "extensions.lastAppVersion",
+  "font.",
+  "general.useragent.",
+  "gfx.color_management.mode",
+  "javascript.",
+  "keyword.",
+  "layout.css.dpi",
+  "mail.openMessageBehavior.",
+  "mail.spotlight.",
+  "mail.winsearch.",
+  "mailnews.database.",
+  "network.",
+  "places.",
+  "print.",
+  "privacy.",
+  "security."
+];
+
+// The blacklist, unlike the whitelist, is a list of regular expressions.
+const PREFS_BLACKLIST = [
+  /^network[.]proxy[.]/,
+  /[.]print_to_filename$/,
+  /[.]lastFolderIndexedUri/,
+];
+
+function populatePreferencesSection() {
+  let modifiedPrefs = getModifiedPrefs();
+
+  function comparePrefs(pref1, pref2) {
+    return pref1.name.localeCompare(pref2.name);
+  }
+
+  modifiedPrefs.sort(comparePrefs);
+
+  let trPrefs = [];
+  modifiedPrefs.forEach(function (pref) {
+    let tdName = createElement("td", pref.name, {"class": "pref-name"});
+    let tdValue = createElement("td", formatPrefValue(pref.value),
+                                {"class": "pref-value"});
+    let tr = createParentElement("tr", [tdName, tdValue]);
+    trPrefs.push(tr);
+  });
+
+  appendChildren(document.getElementById("prefs-tbody"), trPrefs);
+}
+
+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 = [Application.prefs.get(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));
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/content/show-private.css
@@ -0,0 +1,40 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird about:support.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+.data-public {
+  display: none;
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/about-support/jar.mn
@@ -0,0 +1,13 @@
+messenger.jar:
+%   overlay chrome://messenger/content/messenger.xul           chrome://messenger/content/about-support/overlay.xul
+    content/messenger/about-support/hide-private.css           (content/hide-private.css)
+    content/messenger/about-support/show-private.css           (content/show-private.css)
+    content/messenger/about-support/init.js                    (content/init.js)
+    content/messenger/about-support/accounts.js                (content/accounts.js)
+    content/messenger/about-support/extensions.js              (content/extensions.js)
+    content/messenger/about-support/prefs.js                   (content/prefs.js)
+    content/messenger/about-support/gfx.js                     (content/gfx.js)
+    content/messenger/about-support/export.js                  (content/export.js)
+*   content/messenger/about-support/aboutSupport.xhtml         (content/aboutSupport.xhtml)
+    content/messenger/about-support/overlay.js                 (content/overlay.js)
+    content/messenger/about-support/overlay.xul                (content/overlay.xul)
--- a/mail/components/aboutRedirector.js
+++ b/mail/components/aboutRedirector.js
@@ -44,21 +44,24 @@ Components.utils.import("resource://gre/
 
 function AboutRedirector() {}
 AboutRedirector.prototype = {
   classDescription: "Mail about: Redirector",
   classID: Components.ID("{8cc51368-6aa0-43e8-b762-bde9b9fd828c}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
 
   // Each entry in the map has the key as the part after the "about:" and the
-  // value as a record with url and flags entries.
+  // value as a record with url and flags entries. Note that each addition here
+  // should be coupled with a corresponding addition in mailComponents.manifest.
   _redirMap: {
     "rights": {url: "chrome://messenger/content/aboutRights.xhtml",
                flags: (Ci.nsIAboutModule.ALLOW_SCRIPT |
                        Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT)},
+    "support": {url: "chrome://messenger/content/about-support/aboutSupport.xhtml",
+                flags: Ci.nsIAboutModule.ALLOW_SCRIPT},
   },
 
   /**
    * Gets the module name from the given URI.
    */
   _getModuleName: function AboutRedirector__getModuleName(aURI) {
     // Strip out the first ? or #, and anything following it
     let name = (/[^?#]+/.exec(aURI.path))[0];
--- a/mail/components/mailComponents.manifest
+++ b/mail/components/mailComponents.manifest
@@ -1,10 +1,13 @@
 component {8cc51368-6aa0-43e8-b762-bde9b9fd828c} aboutRedirector.js
+# Each addition here should be coupled with a corresponding addition in
+# aboutRedirector.js.
 contract @mozilla.org/network/protocol/about;1?what=rights {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
+contract @mozilla.org/network/protocol/about;1?what=support {8cc51368-6aa0-43e8-b762-bde9b9fd828c}
 
 component {44346520-c5d2-44e5-a1ec-034e04d7fac4} nsMailDefaultHandler.js
 contract @mozilla.org/mail/clh;1 {44346520-c5d2-44e5-a1ec-034e04d7fac4}
 category command-line-handler x-default @mozilla.org/mail/clh;1
 category command-line-validator b-default @mozilla.org/mail/clh;1
 
 component {1c73f03a-b817-4640-b984-18c3478a9ae3} mailContentHandler.js
 contract @mozilla.org/uriloader/content-handler;1?type=text/html {1c73f03a-b817-4640-b984-18c3478a9ae3}
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/aboutSupportMail.dtd
@@ -0,0 +1,14 @@
+<!ENTITY aboutSupport.appBasicsAppBuildID "Application Build ID">
+
+<!ENTITY aboutSupport.accountsTitle "Mail and News Accounts">
+<!ENTITY aboutSupport.showPrivateData.mainText "Include account names">
+<!ENTITY aboutSupport.showPrivateData.explanationText "(possibly identifying information)">
+<!ENTITY aboutSupport.accountsID "ID">
+<!ENTITY aboutSupport.accountsName "Name">
+<!ENTITY aboutSupport.accountsIncomingServer "Incoming server">
+<!ENTITY aboutSupport.accountsOutgoingServers "Outgoing servers">
+<!ENTITY aboutSupport.accountsConnSecurity "Connection security">
+<!ENTITY aboutSupport.accountsAuthMethod "Authentication method">
+<!ENTITY aboutSupport.accountsDefault "Default?">
+
+<!ENTITY aboutSupport.sendViaEmail.label "Send via email">
new file mode 100644
--- /dev/null
+++ b/mail/locales/en-US/chrome/messenger/aboutSupportMail.properties
@@ -0,0 +1,11 @@
+# LOCALIZATION NOTE (warningLabel): Label for warning text that shows up when private data is included
+warningLabel=WARNING:
+# LOCALIZATION NOTE (warningText): Warning text that shows up when private data is included
+warningText=This contains sensitive information which shouldn't be forwarded or published without permission.
+
+# LOCALIZATION NOTE (fsType.local): Indicator that the displayed directory is on a local drive
+fsType.local = (Local drive)
+# LOCALIZATION NOTE (fsType.network): Indicator that the displayed directory is on the network
+fsType.network = (Network drive)
+# LOCALIZATION NOTE (fsType.unknown): Indicator that we couldn't figure out whether the directory is local or on a network
+fsType.unknown = (Unknown location)
--- a/mail/locales/en-US/chrome/messenger/baseMenuOverlay.dtd
+++ b/mail/locales/en-US/chrome/messenger/baseMenuOverlay.dtd
@@ -1,15 +1,17 @@
 <!-- Help Menu -->
 <!ENTITY helpMenu.label "Help">
 <!ENTITY helpMenu.accesskey "H">
 <!-- LOCALIZATION NOTE some localizations of Windows use "?"
                        for the help button in the menubar but Gnome does not.   -->
 <!ENTITY helpMenuWin.label "Help">
 <!ENTITY helpMenuWin.accesskey "H">
+<!ENTITY helpTroubleshootingInfo.label     "Troubleshooting Information">
+<!ENTITY helpTroubleshootingInfo.accesskey "T">
 <!ENTITY releaseCmd.label "Release Notes">
 <!ENTITY releaseCmd.accesskey "R">
 <!ENTITY whatsNewCmd.label "What's New">
 <!ENTITY whatsNewCmd.accesskey "W">
 <!ENTITY featureConfiguratorCmd.label "Migration Assistant">
 <!ENTITY featureConfiguratorCmd.accesskey "M">
 <!ENTITY openHelp.label "Help Contents">
 <!ENTITY openHelp.accesskey "H">
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -4,16 +4,18 @@
 @AB_CD@.jar:
 % locale messenger @AB_CD@ %locale/@AB_CD@/messenger/
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://messenger/locale/downloads/settingsChange.dtd
 % override chrome://global/locale/netError.dtd chrome://messenger/locale/netError.dtd
   locale/@AB_CD@/messenger/credits.dtd                                  (%chrome/messenger/credits.dtd)
   locale/@AB_CD@/messenger/aboutDialog.dtd                              (%chrome/messenger/aboutDialog.dtd)
   locale/@AB_CD@/messenger/aboutRights.dtd                              (%chrome/messenger/aboutRights.dtd)
   locale/@AB_CD@/messenger/aboutRights.properties                       (%chrome/messenger/aboutRights.properties)
+  locale/@AB_CD@/messenger/aboutSupportMail.dtd                         (%chrome/messenger/aboutSupportMail.dtd)
+  locale/@AB_CD@/messenger/aboutSupportMail.properties                  (%chrome/messenger/aboutSupportMail.properties)
   locale/@AB_CD@/messenger/accountCreation.dtd                          (%chrome/messenger/accountCreation.dtd)
   locale/@AB_CD@/messenger/accountCreation.properties                   (%chrome/messenger/accountCreation.properties)
   locale/@AB_CD@/messenger/accountCreationModel.properties              (%chrome/messenger/accountCreationModel.properties)
   locale/@AB_CD@/messenger/accountCreationUtil.properties               (%chrome/messenger/accountCreationUtil.properties)
   locale/@AB_CD@/messenger/systemIntegrationDialog.dtd                  (%chrome/messenger/systemIntegrationDialog.dtd)  
   locale/@AB_CD@/messenger/virtualFolderProperties.dtd                  (%chrome/messenger/virtualFolderProperties.dtd)
   locale/@AB_CD@/messenger/virtualFolderListDialog.dtd                  (%chrome/messenger/virtualFolderListDialog.dtd)
   locale/@AB_CD@/messenger/multimessageview.properties                  (%chrome/messenger/multimessageview.properties)
new file mode 100644
--- /dev/null
+++ b/mail/test/mozmill/content-tabs/test-about-support.js
@@ -0,0 +1,268 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thunderbird Mail Client.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+var MODULE_NAME = 'test-about-support';
+
+var RELATIVE_ROOT = '../shared-modules';
+var MODULE_REQUIRES = ['folder-display-helpers', 'content-tab-helpers'];
+
+var controller = {};
+Components.utils.import('resource://mozmill/modules/controller.js', controller);
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function setupModule(module) {
+  let fdh = collector.getModule("folder-display-helpers");
+  fdh.installInto(module);
+  let cth = collector.getModule("content-tab-helpers");
+  cth.installInto(module);
+}
+
+// After every test we want to close the about:support tab so that failures
+// don't cascade.
+function teardownTest(module) {
+  mc.tabmail.closeOtherTabs(mc.tabmail.tabInfo[0]);
+}
+
+/**
+ * Strings found in the about:support HTML or text that should clearly mark the
+ * data as being from about:support.
+ */
+const ABOUT_SUPPORT_STRINGS = ["Application Basics", "Mail and News Accounts",
+                               "Extensions", "Modified Preferences", "Graphics"];
+
+/**
+ * Strings that if found in the about:support text or HTML usually indicate an
+ * error.
+ */
+const ABOUT_SUPPORT_ERROR_STRINGS = ["undefined", "null"];
+
+
+/*
+ * Helpers
+ */
+
+/**
+ * Opens about:support and waits for it to load.
+ *
+ * @returns the about:support tab.
+ */
+function open_about_support() {
+  let tab = open_content_tab_with_click(mc.menus.helpMenu.aboutsupport_open);
+  assert_content_tab_has_url(tab, "about:support");
+  // We have one variable that's asynchronously populated -- wait for it to be
+  // populated.
+  if (!controller.waitForEval("subject.gExtensions !== undefined",
+                              NORMAL_TIMEOUT, FAST_INTERVAL,
+                              tab.browser.contentWindow)) {
+    mark_failure(["Timeout waiting for about:support's gExtensions to populate."]);
+  }
+  return tab;
+}
+
+
+/*
+ * Tests
+ */
+
+/**
+ * Test displaying the about:support page. Also perform a couple of basic tests
+ * to check that no major errors have occurred. The basic tests are by no means
+ * comprehensive.
+ */
+function test_display_about_support() {
+  let tab = open_about_support();
+  // Check that the document has a few strings that indicate that we've loaded
+  // the right page.
+  for (let [, str] in Iterator(ABOUT_SUPPORT_STRINGS)) {
+    assert_content_tab_text_present(tab, str);
+  }
+
+  // Check that error strings aren't present anywhere
+  for (let [, str] in Iterator(ABOUT_SUPPORT_ERROR_STRINGS)) {
+    assert_content_tab_text_absent(tab, str);
+  }
+  close_tab(tab);
+}
+
+const UNIQUE_ID = "3a9e1694-7115-4237-8b1e-1cabe6e35073";
+
+/**
+ * Test that a modified preference on the whitelist but not on the blacklist
+ * shows up.
+ */
+function test_modified_pref_on_whitelist() {
+  const PREFIX = "accessibility.";
+  let prefName = PREFIX + UNIQUE_ID;
+  Services.prefs.setBoolPref(prefName, true);
+  let tab = open_about_support();
+  // Check that the prefix is actually in the whitelist.
+  if (tab.browser.contentWindow.PREFS_WHITELIST.indexOf(PREFIX) == -1)
+    mark_failure(["The prefs whitelist doesn't contain " + PREFIX]);
+
+  assert_content_tab_text_present(tab, prefName);
+  close_tab(tab);
+  Services.prefs.clearUserPref(prefName);
+}
+
+/**
+ * Test that a modified preference not on the whitelist doesn't show up.
+ */
+function test_modified_pref_not_on_whitelist() {
+  Services.prefs.setBoolPref(UNIQUE_ID, true);
+  let tab = open_about_support();
+  assert_content_tab_text_absent(tab, UNIQUE_ID);
+  close_tab(tab);
+  Services.prefs.clearUserPref(UNIQUE_ID);
+}
+
+/**
+ * Test that a modified preference on the blacklist doesn't show up.
+ */
+function test_modified_pref_on_blacklist() {
+  const PREFIX = "network.proxy.";
+  let prefName = PREFIX + UNIQUE_ID;
+  Services.prefs.setBoolPref(prefName, true);
+  let tab = open_about_support();
+  // Check that the prefix is in the blacklist.
+  if (!tab.browser.contentWindow.PREFS_BLACKLIST.some(
+        function(regex) regex.test(PREFIX))) {
+    mark_failure(["The prefs blacklist doesn't include " + PREFIX]);
+  }
+  assert_content_tab_text_absent(tab, prefName);
+  close_tab(tab);
+  Services.prefs.clearUserPref(prefName);
+}
+
+/**
+ * Test that private data isn't displayed by default, and that when it is
+ * displayed, it actually shows up.
+ */
+function test_private_data() {
+  let tab = open_about_support();
+  let checkbox = content_tab_e(tab, "check-show-private-data");
+  // We use the profile button's div as an example of a public-only element, and
+  // the profile directory display as an example of a private-only element.
+  let privateElem = content_tab_e(tab, "profile-dir-box");
+  let publicElem = content_tab_e(tab, "profile-dir-button-box");
+  assert_true(!checkbox.checked,
+              "Private data checkbox shouldn't be checked by default");
+  assert_element_visible(tab, publicElem);
+  assert_element_hidden(tab, privateElem);
+
+  // Now check the checkbox and see what happens
+  checkbox.click();
+  wait_for_element_display_value(tab, publicElem, "none");
+  wait_for_element_display_value(tab, privateElem, "inline");
+  close_tab(tab);
+}
+
+/**
+ * Test (well, sort of) the copy to clipboard function with public data.
+ */
+function test_copy_to_clipboard_public() {
+  let tab = open_about_support();
+  // To avoid destroying the current contents of the clipboard, instead of
+  // actually copying to it, we just retrieve what would have been copied to it
+  let transferable = tab.browser.contentWindow.getClipboardTransferable();
+  for (let [, flavor] in Iterator(["text/html", "text/unicode"])) {
+    let data = {};
+    transferable.getTransferData(flavor, data, {});
+    let text = data.value.data;
+
+    for (let [, str] in Iterator(ABOUT_SUPPORT_STRINGS)) {
+      if (text.indexOf(str) == -1)
+        mark_failure(["Unable to find \"" + str + "\" in flavor \"" + flavor + "\""]);
+    }
+
+    for (let [, str] in Iterator(ABOUT_SUPPORT_ERROR_STRINGS)) {
+      if (text.indexOf(str) != -1)
+        mark_failure(["Found \"" + str + "\" in flavor \"" + flavor + "\""]);
+    }
+
+    // Check that private data (profile directory) isn't in the output.
+    let profD = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
+    if (text.indexOf(profD) != -1)
+      mark_failure(["Found profile directory in flavor \"" + flavor + "\""]);
+  }
+  close_tab(tab);
+}
+
+/**
+ * Test (well, sort of) the copy to clipboard function with private data.
+ */
+function test_copy_to_clipboard_private() {
+  let bundle = Services.strings.createBundle(
+    "chrome://messenger/locale/aboutSupportMail.properties");
+  let warningText = bundle.GetStringFromName("warningText");
+
+  let tab = open_about_support();
+
+  // Display private data.
+  let privateElem = content_tab_e(tab, "profile-dir-box");
+  content_tab_e(tab, "check-show-private-data").click();
+  wait_for_element_display_value(tab, privateElem, "inline");
+
+  // To avoid destroying the current contents of the clipboard, instead of
+  // actually copying to it, we just retrieve what would have been copied to it
+  let transferable = tab.browser.contentWindow.getClipboardTransferable();
+  for (let [, flavor] in Iterator(["text/html", "text/unicode"])) {
+    let data = {};
+    transferable.getTransferData(flavor, data, {});
+    let text = data.value.data;
+
+    for (let [, str] in Iterator(ABOUT_SUPPORT_STRINGS)) {
+      if (text.indexOf(str) == -1)
+        mark_failure(["Unable to find \"" + str + "\" in flavor \"" + flavor + "\""]);
+    }
+
+    for (let [, str] in Iterator(ABOUT_SUPPORT_ERROR_STRINGS)) {
+      if (text.indexOf(str) != -1)
+        mark_failure(["Found \"" + str + "\" in flavor \"" + flavor + "\""]);
+    }
+
+    // Check that private data (profile directory) is in the output.
+    let profD = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
+    if (text.indexOf(profD) == -1)
+      mark_failure(["Unable to find profile directory in flavor \"" + flavor + "\""]);
+
+    // Check that the warning text is in the output.
+    if (text.indexOf(warningText) == -1)
+      mark_failure(["Unable to find warning text in flavor \"" + flavor + "\""]);
+  }
+  close_tab(tab);
+}
--- a/mail/test/mozmill/shared-modules/test-content-tab-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-content-tab-helpers.js
@@ -73,16 +73,23 @@ function installInto(module) {
   setupModule();
 
   // Now copy helper functions
   module.open_content_tab_with_url = open_content_tab_with_url;
   module.open_content_tab_with_click = open_content_tab_with_click;
   module.plan_for_content_tab_load = plan_for_content_tab_load;
   module.wait_for_content_tab_load = wait_for_content_tab_load;
   module.assert_content_tab_has_url = assert_content_tab_has_url;
+  module.content_tab_e = content_tab_e;
+  module.get_element_display = get_element_display;
+  module.assert_element_hidden = assert_element_hidden;
+  module.assert_element_visible = assert_element_visible;
+  module.wait_for_element_display_value = wait_for_element_display_value;
+  module.assert_content_tab_text_present = assert_content_tab_text_present;
+  module.assert_content_tab_text_absent = assert_content_tab_text_absent;
 }
 
 /**
  * Opens a content tab with the given URL.
  *
  * @param aURL The URL to load (string).
  * @param [aBackground] Whether the tab is opened in the background. Defaults to
  *                      false.
@@ -130,16 +137,17 @@ function open_content_tab_with_click(aEl
   if (!controller.waitForEval("subject.childNodes.length == " + (preCount + 1),
                               FAST_TIMEOUT, FAST_INTERVAL,
                               aController.tabmail.tabContainer))
     mark_failure(["Timeout waiting for the content tab to open"]);
 
   // We append new tabs at the end, so check the last one.
   let expectedNewTab = aController.tabmail.tabInfo[preCount];
   folderDisplayHelper.assert_selected_tab(expectedNewTab);
+  folderDisplayHelper.assert_tab_mode_name(expectedNewTab, "contentTab");
   wait_for_content_tab_load(expectedNewTab);
   return expectedNewTab;
 }
 
 /**
  * Call this before triggering a page load that you are going to wait for using
  * |wait_for_content_tab_load|. This ensures that if a page is already displayed
  * in the given tab that state is sufficiently cleaned up so it doesn't trick us
@@ -189,8 +197,79 @@ function wait_for_content_tab_load(aTab)
 /**
  * Assert that the given content tab has the given URL (string) loaded.
  */
 function assert_content_tab_has_url(aTab, aURL) {
   if (aTab.browser.currentURI.spec != aURL)
     mark_failure(["The tab", aTab, "should have URL", aURL, "but instead has",
                   aTab.browser.currentURI.spec]);
 }
+
+/**
+ * Get the element with the given ID from the content tab's displayed page.
+ */
+function content_tab_e(aTab, aId) {
+  return aTab.browser.contentDocument.getElementById(aId);
+}
+
+/**
+ * Returns the current "display" style property of an element.
+ */
+function get_element_display(aTab, aElem) {
+  let style = aTab.browser.contentWindow.getComputedStyle(aElem);
+  return style.getPropertyValue("display");
+}
+
+/**
+ * Asserts that the given element is hidden from view on the page.
+ */
+function assert_element_hidden(aTab, aElem) {
+  let display = get_element_display(aTab, aElem);
+  if (display != "none") {
+    mark_failure(["Element", aElem, "should be hidden but has display", display,
+                  "instead"]);
+  }
+}
+
+/**
+ * Asserts that the given element is visible on the page.
+ */
+function assert_element_visible(aTab, aElem) {
+  let display = get_element_display(aTab, aElem);
+  if (display != "inline") {
+    mark_failure(["Element", aElem, "should be visible but has display", display,
+                  "instead"]);
+  }
+}
+
+/**
+ * Waits for the element's display property to be the given value.
+ */
+function wait_for_element_display_value(aTab, aElem, aValue) {
+  function isValue() {
+    return get_element_display(aTab, aElem) == aValue;
+  }
+  if (!controller.waitForEval("subject()", NORMAL_TIMEOUT, FAST_INTERVAL,
+                              isValue)) {
+    mark_failure(["Timeout waiting for element", aElem, "to have display value",
+                  aValue]);
+  }
+}
+
+/**
+ * Asserts that the given text is present on the content tab's page.
+ */
+function assert_content_tab_text_present(aTab, aText) {
+  let html = aTab.browser.contentDocument.documentElement.innerHTML;
+  if (html.indexOf(aText) == -1) {
+    mark_failure(["Unable to find string \"" + aText + "\" on the content tab's page"]);
+  }
+}
+
+/**
+ * Asserts that the given text is absent on the content tab's page.
+ */
+function assert_content_tab_text_absent(aTab, aText) {
+  let html = aTab.browser.contentDocument.documentElement.innerHTML;
+  if (html.indexOf(aText) != -1) {
+    mark_failure(["Found string \"" + aText + "\" on the content tab's page"]);
+  }
+}
--- a/mail/themes/gnomestripe/jar.mn
+++ b/mail/themes/gnomestripe/jar.mn
@@ -3,16 +3,17 @@ classic.jar:
 % skin messenger classic/1.0 %skin/classic/messenger/
   skin/classic/messenger/featureConfigurator.css              (mail/featureConfigurator.css)
   skin/classic/messenger/featureConfigurators/subpage.css     (mail/featureConfigurators/subpage.css)
   skin/classic/messenger/featureConfigurators/animation.png   (mail/featureConfigurators/animation.png)
   skin/classic/messenger/featureConfigurators/compact-header.png    (mail/featureConfigurators/compact-header.png)
   skin/classic/messenger/featureConfigurators/folder-columns.png    (mail/featureConfigurators/folder-columns.png)
   skin/classic/messenger/featureConfigurators/toolbars.png    (mail/featureConfigurators/toolbars.png)
   skin/classic/messenger/primaryToolbar.css                   (mail/primaryToolbar.css)
+  skin/classic/messenger/aboutSupport.css                     (../qute/mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                   (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                  (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                    (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                    (mail/accountWizard.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
--- a/mail/themes/pinstripe/jar.mn
+++ b/mail/themes/pinstripe/jar.mn
@@ -6,16 +6,17 @@ classic.jar:
   skin/classic/messenger/featureConfigurators/compact-header.png (mail/featureConfigurators/compact-header.png)
   skin/classic/messenger/featureConfigurators/folder-columns.png (mail/featureConfigurators/folder-columns.png)
   skin/classic/messenger/featureConfigurators/toolbars.png       (mail/featureConfigurators/toolbars.png)
   skin/classic/messenger/glodaFacetView.css                      (mail/glodaFacetView.css)
   skin/classic/messenger/multimessageview.css                    (mail/multimessageview.css)
   skin/classic/messenger/dialogs.css                             (mail/dialogs.css)
   skin/classic/messenger/messenger.css                           (mail/messenger.css)
   skin/classic/messenger/primaryToolbar.css                      (mail/primaryToolbar.css)
+  skin/classic/messenger/aboutSupport.css                        (../qute/mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                      (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                     (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                       (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                       (mail/accountWizard.css)
   skin/classic/messenger/section_collapsed.png                   (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                    (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                       (mail/messageHeader.css)
   skin/classic/messenger/messageWindow.css                       (mail/messageWindow.css)
--- a/mail/themes/qute/jar.mn
+++ b/mail/themes/qute/jar.mn
@@ -43,16 +43,17 @@ classic.jar:
 % skin messenger classic/1.0 %skin/classic/messenger/ os!=WINNT
   skin/classic/messenger/featureConfigurator.css              (mail/featureConfigurator.css)
   skin/classic/messenger/featureConfigurators/subpage.css     (mail/featureConfigurators/subpage.css)
   skin/classic/messenger/featureConfigurators/animation.png   (mail/featureConfigurators/animation.png)
   skin/classic/messenger/featureConfigurators/compact-header.png  (mail/featureConfigurators/compact-header.png)
   skin/classic/messenger/featureConfigurators/folder-columns.png  (mail/featureConfigurators/folder-columns.png)
   skin/classic/messenger/featureConfigurators/toolbars.png    (mail/featureConfigurators/toolbars.png)
   skin/classic/messenger/primaryToolbar.css                   (mail/primaryToolbar.css)
+  skin/classic/messenger/aboutSupport.css                     (mail/aboutSupport.css)
   skin/classic/messenger/accountCentral.css                   (mail/accountCentral.css)
   skin/classic/messenger/accountCreation.css                  (mail/accountCreation.css)
   skin/classic/messenger/accountManage.css                    (mail/accountManage.css)
   skin/classic/messenger/accountWizard.css                    (mail/accountWizard.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
@@ -271,16 +272,17 @@ classic.jar:
 % skin messenger classic/1.0 %skin/classic/aero/messenger/ os=WINNT osversion>=6
   skin/classic/aero/messenger/featureConfigurator.css              (mail/featureConfigurator.css)
   skin/classic/aero/messenger/featureConfigurators/subpage.css     (mail/featureConfigurators/subpage.css)
   skin/classic/aero/messenger/featureConfigurators/animation.png   (mail/featureConfigurators/animation.png)
   skin/classic/aero/messenger/featureConfigurators/compact-header.png  (mail/featureConfigurators/compact-header.png)
   skin/classic/aero/messenger/featureConfigurators/folder-columns.png  (mail/featureConfigurators/folder-columns.png)
   skin/classic/aero/messenger/featureConfigurators/toolbars.png    (mail/featureConfigurators/toolbars.png)
 * skin/classic/aero/messenger/primaryToolbar.css                   (mail/primaryToolbar-aero.css)
+  skin/classic/aero/messenger/aboutSupport.css                     (mail/aboutSupport.css)
   skin/classic/aero/messenger/accountCentral.css                   (mail/accountCentral.css)
   skin/classic/aero/messenger/accountCreation.css                  (mail/accountCreation.css)
   skin/classic/aero/messenger/accountManage.css                    (mail/accountManage.css)
   skin/classic/aero/messenger/accountWizard.css                    (mail/accountWizard.css)
   skin/classic/aero/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/aero/messenger/section_expanded.png                 (mail/section_expanded.png)
 * skin/classic/aero/messenger/messageHeader.css                    (mail/messageHeader-aero.css)
   skin/classic/aero/messenger/messageBody.css                      (mail/messageBody.css)
new file mode 100644
--- /dev/null
+++ b/mail/themes/qute/mail/aboutSupport.css
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is aboutSupport.css.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Siddharth Agarwal <sid.bugzilla@gmail.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 ***** */
+
+.gray-text {
+  color: GrayText;
+}
+
+.thead-level2 > th {
+  background-color: -moz-Dialog;
+  color: -moz-DialogText;
+}
+
+td.data-private {
+  background-color: infobackground;
+  color: infotext;
+}