1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/Troubleshoot.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,432 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +this.EXPORTED_SYMBOLS = [ 1.9 + "Troubleshoot", 1.10 +]; 1.11 + 1.12 +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; 1.13 + 1.14 +Cu.import("resource://gre/modules/AddonManager.jsm"); 1.15 +Cu.import("resource://gre/modules/Services.jsm"); 1.16 + 1.17 +#ifdef MOZ_CRASHREPORTER 1.18 +Cu.import("resource://gre/modules/CrashReports.jsm"); 1.19 +#endif 1.20 + 1.21 +let Experiments; 1.22 +try { 1.23 + Experiments = Cu.import("resource:///modules/experiments/Experiments.jsm").Experiments; 1.24 +} 1.25 +catch (e) { 1.26 +} 1.27 + 1.28 +// We use a preferences whitelist to make sure we only show preferences that 1.29 +// are useful for support and won't compromise the user's privacy. Note that 1.30 +// entries are *prefixes*: for example, "accessibility." applies to all prefs 1.31 +// under the "accessibility.*" branch. 1.32 +const PREFS_WHITELIST = [ 1.33 + "accessibility.", 1.34 + "browser.cache.", 1.35 + "browser.display.", 1.36 + "browser.fixup.", 1.37 + "browser.history_expire_", 1.38 + "browser.link.open_newwindow", 1.39 + "browser.newtab.url", 1.40 + "browser.places.", 1.41 + "browser.privatebrowsing.", 1.42 + "browser.search.context.loadInBackground", 1.43 + "browser.search.log", 1.44 + "browser.search.openintab", 1.45 + "browser.search.param", 1.46 + "browser.search.searchEnginesURL", 1.47 + "browser.search.suggest.enabled", 1.48 + "browser.search.update", 1.49 + "browser.search.useDBForOrder", 1.50 + "browser.sessionstore.", 1.51 + "browser.startup.homepage", 1.52 + "browser.tabs.", 1.53 + "browser.urlbar.", 1.54 + "browser.zoom.", 1.55 + "dom.", 1.56 + "extensions.checkCompatibility", 1.57 + "extensions.lastAppVersion", 1.58 + "font.", 1.59 + "general.autoScroll", 1.60 + "general.useragent.", 1.61 + "gfx.", 1.62 + "html5.", 1.63 + "image.", 1.64 + "javascript.", 1.65 + "keyword.", 1.66 + "layers.", 1.67 + "layout.css.dpi", 1.68 + "media.", 1.69 + "mousewheel.", 1.70 + "network.", 1.71 + "permissions.default.image", 1.72 + "places.", 1.73 + "plugin.", 1.74 + "plugins.", 1.75 + "print.", 1.76 + "privacy.", 1.77 + "security.", 1.78 + "social.enabled", 1.79 + "storage.vacuum.last.", 1.80 + "svg.", 1.81 + "toolkit.startup.recent_crashes", 1.82 + "webgl.", 1.83 +]; 1.84 + 1.85 +// The blacklist, unlike the whitelist, is a list of regular expressions. 1.86 +const PREFS_BLACKLIST = [ 1.87 + /^network[.]proxy[.]/, 1.88 + /[.]print_to_filename$/, 1.89 +]; 1.90 + 1.91 +this.Troubleshoot = { 1.92 + 1.93 + /** 1.94 + * Captures a snapshot of data that may help troubleshooters troubleshoot 1.95 + * trouble. 1.96 + * 1.97 + * @param done A function that will be asynchronously called when the 1.98 + * snapshot completes. It will be passed the snapshot object. 1.99 + */ 1.100 + snapshot: function snapshot(done) { 1.101 + let snapshot = {}; 1.102 + let numPending = Object.keys(dataProviders).length; 1.103 + function providerDone(providerName, providerData) { 1.104 + snapshot[providerName] = providerData; 1.105 + if (--numPending == 0) 1.106 + // Ensure that done is always and truly called asynchronously. 1.107 + Services.tm.mainThread.dispatch(done.bind(null, snapshot), 1.108 + Ci.nsIThread.DISPATCH_NORMAL); 1.109 + } 1.110 + for (let name in dataProviders) { 1.111 + try { 1.112 + dataProviders[name](providerDone.bind(null, name)); 1.113 + } 1.114 + catch (err) { 1.115 + let msg = "Troubleshoot data provider failed: " + name + "\n" + err; 1.116 + Cu.reportError(msg); 1.117 + providerDone(name, msg); 1.118 + } 1.119 + } 1.120 + }, 1.121 + 1.122 + kMaxCrashAge: 3 * 24 * 60 * 60 * 1000, // 3 days 1.123 +}; 1.124 + 1.125 +// Each data provider is a name => function mapping. When a snapshot is 1.126 +// captured, each provider's function is called, and it's the function's job to 1.127 +// generate the provider's data. The function is passed a "done" callback, and 1.128 +// when done, it must pass its data to the callback. The resulting snapshot 1.129 +// object will contain a name => data entry for each provider. 1.130 +let dataProviders = { 1.131 + 1.132 + application: function application(done) { 1.133 + let data = { 1.134 + name: Services.appinfo.name, 1.135 + version: Services.appinfo.version, 1.136 + userAgent: Cc["@mozilla.org/network/protocol;1?name=http"]. 1.137 + getService(Ci.nsIHttpProtocolHandler). 1.138 + userAgent, 1.139 + }; 1.140 + try { 1.141 + data.vendor = Services.prefs.getCharPref("app.support.vendor"); 1.142 + } 1.143 + catch (e) {} 1.144 + let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. 1.145 + getService(Ci.nsIURLFormatter); 1.146 + try { 1.147 + data.supportURL = urlFormatter.formatURLPref("app.support.baseURL"); 1.148 + } 1.149 + catch (e) {} 1.150 + done(data); 1.151 + }, 1.152 + 1.153 +#ifdef MOZ_CRASHREPORTER 1.154 + crashes: function crashes(done) { 1.155 + let reports = CrashReports.getReports(); 1.156 + let now = new Date(); 1.157 + let reportsNew = reports.filter(report => (now - report.date < Troubleshoot.kMaxCrashAge)); 1.158 + let reportsSubmitted = reportsNew.filter(report => (!report.pending)); 1.159 + let reportsPendingCount = reportsNew.length - reportsSubmitted.length; 1.160 + let data = {submitted : reportsSubmitted, pending : reportsPendingCount}; 1.161 + done(data); 1.162 + }, 1.163 +#endif 1.164 + 1.165 + extensions: function extensions(done) { 1.166 + AddonManager.getAddonsByTypes(["extension"], function (extensions) { 1.167 + extensions.sort(function (a, b) { 1.168 + if (a.isActive != b.isActive) 1.169 + return b.isActive ? 1 : -1; 1.170 + let lc = a.name.localeCompare(b.name); 1.171 + if (lc != 0) 1.172 + return lc; 1.173 + if (a.version != b.version) 1.174 + return a.version > b.version ? 1 : -1; 1.175 + return 0; 1.176 + }); 1.177 + let props = ["name", "version", "isActive", "id"]; 1.178 + done(extensions.map(function (ext) { 1.179 + return props.reduce(function (extData, prop) { 1.180 + extData[prop] = ext[prop]; 1.181 + return extData; 1.182 + }, {}); 1.183 + })); 1.184 + }); 1.185 + }, 1.186 + 1.187 + experiments: function experiments(done) { 1.188 + if (Experiments === undefined) { 1.189 + done([]); 1.190 + return; 1.191 + } 1.192 + 1.193 + // getExperiments promises experiment history 1.194 + Experiments.instance().getExperiments().then( 1.195 + experiments => done(experiments) 1.196 + ); 1.197 + }, 1.198 + 1.199 + modifiedPreferences: function modifiedPreferences(done) { 1.200 + function getPref(name) { 1.201 + let table = {}; 1.202 + table[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref"; 1.203 + table[Ci.nsIPrefBranch.PREF_INT] = "getIntPref"; 1.204 + table[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref"; 1.205 + let type = Services.prefs.getPrefType(name); 1.206 + if (!(type in table)) 1.207 + throw new Error("Unknown preference type " + type + " for " + name); 1.208 + return Services.prefs[table[type]](name); 1.209 + } 1.210 + done(PREFS_WHITELIST.reduce(function (prefs, branch) { 1.211 + Services.prefs.getChildList(branch).forEach(function (name) { 1.212 + if (Services.prefs.prefHasUserValue(name) && 1.213 + !PREFS_BLACKLIST.some(function (re) re.test(name))) 1.214 + prefs[name] = getPref(name); 1.215 + }); 1.216 + return prefs; 1.217 + }, {})); 1.218 + }, 1.219 + 1.220 + graphics: function graphics(done) { 1.221 + function statusMsgForFeature(feature) { 1.222 + // We return an array because in the tryNewerDriver case we need to 1.223 + // include the suggested version, which the consumer likely needs to plug 1.224 + // into a format string from a localization file. Rather than returning 1.225 + // a string in some cases and an array in others, return an array always. 1.226 + let msg = [""]; 1.227 + try { 1.228 + var status = gfxInfo.getFeatureStatus(feature); 1.229 + } 1.230 + catch (e) {} 1.231 + switch (status) { 1.232 + case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE: 1.233 + case Ci.nsIGfxInfo.FEATURE_DISCOURAGED: 1.234 + msg = ["blockedGfxCard"]; 1.235 + break; 1.236 + case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION: 1.237 + msg = ["blockedOSVersion"]; 1.238 + break; 1.239 + case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION: 1.240 + try { 1.241 + var suggestedDriverVersion = 1.242 + gfxInfo.getFeatureSuggestedDriverVersion(feature); 1.243 + } 1.244 + catch (e) {} 1.245 + msg = suggestedDriverVersion ? 1.246 + ["tryNewerDriver", suggestedDriverVersion] : 1.247 + ["blockedDriver"]; 1.248 + break; 1.249 + } 1.250 + return msg; 1.251 + } 1.252 + 1.253 + let data = {}; 1.254 + 1.255 + try { 1.256 + // nsIGfxInfo may not be implemented on some platforms. 1.257 + var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); 1.258 + } 1.259 + catch (e) {} 1.260 + 1.261 + data.numTotalWindows = 0; 1.262 + data.numAcceleratedWindows = 0; 1.263 + let winEnumer = Services.ww.getWindowEnumerator(); 1.264 + while (winEnumer.hasMoreElements()) { 1.265 + data.numTotalWindows++; 1.266 + let winUtils = winEnumer.getNext(). 1.267 + QueryInterface(Ci.nsIInterfaceRequestor). 1.268 + getInterface(Ci.nsIDOMWindowUtils); 1.269 + try { 1.270 + data.windowLayerManagerType = winUtils.layerManagerType; 1.271 + data.windowLayerManagerRemote = winUtils.layerManagerRemote; 1.272 + } 1.273 + catch (e) { 1.274 + continue; 1.275 + } 1.276 + if (data.windowLayerManagerType != "Basic") 1.277 + data.numAcceleratedWindows++; 1.278 + } 1.279 + 1.280 + if (!data.numAcceleratedWindows && gfxInfo) { 1.281 + let feature = 1.282 +#ifdef XP_WIN 1.283 + gfxInfo.FEATURE_DIRECT3D_9_LAYERS; 1.284 +#else 1.285 + gfxInfo.FEATURE_OPENGL_LAYERS; 1.286 +#endif 1.287 + data.numAcceleratedWindowsMessage = statusMsgForFeature(feature); 1.288 + } 1.289 + 1.290 + if (!gfxInfo) { 1.291 + done(data); 1.292 + return; 1.293 + } 1.294 + 1.295 + // keys are the names of attributes on nsIGfxInfo, values become the names 1.296 + // of the corresponding properties in our data object. A null value means 1.297 + // no change. This is needed so that the names of properties in the data 1.298 + // object are the same as the names of keys in aboutSupport.properties. 1.299 + let gfxInfoProps = { 1.300 + adapterDescription: null, 1.301 + adapterVendorID: null, 1.302 + adapterDeviceID: null, 1.303 + adapterRAM: null, 1.304 + adapterDriver: "adapterDrivers", 1.305 + adapterDriverVersion: "driverVersion", 1.306 + adapterDriverDate: "driverDate", 1.307 + 1.308 + adapterDescription2: null, 1.309 + adapterVendorID2: null, 1.310 + adapterDeviceID2: null, 1.311 + adapterRAM2: null, 1.312 + adapterDriver2: "adapterDrivers2", 1.313 + adapterDriverVersion2: "driverVersion2", 1.314 + adapterDriverDate2: "driverDate2", 1.315 + isGPU2Active: null, 1.316 + 1.317 + D2DEnabled: "direct2DEnabled", 1.318 + DWriteEnabled: "directWriteEnabled", 1.319 + DWriteVersion: "directWriteVersion", 1.320 + cleartypeParameters: "clearTypeParameters", 1.321 + }; 1.322 + 1.323 + for (let prop in gfxInfoProps) { 1.324 + try { 1.325 + data[gfxInfoProps[prop] || prop] = gfxInfo[prop]; 1.326 + } 1.327 + catch (e) {} 1.328 + } 1.329 + 1.330 + if (("direct2DEnabled" in data) && !data.direct2DEnabled) 1.331 + data.direct2DEnabledMessage = 1.332 + statusMsgForFeature(Ci.nsIGfxInfo.FEATURE_DIRECT2D); 1.333 + 1.334 + let doc = 1.335 + Cc["@mozilla.org/xmlextras/domparser;1"] 1.336 + .createInstance(Ci.nsIDOMParser) 1.337 + .parseFromString("<html/>", "text/html"); 1.338 + 1.339 + let canvas = doc.createElement("canvas"); 1.340 + canvas.width = 1; 1.341 + canvas.height = 1; 1.342 + 1.343 + let gl; 1.344 + try { 1.345 + gl = canvas.getContext("experimental-webgl"); 1.346 + } catch(e) {} 1.347 + 1.348 + if (gl) { 1.349 + let ext = gl.getExtension("WEBGL_debug_renderer_info"); 1.350 + // this extension is unconditionally available to chrome. No need to check. 1.351 + data.webglRenderer = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL) 1.352 + + " -- " 1.353 + + gl.getParameter(ext.UNMASKED_RENDERER_WEBGL); 1.354 + } else { 1.355 + let feature = 1.356 +#ifdef XP_WIN 1.357 + // If ANGLE is not available but OpenGL is, we want to report on the 1.358 + // OpenGL feature, because that's what's going to get used. In all 1.359 + // other cases we want to report on the ANGLE feature. 1.360 + gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_ANGLE) != 1.361 + Ci.nsIGfxInfo.FEATURE_NO_INFO && 1.362 + gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_OPENGL) == 1.363 + Ci.nsIGfxInfo.FEATURE_NO_INFO ? 1.364 + Ci.nsIGfxInfo.FEATURE_WEBGL_OPENGL : 1.365 + Ci.nsIGfxInfo.FEATURE_WEBGL_ANGLE; 1.366 +#else 1.367 + Ci.nsIGfxInfo.FEATURE_WEBGL_OPENGL; 1.368 +#endif 1.369 + data.webglRendererMessage = statusMsgForFeature(feature); 1.370 + } 1.371 + 1.372 + let infoInfo = gfxInfo.getInfo(); 1.373 + if (infoInfo) 1.374 + data.info = infoInfo; 1.375 + 1.376 + let failures = gfxInfo.getFailures(); 1.377 + if (failures.length) 1.378 + data.failures = failures; 1.379 + 1.380 + done(data); 1.381 + }, 1.382 + 1.383 + javaScript: function javaScript(done) { 1.384 + let data = {}; 1.385 + let winEnumer = Services.ww.getWindowEnumerator(); 1.386 + if (winEnumer.hasMoreElements()) 1.387 + data.incrementalGCEnabled = winEnumer.getNext(). 1.388 + QueryInterface(Ci.nsIInterfaceRequestor). 1.389 + getInterface(Ci.nsIDOMWindowUtils). 1.390 + isIncrementalGCEnabled(); 1.391 + done(data); 1.392 + }, 1.393 + 1.394 + accessibility: function accessibility(done) { 1.395 + let data = {}; 1.396 + try { 1.397 + data.isActive = Components.manager.QueryInterface(Ci.nsIServiceManager). 1.398 + isServiceInstantiatedByContractID( 1.399 + "@mozilla.org/accessibilityService;1", 1.400 + Ci.nsISupports); 1.401 + } 1.402 + catch (e) { 1.403 + data.isActive = false; 1.404 + } 1.405 + try { 1.406 + data.forceDisabled = 1.407 + Services.prefs.getIntPref("accessibility.force_disabled"); 1.408 + } 1.409 + catch (e) {} 1.410 + done(data); 1.411 + }, 1.412 + 1.413 + libraryVersions: function libraryVersions(done) { 1.414 + let data = {}; 1.415 + let verInfo = Cc["@mozilla.org/security/nssversion;1"]. 1.416 + getService(Ci.nsINSSVersion); 1.417 + for (let prop in verInfo) { 1.418 + let match = /^([^_]+)_((Min)?Version)$/.exec(prop); 1.419 + if (match) { 1.420 + let verProp = match[2][0].toLowerCase() + match[2].substr(1); 1.421 + data[match[1]] = data[match[1]] || {}; 1.422 + data[match[1]][verProp] = verInfo[prop]; 1.423 + } 1.424 + } 1.425 + done(data); 1.426 + }, 1.427 + 1.428 + userJS: function userJS(done) { 1.429 + let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile); 1.430 + userJSFile.append("user.js"); 1.431 + done({ 1.432 + exists: userJSFile.exists() && userJSFile.fileSize > 0, 1.433 + }); 1.434 + }, 1.435 +};