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
14 # The Original Code is aboutSupport.xhtml.
16 # The Initial Developer of the Original Code is
18 # Portions created by the Initial Developer are Copyright (C) 2009
19 # the Initial Developer. All Rights Reserved.
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 = [
55 "browser.history_expire_",
56 "browser.link.open_newwindow",
57 "browser.mousewheel.",
59 "browser.startup.homepage",
63 "extensions.checkCompatibility",
64 "extensions.lastAppVersion",
68 "mozilla.widget.render-mode",
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),
115 trExtensions.push(tr);
117 appendChildren(document.getElementById("extensions-tbody"), trExtensions);
121 function populatePreferencesSection() {
122 let modifiedPrefs = getModifiedPrefs();
124 function comparePrefs(pref1, pref2) {
125 if (pref1.name < pref2.name)
127 if (pref1.name > pref2.name)
132 let sortedPrefs = modifiedPrefs.sort(comparePrefs);
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]);
142 appendChildren(document.getElementById("prefs-tbody"), trPrefs);
145 function populateGraphicsSection() {
146 function createHeader(name)
148 let elem = createElement("th", name);
149 elem.className = "column";
153 let bundle = Services.strings.createBundle("chrome://global/locale/aboutSupport.properties");
154 let graphics_tbody = document.getElementById("graphics-tbody");
158 // nsIGfxInfo is currently only implemented on Windows
159 gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
164 trGraphics.push(createParentElement("tr", [
165 createHeader(bundle.GetStringFromName("adapterDescription")),
166 createElement("td", gfxInfo.adapterDescription),
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)),
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)),
178 trGraphics.push(createParentElement("tr", [
179 createHeader(bundle.GetStringFromName("adapterRAM")),
180 createElement("td", gfxInfo.adapterRAM),
182 trGraphics.push(createParentElement("tr", [
183 createHeader(bundle.GetStringFromName("adapterDrivers")),
184 createElement("td", gfxInfo.adapterDriver),
186 trGraphics.push(createParentElement("tr", [
187 createHeader(bundle.GetStringFromName("driverVersion")),
188 createElement("td", gfxInfo.adapterDriverVersion),
190 trGraphics.push(createParentElement("tr", [
191 createHeader(bundle.GetStringFromName("driverDate")),
192 createElement("td", gfxInfo.adapterDriverDate),
195 var d2dEnabled = gfxInfo.D2DEnabled;
196 var d2dMessage = d2dEnabled;
198 var d2dStatus = -1; // different from any status value defined in the IDL
200 d2dStatus = gfxInfo.getFeatureStatus(gfxInfo.FEATURE_DIRECT2D);
202 window.dump(e + '\n');
204 if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DEVICE ||
205 d2dStatus == gfxInfo.FEATURE_DISCOURAGED)
207 d2dMessage = bundle.GetStringFromName("blockedGraphicsCard");
209 else if (d2dStatus == gfxInfo.FEATURE_BLOCKED_DRIVER_VERSION)
211 var d2dSuggestedDriverVersion = null;
213 gfxInfo.getFeatureSuggestedDriverVersion(gfxInfo.FEATURE_DIRECT2D);
215 window.dump(e + '\n');
217 if (d2dSuggestedDriverVersion) {
218 d2dMessage = bundle.GetStringFromName("tryNewerDriverVersion").replace("%1", d2dSuggestedDriverVersion);
222 trGraphics.push(createParentElement("tr", [
223 createHeader(bundle.GetStringFromName("direct2DEnabled")),
224 createElement("td", d2dMessage),
227 trGraphics.push(createParentElement("tr", [
228 createHeader(bundle.GetStringFromName("directWriteEnabled")),
229 createElement("td", gfxInfo.DWriteEnabled),
232 appendChildren(graphics_tbody, trGraphics);
234 } // end if (gfxInfo)
236 let windows = Services.ww.getWindowEnumerator();
237 let acceleratedWindows = 0;
238 let totalWindows = 0;
240 while (windows.hasMoreElements()) {
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;
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) {
262 let type = Services.prefs.getPrefType(aName);
264 case Ci.nsIPrefBranch2.PREF_STRING:
265 value = Services.prefs.getComplexValue(aName, Ci.nsISupportsString).data;
267 case Ci.nsIPrefBranch2.PREF_BOOL:
268 value = Services.prefs.getBoolPref(aName);
270 case Ci.nsIPrefBranch2.PREF_INT:
271 value = Services.prefs.getIntPref(aName);
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;
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))];
303 function getWhitelistedPrefNames() {
305 PREFS_WHITELIST.forEach(function (prefStem) {
306 let prefNames = Services.prefs.getChildList(prefStem);
307 results = results.concat(prefNames);
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);
322 function createElement(tagName, textContent, opt_class) {
323 let elem = document.createElement(tagName);
324 elem.textContent = textContent;
325 elem.className = opt_class || "";
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
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.
379 text = text.replace(/\n/g, "\r\n");
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;
394 if (node.nodeType == Node.TEXT_NODE) {
395 // Text belonging to this element uses its indentation level.
396 generateTextForTextNode(node, indent, textFragmentAccumulator);
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);
404 node = node.nextSibling;
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();