123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
- Cu.import("resource://gre/modules/XPCOMUtils.jsm");
- Cu.import("resource://gre/modules/Services.jsm");
- Cu.import("resource://gre/modules/Troubleshoot.jsm");
- Cu.import("resource://gre/modules/ResetProfile.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
- "resource://gre/modules/PlacesDBUtils.jsm");
- window.addEventListener("load", function onload(event) {
- try {
- window.removeEventListener("load", onload, false);
- Troubleshoot.snapshot(function (snapshot) {
- for (let prop in snapshotFormatters)
- snapshotFormatters[prop](snapshot[prop]);
- });
- populateActionBox();
- setupEventListeners();
- } catch (e) {
- Cu.reportError("stack of load error for about:support: " + e + ": " + e.stack);
- }
- }, false);
- // Each property in this object corresponds to a property in Troubleshoot.jsm's
- // snapshot data. Each function is passed its property's corresponding data,
- // and it's the function's job to update the page with it.
- var snapshotFormatters = {
- application: function application(data) {
- $("application-box").textContent = data.name;
- $("useragent-box").textContent = data.userAgent;
- $("os-box").textContent = data.osVersion;
- $("binary-box").textContent = Services.dirsvc.get("XREExeF", Ci.nsIFile).path;
- $("supportLink").href = data.supportURL;
- let version = Services.appinfo.version;
- if (data.versionArch) {
- version += " (" + data.versionArch + ")";
- }
- if (data.vendor)
- version += " (" + data.vendor + ")";
- $("version-box").textContent = version;
- $("buildid-box").textContent = data.buildID;
- if (data.updateChannel)
- $("updatechannel-box").textContent = data.updateChannel;
- $("safemode-box").textContent = data.safeMode;
- },
- crashes: function crashes(data) {
- return;
- },
- extensions: function extensions(data) {
- $.append($("extensions-tbody"), data.map(function (extension) {
- return $.new("tr", [
- $.new("td", extension.name),
- $.new("td", extension.version),
- $.new("td", extension.isActive),
- $.new("td", extension.id),
- ]);
- }));
- },
- modifiedPreferences: function modifiedPreferences(data) {
- $.append($("prefs-tbody"), sortedArrayFromObject(data).map(
- function ([name, value]) {
- return $.new("tr", [
- $.new("td", name, "pref-name"),
- // Very long preference values can cause users problems when they
- // copy and paste them into some text editors. Long values generally
- // aren't useful anyway, so truncate them to a reasonable length.
- $.new("td", String(value).substr(0, 120), "pref-value"),
- ]);
- }
- ));
- },
- lockedPreferences: function lockedPreferences(data) {
- $.append($("locked-prefs-tbody"), sortedArrayFromObject(data).map(
- function ([name, value]) {
- return $.new("tr", [
- $.new("td", name, "pref-name"),
- $.new("td", String(value).substr(0, 120), "pref-value"),
- ]);
- }
- ));
- },
- graphics: function graphics(data) {
- let strings = stringBundle();
- function localizedMsg(msgArray) {
- let nameOrMsg = msgArray.shift();
- if (msgArray.length) {
- // formatStringFromName logs an NS_ASSERTION failure otherwise that says
- // "use GetStringFromName". Lame.
- try {
- return strings.formatStringFromName(nameOrMsg, msgArray,
- msgArray.length);
- }
- catch (err) {
- // Throws if nameOrMsg is not a name in the bundle. This shouldn't
- // actually happen though, since msgArray.length > 1 => nameOrMsg is a
- // name in the bundle, not a message, and the remaining msgArray
- // elements are parameters.
- return nameOrMsg;
- }
- }
- try {
- return strings.GetStringFromName(nameOrMsg);
- }
- catch (err) {
- // Throws if nameOrMsg is not a name in the bundle.
- }
- return nameOrMsg;
- }
- // Read APZ info out of data.info, stripping it out in the process.
- let apzInfo = [];
- let formatApzInfo = function (info) {
- let out = [];
- for (let type of ['Wheel', 'Touch', 'Drag']) {
- let key = 'Apz' + type + 'Input';
- if (!(key in info))
- continue;
- delete info[key];
- let message = localizedMsg([type.toLowerCase() + 'Enabled']);
- out.push(message);
- }
- return out;
- };
- // Create a <tr> element with key and value columns.
- //
- // @key Text in the key column. Localized automatically, unless starts with "#".
- // @value Text in the value column. Not localized.
- function buildRow(key, value) {
- let title;
- if (key[0] == "#") {
- title = key.substr(1);
- } else {
- try {
- title = strings.GetStringFromName(key);
- } catch (e) {
- title = key;
- }
- }
- let td = $.new("td", value);
- td.style["white-space"] = "pre-wrap";
- return $.new("tr", [
- $.new("th", title, "column"),
- td,
- ]);
- }
- // @where The name in "graphics-<name>-tbody", of the element to append to.
- // @trs Array of row elements.
- function addRows(where, trs) {
- $.append($("graphics-" + where + "-tbody"), trs);
- }
- // Build and append a row.
- //
- // @where The name in "graphics-<name>-tbody", of the element to append to.
- function addRow(where, key, value) {
- addRows(where, [buildRow(key, value)]);
- }
- if (data.clearTypeParameters !== undefined) {
- addRow("diagnostics", "clearTypeParameters", data.clearTypeParameters);
- }
- if ("info" in data) {
- apzInfo = formatApzInfo(data.info);
- let trs = sortedArrayFromObject(data.info).map(function ([prop, val]) {
- return $.new("tr", [
- $.new("th", prop, "column"),
- $.new("td", String(val)),
- ]);
- });
- addRows("diagnostics", trs);
- delete data.info;
- }
- #ifdef NIGHTLY_BUILD
- let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- let gpuProcessPid = windowUtils.gpuProcessPid;
- if (gpuProcessPid != -1) {
- let gpuProcessKillButton = $.new("button");
- gpuProcessKillButton.addEventListener("click", function() {
- windowUtils.terminateGPUProcess();
- });
- gpuProcessKillButton.textContent = strings.GetStringFromName("gpuProcessKillButton");
- addRow("diagnostics", "GPUProcessPid", gpuProcessPid);
- addRow("diagnostics", "GPUProcess", [gpuProcessKillButton]);
- }
- #endif
- // graphics-failures-tbody tbody
- if ("failures" in data) {
- // If indices is there, it should be the same length as failures,
- // (see Troubleshoot.jsm) but we check anyway:
- if ("indices" in data && data.failures.length == data.indices.length) {
- let combined = [];
- for (let i = 0; i < data.failures.length; i++) {
- let assembled = assembleFromGraphicsFailure(i, data);
- combined.push(assembled);
- }
- combined.sort(function(a, b) {
- if (a.index < b.index) return -1;
- if (a.index > b.index) return 1;
- return 0;
- });
- $.append($("graphics-failures-tbody"),
- combined.map(function(val) {
- return $.new("tr", [$.new("th", val.header, "column"),
- $.new("td", val.message)]);
- }));
- delete data.indices;
- } else {
- $.append($("graphics-failures-tbody"),
- [$.new("tr", [$.new("th", "LogFailure", "column"),
- $.new("td", data.failures.map(function (val) {
- return $.new("p", val);
- }))])]);
- }
- } else {
- $("graphics-failures-tbody").style.display = "none";
- }
- // Add a new row to the table, and take the key (or keys) out of data.
- //
- // @where Table section to add to.
- // @key Data key to use.
- // @colKey The localization key to use, if different from key.
- function addRowFromKey(where, key, colKey) {
- if (!(key in data))
- return;
- colKey = colKey || key;
- let value;
- let messageKey = key + "Message";
- if (messageKey in data) {
- value = localizedMsg(data[messageKey]);
- delete data[messageKey];
- } else {
- value = data[key];
- }
- delete data[key];
- if (value) {
- addRow(where, colKey, value);
- }
- }
- // graphics-features-tbody
- let compositor = data.windowLayerManagerRemote
- ? data.windowLayerManagerType
- : "BasicLayers (" + strings.GetStringFromName("mainThreadNoOMTC") + ")";
- addRow("features", "compositing", compositor);
-
- let acceleratedWindows = data.numAcceleratedWindows + "/" + data.numTotalWindows;
- if (data.windowLayerManagerType) {
- acceleratedWindows += " " + data.windowLayerManagerType;
- }
- if (data.windowLayerManagerRemote) {
- acceleratedWindows += " (OMTC)";
- }
- if (data.numAcceleratedWindowsMessage) {
- acceleratedWindows += " " + localizedMsg(data.numAcceleratedWindowsMessage);
- }
- addRow("features", "acceleratedWindows", acceleratedWindows);
- delete data.windowLayerManagerRemote;
- delete data.windowLayerManagerType;
- delete data.numTotalWindows;
- delete data.numAcceleratedWindows;
- delete data.numAcceleratedWindowsMessage;
- addRow("features", "asyncPanZoom",
- apzInfo.length
- ? apzInfo.join("; ")
- : localizedMsg(["apzNone"]));
- addRowFromKey("features", "webgl1WSIInfo");
- addRowFromKey("features", "webgl1Renderer");
- addRowFromKey("features", "webgl1Version");
- addRowFromKey("features", "webgl1DriverExtensions");
- addRowFromKey("features", "webgl1Extensions");
- addRowFromKey("features", "webgl2WSIInfo");
- addRowFromKey("features", "webgl2Renderer");
- addRowFromKey("features", "webgl2Version");
- addRowFromKey("features", "webgl2DriverExtensions");
- addRowFromKey("features", "webgl2Extensions");
- addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
- addRowFromKey("features", "currentAudioBackend", "audioBackend");
- addRowFromKey("features", "direct2DEnabled", "#Direct2D");
- if ("directWriteEnabled" in data) {
- let message = data.directWriteEnabled;
- if ("directWriteVersion" in data)
- message += " (" + data.directWriteVersion + ")";
- addRow("features", "#DirectWrite", message);
- delete data.directWriteEnabled;
- delete data.directWriteVersion;
- }
- // Adapter tbodies.
- let adapterKeys = [
- ["adapterDescription", "gpuDescription"],
- ["adapterVendorID", "gpuVendorID"],
- ["adapterDeviceID", "gpuDeviceID"],
- ["driverVersion", "gpuDriverVersion"],
- ["driverDate", "gpuDriverDate"],
- ["adapterDrivers", "gpuDrivers"],
- ["adapterSubsysID", "gpuSubsysID"],
- ["adapterRAM", "gpuRAM"],
- ];
- function showGpu(id, suffix) {
- function get(prop) {
- return data[prop + suffix];
- }
- let trs = [];
- for (let [prop, key] of adapterKeys) {
- let value = get(prop);
- if (value === undefined || value === "")
- continue;
- trs.push(buildRow(key, value));
- }
- if (trs.length == 0) {
- $("graphics-" + id + "-tbody").style.display = "none";
- return;
- }
- let active = "yes";
- if ("isGPU2Active" in data && ((suffix == "2") != data.isGPU2Active)) {
- active = "no";
- }
- addRow(id, "gpuActive", strings.GetStringFromName(active));
- addRows(id, trs);
- }
- showGpu("gpu-1", "");
- showGpu("gpu-2", "2");
- // Remove adapter keys.
- for (let [prop, key] of adapterKeys) {
- delete data[prop];
- delete data[prop + "2"];
- }
- delete data.isGPU2Active;
- let featureLog = data.featureLog;
- delete data.featureLog;
- let features = [];
- for (let feature of featureLog.features) {
- // Only add interesting decisions - ones that were not automatic based on
- // all.js/gfxPrefs defaults.
- if (feature.log.length > 1 || feature.log[0].status != "available") {
- features.push(feature);
- }
- }
- if (features.length) {
- for (let feature of features) {
- let trs = [];
- for (let entry of feature.log) {
- if (entry.type == "default" && entry.status == "available")
- continue;
- let contents;
- if (entry.message.length > 0 && entry.message[0] == "#") {
- // This is a failure ID. See nsIGfxInfo.idl.
- let m;
- if (m = /#BLOCKLIST_FEATURE_FAILURE_BUG_(\d+)/.exec(entry.message)) {
- let bugSpan = $.new("span");
- bugSpan.textContent = strings.GetStringFromName("blocklistedBug") + "; ";
- let bugHref = $.new("a");
- bugHref.href = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + m[1];
- bugHref.textContent = strings.formatStringFromName("bugLink", [m[1]], 1);
- contents = [bugSpan, bugHref];
- } else {
- contents = strings.formatStringFromName(
- "unknownFailure", [entry.message.substr(1)], 1);
- }
- } else {
- contents = entry.status + " by " + entry.type + ": " + entry.message;
- }
- trs.push($.new("tr", [
- $.new("td", contents),
- ]));
- }
- addRow("decisions", feature.name, [$.new("table", trs)]);
- }
- } else {
- $("graphics-decisions-tbody").style.display = "none";
- }
- if (featureLog.fallbacks.length) {
- for (let fallback of featureLog.fallbacks) {
- addRow("workarounds", fallback.name, fallback.message);
- }
- } else {
- $("graphics-workarounds-tbody").style.display = "none";
- }
- let crashGuards = data.crashGuards;
- delete data.crashGuards;
- if (crashGuards.length) {
- for (let guard of crashGuards) {
- let resetButton = $.new("button");
- let onClickReset = (function (guard) {
- // Note - need this wrapper until bug 449811 fixes |guard| scoping.
- return function () {
- Services.prefs.setIntPref(guard.prefName, 0);
- resetButton.removeEventListener("click", onClickReset);
- resetButton.disabled = true;
- };
- })(guard);
- resetButton.textContent = strings.GetStringFromName("resetOnNextRestart");
- resetButton.addEventListener("click", onClickReset);
- addRow("crashguards", guard.type + "CrashGuard", [resetButton]);
- }
- } else {
- $("graphics-crashguards-tbody").style.display = "none";
- }
- // Now that we're done, grab any remaining keys in data and drop them into
- // the diagnostics section.
- for (let key in data) {
- let value = data[key];
- if (Array.isArray(value)) {
- value = localizedMsg(value);
- }
- addRow("diagnostics", key, value);
- }
- },
- javaScript: function javaScript(data) {
- $("javascript-incremental-gc").textContent = data.incrementalGCEnabled;
- },
- accessibility: function accessibility(data) {
- $("a11y-activated").textContent = data.isActive;
- $("a11y-force-disabled").textContent = data.forceDisabled || 0;
- },
- libraryVersions: function libraryVersions(data) {
- let strings = stringBundle();
- let trs = [
- $.new("tr", [
- $.new("th", ""),
- $.new("th", strings.GetStringFromName("minLibVersions")),
- $.new("th", strings.GetStringFromName("loadedLibVersions")),
- ])
- ];
- sortedArrayFromObject(data).forEach(
- function ([name, val]) {
- trs.push($.new("tr", [
- $.new("td", name),
- $.new("td", val.minVersion),
- $.new("td", val.version),
- ]));
- }
- );
- $.append($("libversions-tbody"), trs);
- },
- userJS: function userJS(data) {
- if (!data.exists)
- return;
- let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile);
- userJSFile.append("user.js");
- $("prefs-user-js-link").href = Services.io.newFileURI(userJSFile).spec;
- $("prefs-user-js-section").style.display = "";
- // Clear the no-copy class
- $("prefs-user-js-section").className = "";
- }
- };
- var $ = document.getElementById.bind(document);
- $.new = function $_new(tag, textContentOrChildren, className, attributes) {
- let elt = document.createElement(tag);
- if (className)
- elt.className = className;
- if (attributes) {
- for (let attrName in attributes)
- elt.setAttribute(attrName, attributes[attrName]);
- }
- if (Array.isArray(textContentOrChildren))
- this.append(elt, textContentOrChildren);
- else
- elt.textContent = String(textContentOrChildren);
- return elt;
- };
- $.append = function $_append(parent, children) {
- children.forEach(c => parent.appendChild(c));
- };
- function stringBundle() {
- return Services.strings.createBundle(
- "chrome://global/locale/aboutSupport.properties");
- }
- function assembleFromGraphicsFailure(i, data)
- {
- // Only cover the cases we have today; for example, we do not have
- // log failures that assert and we assume the log level is 1/error.
- let message = data.failures[i];
- let index = data.indices[i];
- let what = "";
- if (message.search(/\[GFX1-\]: \(LF\)/) == 0) {
- // Non-asserting log failure - the message is substring(14)
- what = "LogFailure";
- message = message.substring(14);
- } else if (message.search(/\[GFX1-\]: /) == 0) {
- // Non-asserting - the message is substring(9)
- what = "Error";
- message = message.substring(9);
- } else if (message.search(/\[GFX1\]: /) == 0) {
- // Asserting - the message is substring(8)
- what = "Assert";
- message = message.substring(8);
- }
- let assembled = {"index" : index,
- "header" : ("(#" + index + ") " + what),
- "message" : message};
- return assembled;
- }
- function sortedArrayFromObject(obj) {
- let tuples = [];
- for (let prop in obj)
- tuples.push([prop, obj[prop]]);
- tuples.sort(([prop1, v1], [prop2, v2]) => prop1.localeCompare(prop2));
- return tuples;
- }
- function getLoadContext() {
- return window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsILoadContext);
- }
- function copyContentsToClipboard() {
- // Get the HTML and text representations for the important part of the page.
- let contentsDiv = $("contents");
- let dataHtml = contentsDiv.innerHTML;
- let dataText = createTextForElement(contentsDiv);
- // We can't use plain strings, we have to use nsSupportsString.
- let supportsStringClass = Cc["@mozilla.org/supports-string;1"];
- let ssHtml = supportsStringClass.createInstance(Ci.nsISupportsString);
- let ssText = supportsStringClass.createInstance(Ci.nsISupportsString);
- let transferable = Cc["@mozilla.org/widget/transferable;1"]
- .createInstance(Ci.nsITransferable);
- transferable.init(getLoadContext());
- // 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);
- // Store the data into the clipboard.
- let clipboard = Cc["@mozilla.org/widget/clipboard;1"]
- .getService(Ci.nsIClipboard);
- clipboard.setData(transferable, null, clipboard.kGlobalClipboard);
- }
- // Return the plain text representation of an element. Do a little bit
- // of pretty-printing to make it human-readable.
- function createTextForElement(elem) {
- let serializer = new Serializer();
- let text = serializer.serialize(elem);
- #ifdef XP_WIN
- // Actual CR/LF pairs are needed for some Windows text editors.
- text = text.replace(/\n/g, "\r\n");
- #endif
- return text;
- }
- function Serializer() {
- }
- Serializer.prototype = {
- serialize: function (rootElem) {
- this._lines = [];
- this._startNewLine();
- this._serializeElement(rootElem);
- this._startNewLine();
- return this._lines.join("\n").trim() + "\n";
- },
- // The current line is always the line that writing will start at next. When
- // an element is serialized, the current line is updated to be the line at
- // which the next element should be written.
- get _currentLine() {
- return this._lines.length ? this._lines[this._lines.length - 1] : null;
- },
- set _currentLine(val) {
- return this._lines[this._lines.length - 1] = val;
- },
- _serializeElement: function (elem) {
- if (this._ignoreElement(elem))
- return;
- // table
- if (elem.localName == "table") {
- this._serializeTable(elem);
- return;
- }
- // all other elements
- let hasText = false;
- for (let child of elem.childNodes) {
- if (child.nodeType == Node.TEXT_NODE) {
- let text = this._nodeText(
- child, (child.classList && child.classList.contains("endline")));
- this._appendText(text);
- hasText = hasText || !!text.trim();
- }
- else if (child.nodeType == Node.ELEMENT_NODE)
- this._serializeElement(child);
- }
- // For headings, draw a "line" underneath them so they stand out.
- if (/^h[0-9]+$/.test(elem.localName)) {
- let headerText = (this._currentLine || "").trim();
- if (headerText) {
- this._startNewLine();
- this._appendText("-".repeat(headerText.length));
- }
- }
- // Add a blank line underneath block elements but only if they contain text.
- if (hasText) {
- let display = window.getComputedStyle(elem).getPropertyValue("display");
- if (display == "block") {
- this._startNewLine();
- this._startNewLine();
- }
- }
- },
- _startNewLine: function () {
- let currLine = this._currentLine;
- if (currLine) {
- // The current line is not empty. Trim it.
- this._currentLine = currLine.trim();
- if (!this._currentLine)
- // The current line became empty. Discard it.
- this._lines.pop();
- }
- this._lines.push("");
- },
- _appendText: function (text) {
- this._currentLine += text;
- },
- _isHiddenSubHeading: function (th) {
- return th.parentNode.parentNode.style.display == "none";
- },
- _serializeTable: function (table) {
- // Collect the table's column headings if in fact there are any. First
- // check thead. If there's no thead, check the first tr.
- let colHeadings = {};
- let tableHeadingElem = table.querySelector("thead");
- if (!tableHeadingElem)
- tableHeadingElem = table.querySelector("tr");
- if (tableHeadingElem) {
- let tableHeadingCols = tableHeadingElem.querySelectorAll("th,td");
- // If there's a contiguous run of th's in the children starting from the
- // rightmost child, then consider them to be column headings.
- for (let i = tableHeadingCols.length - 1; i >= 0; i--) {
- let col = tableHeadingCols[i];
- if (col.localName != "th" || col.classList.contains("title-column"))
- break;
- colHeadings[i] = this._nodeText(
- col, (col.classList && col.classList.contains("endline"))).trim();
- }
- }
- let hasColHeadings = Object.keys(colHeadings).length > 0;
- if (!hasColHeadings)
- tableHeadingElem = null;
- let trs = table.querySelectorAll("table > tr, tbody > tr");
- let startRow =
- tableHeadingElem && tableHeadingElem.localName == "tr" ? 1 : 0;
- if (startRow >= trs.length)
- // The table's empty.
- return;
- if (hasColHeadings && !this._ignoreElement(tableHeadingElem)) {
- // Use column headings. Print each tr as a multi-line chunk like:
- // Heading 1: Column 1 value
- // Heading 2: Column 2 value
- for (let i = startRow; i < trs.length; i++) {
- if (this._ignoreElement(trs[i]))
- continue;
- let children = trs[i].querySelectorAll("td");
- for (let j = 0; j < children.length; j++) {
- let text = "";
- if (colHeadings[j])
- text += colHeadings[j] + ": ";
- text += this._nodeText(
- children[j],
- (children[j].classList &&
- children[j].classList.contains("endline"))).trim();
- this._appendText(text);
- this._startNewLine();
- }
- this._startNewLine();
- }
- return;
- }
- // Don't use column headings. Assume the table has only two columns and
- // print each tr in a single line like:
- // Column 1 value: Column 2 value
- for (let i = startRow; i < trs.length; i++) {
- if (this._ignoreElement(trs[i]))
- continue;
- let children = trs[i].querySelectorAll("th,td");
- let rowHeading = this._nodeText(
- children[0],
- (children[0].classList &&
- children[0].classList.contains("endline"))).trim();
- if (children[0].classList.contains("title-column")) {
- if (!this._isHiddenSubHeading(children[0]))
- this._appendText(rowHeading);
- } else if (children.length == 1) {
- // This is a single-cell row.
- this._appendText(rowHeading);
- } else {
- let childTables = trs[i].querySelectorAll("table");
- if (childTables.length) {
- // If we have child tables, don't use nodeText - its trs are already
- // queued up from querySelectorAll earlier.
- this._appendText(rowHeading + ": ");
- } else {
- this._appendText(rowHeading + ": " + this._nodeText(
- children[1],
- (children[1].classList &&
- children[1].classList.contains("endline"))).trim());
- }
- }
- this._startNewLine();
- }
- this._startNewLine();
- },
- _ignoreElement: function (elem) {
- return elem.classList.contains("no-copy");
- },
- _nodeText: function (node, endline) {
- let whiteChars = /\s+/g
- let whiteCharsButNoEndline = /(?!\n)[\s]+/g;
- let _node = node.cloneNode(true);
- if (_node.firstElementChild &&
- (_node.firstElementChild.nodeName.toLowerCase() == "button")) {
- _node.removeChild(_node.firstElementChild);
- }
- return _node.textContent.replace(
- endline ? whiteCharsButNoEndline : whiteChars, " ");
- },
- };
- 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();
- }
- /**
- * Profile reset is only supported for the default profile if the appropriate migrator exists.
- */
- function populateActionBox() {
- if (ResetProfile.resetSupported()) {
- $("reset-box").style.display = "block";
- $("action-box").style.display = "block";
- }
- if (!Services.appinfo.inSafeMode) {
- $("safe-mode-box").style.display = "block";
- $("action-box").style.display = "block";
- }
- }
- // Prompt user to restart the browser
- function restart(safeMode) {
- let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
- .createInstance(Ci.nsISupportsPRBool);
- Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
- if (cancelQuit.data) {
- return;
- }
- let flags = Ci.nsIAppStartup.eAttemptQuit;
- if (safeMode) {
- Services.startup.restartInSafeMode(flags);
- } else {
- Services.startup.quit(flags | Ci.nsIAppStartup.eRestart);
- }
- }
- /**
- * Set up event listeners for buttons.
- */
- function setupEventListeners() {
- #ifdef MOZ_UPDATER
- $("show-update-history-button").addEventListener("click", function(event) {
- var prompter = Cc["@mozilla.org/updates/update-prompt;1"].createInstance(Ci.nsIUpdatePrompt);
- prompter.showUpdateHistory(window);
- });
- #endif
- $("reset-box-button").addEventListener("click", function(event) {
- ResetProfile.openConfirmationDialog(window);
- });
- $("copy-to-clipboard").addEventListener("click", function(event) {
- copyContentsToClipboard();
- });
- $("profile-dir-button").addEventListener("click", function(event) {
- openProfileDirectory();
- });
- $("restart-in-safe-mode-button").addEventListener("click", function(event) {
- if (Services.obs.enumerateObservers("restart-in-safe-mode").hasMoreElements()) {
- Services.obs.notifyObservers(null, "restart-in-safe-mode", "");
- } else {
- restart(true);
- }
- });
- $("restart-button").addEventListener("click", function(event) {
- restart(false);
- });
- $("verify-place-integrity-button").addEventListener("click", function(event) {
- PlacesDBUtils.checkAndFixDatabase(function(aLog) {
- let msg = aLog.join("\n");
- $("verify-place-result").style.display = "block";
- $("verify-place-result-parent").classList.remove("no-copy");
- $("verify-place-result").textContent = msg;
- });
- });
- }
|