Bug 600620 - followup: fix typo in aboutSupport.js - r=jmuizelaar, a=blocking2.0...
[mozilla-central.git] / toolkit / content / aboutSupport.js
blob2858ae337a1b7b88a843880c8c6da45098f92c71
1 # ***** BEGIN LICENSE BLOCK *****
2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
7 # http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
12 # License.
14 # The Original Code is aboutSupport.xhtml.
16 # The Initial Developer of the Original Code is
17 # Mozilla Foundation
18 # Portions created by the Initial Developer are Copyright (C) 2009
19 # the Initial Developer. All Rights Reserved.
21 # Contributor(s):
22 #   Curtis Bartley <cbartley@mozilla.com>
24 # Alternatively, the contents of this file may be used under the terms of
25 # either the GNU General Public License Version 2 or later (the "GPL"), or
26 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 # in which case the provisions of the GPL or the LGPL are applicable instead
28 # of those above. If you wish to allow use of your version of this file only
29 # under the terms of either the GPL or the LGPL, and not to allow others to
30 # use your version of this file under the terms of the MPL, indicate your
31 # decision by deleting the provisions above and replace them with the notice
32 # and other provisions required by the GPL or the LGPL. If you do not delete
33 # the provisions above, a recipient may use your version of this file under
34 # the terms of any one of the MPL, the GPL or the LGPL.
36 # ***** END LICENSE BLOCK *****
38 const Cc = Components.classes;
39 const Ci = Components.interfaces;
41 Components.utils.import("resource://gre/modules/AddonManager.jsm");
42 Components.utils.import("resource://gre/modules/Services.jsm");
45 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
46                                                Ci.nsIPrefLocalizedString).data;
48 // We use a preferences whitelist to make sure we only show preferences that
49 // are useful for support and won't compromise the user's privacy.  Note that
50 // entries are *prefixes*: for example, "accessibility." applies to all prefs
51 // under the "accessibility.*" branch.
52 const PREFS_WHITELIST = [
53   "accessibility.",
54   "browser.fixup.",
55   "browser.history_expire_",
56   "browser.link.open_newwindow",
57   "browser.mousewheel.",
58   "browser.places.",
59   "browser.startup.homepage",
60   "browser.tabs.",
61   "browser.zoom.",
62   "dom.",
63   "extensions.checkCompatibility",
64   "extensions.lastAppVersion",
65   "font.",
66   "general.useragent.",
67   "gfx.",
68   "mozilla.widget.render-mode",
69   "layers.",
70   "javascript.",
71   "keyword.",
72   "layout.css.dpi",
73   "network.",
74   "places.",
75   "print.",
76   "privacy.",
77   "security."
80 // The blacklist, unlike the whitelist, is a list of regular expressions.
81 const PREFS_BLACKLIST = [
82   /^network[.]proxy[.]/,
83   /[.]print_to_filename$/,
86 window.onload = function () {
87   // Get the support URL.
88   let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
89                        .getService(Ci.nsIURLFormatter);
90   let supportUrl = urlFormatter.formatURLPref("app.support.baseURL");
92   // Update the application basics section.
93   document.getElementById("application-box").textContent = Services.appinfo.name;
94   document.getElementById("version-box").textContent = Services.appinfo.version;
95   document.getElementById("useragent-box").textContent = navigator.userAgent;
96   document.getElementById("supportLink").href = supportUrl;
98   // Update the other sections.
99   populatePreferencesSection();
100   populateExtensionsSection();
101   populateGraphicsSection();
104 function populateExtensionsSection() {
105   AddonManager.getAddonsByTypes(["extension"], function(extensions) {
106     let trExtensions = [];
107     for (let i = 0; i < extensions.length; i++) {
108       let extension = extensions[i];
109       let tr = createParentElement("tr", [
110         createElement("td", extension.name),
111         createElement("td", extension.version),
112         createElement("td", extension.isActive),
113         createElement("td", extension.id),
114       ]);
115       trExtensions.push(tr);
116     }
117     appendChildren(document.getElementById("extensions-tbody"), trExtensions);
118   });
121 function populatePreferencesSection() {
122   let modifiedPrefs = getModifiedPrefs();
124   function comparePrefs(pref1, pref2) {
125     if (pref1.name < pref2.name)
126       return -1;
127     if (pref1.name > pref2.name)
128       return 1;
129     return 0;
130   }
132   let sortedPrefs = modifiedPrefs.sort(comparePrefs);
134   let trPrefs = [];
135   sortedPrefs.forEach(function (pref) {
136     let tdName = createElement("td", pref.name, "pref-name");
137     let tdValue = createElement("td", formatPrefValue(pref.value), "pref-value");
138     let tr = createParentElement("tr", [tdName, tdValue]);
139     trPrefs.push(tr);
140   });
142   appendChildren(document.getElementById("prefs-tbody"), trPrefs);
145 function populateGraphicsSection() {
146   function createHeader(name)
147   {
148     let elem = createElement("th", name);
149     elem.className = "column";
150     return elem;
151   }
153   let bundle = Services.strings.createBundle("chrome://global/locale/aboutSupport.properties");
154   let graphics_tbody = document.getElementById("graphics-tbody");
156   var gfxInfo = null;
157   try {
158     // nsIGfxInfo is currently only implemented on Windows
159     gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
160   } catch(e) {}
162   if (gfxInfo) {
163     let trGraphics = [];
164     trGraphics.push(createParentElement("tr", [
165       createHeader(bundle.GetStringFromName("adapterDescription")),
166       createElement("td", gfxInfo.adapterDescription),
167     ]));
168     trGraphics.push(createParentElement("tr", [
169       createHeader(bundle.GetStringFromName("adapterVendorID")),
170       // pad with zeros. (printf would be nicer)
171       createElement("td", String('0000'+gfxInfo.adapterVendorID.toString(16)).slice(-4)),
172     ]));
173     trGraphics.push(createParentElement("tr", [
174       createHeader(bundle.GetStringFromName("adapterDeviceID")),
175       // pad with zeros. (printf would be nicer)
176       createElement("td", String('0000'+gfxInfo.adapterDeviceID.toString(16)).slice(-4)),
177     ]));
178     trGraphics.push(createParentElement("tr", [
179       createHeader(bundle.GetStringFromName("adapterRAM")),
180       createElement("td", gfxInfo.adapterRAM),
181     ]));
182     trGraphics.push(createParentElement("tr", [
183       createHeader(bundle.GetStringFromName("adapterDrivers")),
184       createElement("td", gfxInfo.adapterDriver),
185     ]));
186     trGraphics.push(createParentElement("tr", [
187       createHeader(bundle.GetStringFromName("driverVersion")),
188       createElement("td", gfxInfo.adapterDriverVersion),
189     ]));
190     trGraphics.push(createParentElement("tr", [
191       createHeader(bundle.GetStringFromName("driverDate")),
192       createElement("td", gfxInfo.adapterDriverDate),
193     ]));
195     var d2dEnabled = gfxInfo.D2DEnabled;
196     var d2dMessage = d2dEnabled;
197     if (!d2dEnabled) {
198       var d2dStatus = -1; // different from any status value defined in the IDL
199       try {
200         d2dStatus = gfxInfo.getFeatureStatus(gfxInfo.FEATURE_DIRECT2D);
201       } catch(e) {
202         window.dump(e + '\n');
203       }  
204       if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DEVICE ||
205           d2dStatus == gfxInfo.FEATURE_DISCOURAGED)
206       {
207         d2dMessage = bundle.GetStringFromName("blockedGraphicsCard");
208       }
209       else if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DRIVER_VERSION)
210       {
211         var d2dSuggestedDriverVersion = null;
212         try {
213           gfxInfo.getFeatureSuggestedDriverVersion(gfxInfo.FEATURE_DIRECT2D);
214         } catch(e) {
215           window.dump(e + '\n');
216         }
217         if (d2dSuggestedDriverVersion) {
218           d2dMessage = bundle.GetStringFromName("tryNewerDriverVersion").replace("%1", d2dSuggestedDriverVersion);
219         }
220       }
221     }
222     trGraphics.push(createParentElement("tr", [
223       createHeader(bundle.GetStringFromName("direct2DEnabled")),
224       createElement("td", d2dMessage),
225     ]));
227     trGraphics.push(createParentElement("tr", [
228       createHeader(bundle.GetStringFromName("directWriteEnabled")),
229       createElement("td", gfxInfo.DWriteEnabled),
230     ]));
232     appendChildren(graphics_tbody, trGraphics);
234   } // end if (gfxInfo)
236   let windows = Services.ww.getWindowEnumerator();
237   let acceleratedWindows = 0;
238   let totalWindows = 0;
239   let mgrType;
240   while (windows.hasMoreElements()) {
241     totalWindows++;
243     let awindow = windows.getNext().QueryInterface(Ci.nsIInterfaceRequestor);
244     let windowutils = awindow.getInterface(Ci.nsIDOMWindowUtils);
245     if (windowutils.layerManagerType != "Basic") {
246       acceleratedWindows++;
247       mgrType = windowutils.layerManagerType;
248     }
249   }
251   let msg = acceleratedWindows + "/" + totalWindows;
252   if (acceleratedWindows)
253     msg += " " + mgrType;
255   let header = createHeader(bundle.GetStringFromName("acceleratedWindows"));
257   appendChildren(graphics_tbody, [ header, createElement("td", msg) ]);
260 function getPrefValue(aName) {
261   let value = "";
262   let type = Services.prefs.getPrefType(aName);
263   switch (type) {
264     case Ci.nsIPrefBranch2.PREF_STRING:
265       value = Services.prefs.getComplexValue(aName, Ci.nsISupportsString).data;
266       break;
267     case Ci.nsIPrefBranch2.PREF_BOOL:
268       value = Services.prefs.getBoolPref(aName);
269       break;
270     case Ci.nsIPrefBranch2.PREF_INT:
271       value = Services.prefs.getIntPref(aName);
272       break;
273   }
275   return { name: aName, value: value };
278 function formatPrefValue(prefValue) {
279   // Some pref values are really long and don't have spaces.  This can cause
280   // problems when copying and pasting into some WYSIWYG editors.  In general
281   // the exact contents of really long pref values aren't particularly useful,
282   // so we truncate them to some reasonable length.
283   let maxPrefValueLen = 120;
284   let text = "" + prefValue;
285   if (text.length > maxPrefValueLen)
286     text = text.substring(0, maxPrefValueLen) + ELLIPSIS;
287   return text;
290 function getModifiedPrefs() {
291   // We use the low-level prefs API to identify prefs that have been
292   // modified, rather that Application.prefs.all since the latter is
293   // much, much slower.  Application.prefs.all also gets slower each
294   // time it's called.  See bug 517312.
295   let prefNames = getWhitelistedPrefNames();
296   let prefs = [getPrefValue(prefName)
297                       for each (prefName in prefNames)
298                           if (Services.prefs.prefHasUserValue(prefName)
299                             && !isBlacklisted(prefName))];
300   return prefs;
303 function getWhitelistedPrefNames() {
304   let results = [];
305   PREFS_WHITELIST.forEach(function (prefStem) {
306     let prefNames = Services.prefs.getChildList(prefStem);
307     results = results.concat(prefNames);
308   });
309   return results;
312 function isBlacklisted(prefName) {
313   return PREFS_BLACKLIST.some(function (re) re.test(prefName));
316 function createParentElement(tagName, childElems) {
317   let elem = document.createElement(tagName);
318   appendChildren(elem, childElems);
319   return elem;
322 function createElement(tagName, textContent, opt_class) {
323   let elem = document.createElement(tagName);
324   elem.textContent = textContent;
325   elem.className = opt_class || "";
326   return elem;
329 function appendChildren(parentElem, childNodes) {
330   for (let i = 0; i < childNodes.length; i++)
331     parentElem.appendChild(childNodes[i]);
334 function copyContentsToClipboard() {
335   // Get the HTML and text representations for the important part of the page.
336   let contentsDiv = document.getElementById("contents");
337   let dataHtml = contentsDiv.innerHTML;
338   let dataText = createTextForElement(contentsDiv);
340   // We can't use plain strings, we have to use nsSupportsString.
341   let supportsStringClass = Cc["@mozilla.org/supports-string;1"];
342   let ssHtml = supportsStringClass.createInstance(Ci.nsISupportsString);
343   let ssText = supportsStringClass.createInstance(Ci.nsISupportsString);
345   let transferable = Cc["@mozilla.org/widget/transferable;1"]
346                        .createInstance(Ci.nsITransferable);
348   // Add the HTML flavor.
349   transferable.addDataFlavor("text/html");
350   ssHtml.data = dataHtml;
351   transferable.setTransferData("text/html", ssHtml, dataHtml.length * 2);
353   // Add the plain text flavor.
354   transferable.addDataFlavor("text/unicode");
355   ssText.data = dataText;
356   transferable.setTransferData("text/unicode", ssText, dataText.length * 2);
358   // Store the data into the clipboard.
359   let clipboard = Cc["@mozilla.org/widget/clipboard;1"]
360                     .getService(Ci.nsIClipboard);
361   clipboard.setData(transferable, null, clipboard.kGlobalClipboard);
364 // Return the plain text representation of an element.  Do a little bit
365 // of pretty-printing to make it human-readable.
366 function createTextForElement(elem) {
367   // Generate the initial text.
368   let textFragmentAccumulator = [];
369   generateTextForElement(elem, "", textFragmentAccumulator);
370   let text = textFragmentAccumulator.join("");
372   // Trim extraneous whitespace before newlines, then squash extraneous
373   // blank lines.
374   text = text.replace(/[ \t]+\n/g, "\n");
375   text = text.replace(/\n\n\n+/g, "\n\n");
377   // Actual CR/LF pairs are needed for some Windows text editors.
378 #ifdef XP_WIN
379   text = text.replace(/\n/g, "\r\n");
380 #endif
382   return text;
385 function generateTextForElement(elem, indent, textFragmentAccumulator) {
386   // Add a little extra spacing around most elements.
387   if (elem.tagName != "td")
388     textFragmentAccumulator.push("\n");
390   // Generate the text representation for each child node.
391   let node = elem.firstChild;
392   while (node) {
394     if (node.nodeType == Node.TEXT_NODE) {
395       // Text belonging to this element uses its indentation level.
396       generateTextForTextNode(node, indent, textFragmentAccumulator);
397     }
398     else if (node.nodeType == Node.ELEMENT_NODE) {
399       // Recurse on the child element with an extra level of indentation.
400       generateTextForElement(node, indent + "  ", textFragmentAccumulator);
401     }
403     // Advance!
404     node = node.nextSibling;
405   }
408 function generateTextForTextNode(node, indent, textFragmentAccumulator) {
409   // If the text node is the first of a run of text nodes, then start
410   // a new line and add the initial indentation.
411   let prevNode = node.previousSibling;
412   if (!prevNode || prevNode.nodeType == Node.TEXT_NODE)
413     textFragmentAccumulator.push("\n" + indent);
415   // Trim the text node's text content and add proper indentation after
416   // any internal line breaks.
417   let text = node.textContent.trim().replace("\n", "\n" + indent, "g");
418   textFragmentAccumulator.push(text);
421 function openProfileDirectory() {
422   // Get the profile directory.
423   let currProfD = Services.dirsvc.get("ProfD", Ci.nsIFile);
424   let profileDir = currProfD.path;
426   // Show the profile directory.
427   let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
428                                            "nsILocalFile", "initWithPath");
429   new nsLocalFile(profileDir).reveal();