Bug 1625482 [wpt PR 22496] - [ScrollTimeline] Do not show scrollbar to bypass flakine...
[gecko.git] / toolkit / modules / Troubleshoot.jsm
blobfbf432a316273c361484572ed32d77858f3bcc9d
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 var EXPORTED_SYMBOLS = ["Troubleshoot"];
7 const { AddonManager } = ChromeUtils.import(
8   "resource://gre/modules/AddonManager.jsm"
9 );
10 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
11 const { AppConstants } = ChromeUtils.import(
12   "resource://gre/modules/AppConstants.jsm"
14 const { E10SUtils } = ChromeUtils.import(
15   "resource://gre/modules/E10SUtils.jsm"
17 const { XPCOMUtils } = ChromeUtils.import(
18   "resource://gre/modules/XPCOMUtils.jsm"
20 XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser"]);
22 // We use a preferences whitelist to make sure we only show preferences that
23 // are useful for support and won't compromise the user's privacy.  Note that
24 // entries are *prefixes*: for example, "accessibility." applies to all prefs
25 // under the "accessibility.*" branch.
26 const PREFS_WHITELIST = [
27   "accessibility.",
28   "apz.",
29   "browser.cache.",
30   "browser.contentblocking.category",
31   "browser.display.",
32   "browser.download.folderList",
33   "browser.download.hide_plugins_without_extensions",
34   "browser.download.lastDir.savePerSite",
35   "browser.download.manager.addToRecentDocs",
36   "browser.download.manager.resumeOnWakeDelay",
37   "browser.download.preferred.",
38   "browser.download.skipConfirmLaunchExecutable",
39   "browser.download.useDownloadDir",
40   "browser.fixup.",
41   "browser.history_expire_",
42   "browser.link.open_newwindow",
43   "browser.places.",
44   "browser.privatebrowsing.",
45   "browser.search.context.loadInBackground",
46   "browser.search.log",
47   "browser.search.openintab",
48   "browser.search.param",
49   "browser.search.searchEnginesURL",
50   "browser.search.suggest.enabled",
51   "browser.search.update",
52   "browser.search.useDBForOrder",
53   "browser.sessionstore.",
54   "browser.startup.homepage",
55   "browser.startup.page",
56   "browser.tabs.",
57   "browser.urlbar.",
58   "browser.zoom.",
59   "dom.",
60   "extensions.checkCompatibility",
61   "extensions.formautofill.",
62   "extensions.lastAppVersion",
63   "fission.autostart",
64   "font.",
65   "general.autoScroll",
66   "general.useragent.",
67   "gfx.",
68   "html5.",
69   "identity.fxaccounts.enabled",
70   "idle.",
71   "image.",
72   "javascript.",
73   "keyword.",
74   "layers.",
75   "layout.css.dpi",
76   "layout.display-list.",
77   "media.",
78   "mousewheel.",
79   "network.",
80   "permissions.default.image",
81   "places.",
82   "plugin.",
83   "plugins.",
84   "print.",
85   "privacy.",
86   "remote.enabled",
87   "security.",
88   "services.sync.declinedEngines",
89   "services.sync.lastPing",
90   "services.sync.lastSync",
91   "services.sync.numClients",
92   "services.sync.engine.",
93   "signon.",
94   "storage.vacuum.last.",
95   "svg.",
96   "toolkit.startup.recent_crashes",
97   "ui.osk.enabled",
98   "ui.osk.detect_physical_keyboard",
99   "ui.osk.require_tablet_mode",
100   "ui.osk.debug.keyboardDisplayReason",
101   "webgl.",
104 // The blacklist, unlike the whitelist, is a list of regular expressions.
105 const PREFS_BLACKLIST = [
106   /^browser[.]fixup[.]domainwhitelist[.]/,
107   /^media[.]webrtc[.]debug[.]aec_log_dir/,
108   /^media[.]webrtc[.]debug[.]log_file/,
109   /^network[.]proxy[.]/,
110   /[.]print_to_filename$/,
111   /^print[.]macosx[.]pagesetup/,
112   /^print[.]printer/,
115 // Table of getters for various preference types.
116 const PREFS_GETTERS = {};
118 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_STRING] = (prefs, name) =>
119   prefs.getStringPref(name);
120 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_INT] = (prefs, name) =>
121   prefs.getIntPref(name);
122 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_BOOL] = (prefs, name) =>
123   prefs.getBoolPref(name);
125 // List of unimportant locked prefs (won't be shown on the troubleshooting
126 // session)
127 const PREFS_UNIMPORTANT_LOCKED = [
128   "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
129   "privacy.restrict3rdpartystorage.url_decorations",
132 // Return the preferences filtered by PREFS_BLACKLIST and PREFS_WHITELIST lists
133 // and also by the custom 'filter'-ing function.
134 function getPrefList(filter) {
135   filter = filter || (name => true);
136   function getPref(name) {
137     let type = Services.prefs.getPrefType(name);
138     if (!(type in PREFS_GETTERS)) {
139       throw new Error("Unknown preference type " + type + " for " + name);
140     }
141     return PREFS_GETTERS[type](Services.prefs, name);
142   }
144   return PREFS_WHITELIST.reduce(function(prefs, branch) {
145     Services.prefs.getChildList(branch).forEach(function(name) {
146       if (filter(name) && !PREFS_BLACKLIST.some(re => re.test(name))) {
147         prefs[name] = getPref(name);
148       }
149     });
150     return prefs;
151   }, {});
154 var Troubleshoot = {
155   /**
156    * Captures a snapshot of data that may help troubleshooters troubleshoot
157    * trouble.
158    *
159    * @param done A function that will be asynchronously called when the
160    *             snapshot completes.  It will be passed the snapshot object.
161    */
162   snapshot: function snapshot(done) {
163     let snapshot = {};
164     let numPending = Object.keys(dataProviders).length;
165     function providerDone(providerName, providerData) {
166       snapshot[providerName] = providerData;
167       if (--numPending == 0) {
168         // Ensure that done is always and truly called asynchronously.
169         Services.tm.dispatchToMainThread(done.bind(null, snapshot));
170       }
171     }
172     for (let name in dataProviders) {
173       try {
174         dataProviders[name](providerDone.bind(null, name));
175       } catch (err) {
176         let msg = "Troubleshoot data provider failed: " + name + "\n" + err;
177         Cu.reportError(msg);
178         providerDone(name, msg);
179       }
180     }
181   },
183   kMaxCrashAge: 3 * 24 * 60 * 60 * 1000, // 3 days
186 // Each data provider is a name => function mapping.  When a snapshot is
187 // captured, each provider's function is called, and it's the function's job to
188 // generate the provider's data.  The function is passed a "done" callback, and
189 // when done, it must pass its data to the callback.  The resulting snapshot
190 // object will contain a name => data entry for each provider.
191 var dataProviders = {
192   application: function application(done) {
193     let data = {
194       name: Services.appinfo.name,
195       osVersion:
196         Services.sysinfo.getProperty("name") +
197         " " +
198         Services.sysinfo.getProperty("version"),
199       version: AppConstants.MOZ_APP_VERSION_DISPLAY,
200       buildID: Services.appinfo.appBuildID,
201       userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].getService(
202         Ci.nsIHttpProtocolHandler
203       ).userAgent,
204       safeMode: Services.appinfo.inSafeMode,
205     };
207     if (AppConstants.MOZ_UPDATER) {
208       data.updateChannel = ChromeUtils.import(
209         "resource://gre/modules/UpdateUtils.jsm",
210         {}
211       ).UpdateUtils.UpdateChannel;
212     }
214     // eslint-disable-next-line mozilla/use-default-preference-values
215     try {
216       data.vendor = Services.prefs.getCharPref("app.support.vendor");
217     } catch (e) {}
218     try {
219       data.supportURL = Services.urlFormatter.formatURLPref(
220         "app.support.baseURL"
221       );
222     } catch (e) {}
224     data.numTotalWindows = 0;
225     data.numRemoteWindows = 0;
226     for (let { docShell } of Services.wm.getEnumerator("navigator:browser")) {
227       data.numTotalWindows++;
228       let remote = docShell.QueryInterface(Ci.nsILoadContext).useRemoteTabs;
229       if (remote) {
230         data.numRemoteWindows++;
231       }
232     }
234     try {
235       data.launcherProcessState = Services.appinfo.launcherProcessState;
236     } catch (e) {}
238     data.remoteAutoStart = Services.appinfo.browserTabsRemoteAutostart;
240     try {
241       let e10sStatus = Cc["@mozilla.org/supports-PRUint64;1"].createInstance(
242         Ci.nsISupportsPRUint64
243       );
244       let appinfo = Services.appinfo.QueryInterface(Ci.nsIObserver);
245       appinfo.observe(e10sStatus, "getE10SBlocked", "");
246       data.autoStartStatus = e10sStatus.data;
247     } catch (e) {
248       data.autoStartStatus = -1;
249     }
251     if (Services.policies) {
252       data.policiesStatus = Services.policies.status;
253     }
255     const keyLocationServiceGoogle = Services.urlFormatter
256       .formatURL("%GOOGLE_LOCATION_SERVICE_API_KEY%")
257       .trim();
258     data.keyLocationServiceGoogleFound =
259       keyLocationServiceGoogle != "no-google-location-service-api-key" &&
260       !!keyLocationServiceGoogle.length;
262     const keySafebrowsingGoogle = Services.urlFormatter
263       .formatURL("%GOOGLE_SAFEBROWSING_API_KEY%")
264       .trim();
265     data.keySafebrowsingGoogleFound =
266       keySafebrowsingGoogle != "no-google-safebrowsing-api-key" &&
267       !!keySafebrowsingGoogle.length;
269     const keyMozilla = Services.urlFormatter
270       .formatURL("%MOZILLA_API_KEY%")
271       .trim();
272     data.keyMozillaFound =
273       keyMozilla != "no-mozilla-api-key" && !!keyMozilla.length;
275     done(data);
276   },
278   extensions: async function extensions(done) {
279     let extensions = await AddonManager.getAddonsByTypes(["extension"]);
280     extensions = extensions.filter(e => !e.isSystem);
281     extensions.sort(function(a, b) {
282       if (a.isActive != b.isActive) {
283         return b.isActive ? 1 : -1;
284       }
286       // In some unfortunate cases addon names can be null.
287       let aname = a.name || "";
288       let bname = b.name || "";
289       let lc = aname.localeCompare(bname);
290       if (lc != 0) {
291         return lc;
292       }
293       if (a.version != b.version) {
294         return a.version > b.version ? 1 : -1;
295       }
296       return 0;
297     });
298     let props = ["name", "version", "isActive", "id"];
299     done(
300       extensions.map(function(ext) {
301         return props.reduce(function(extData, prop) {
302           extData[prop] = ext[prop];
303           return extData;
304         }, {});
305       })
306     );
307   },
309   securitySoftware: function securitySoftware(done) {
310     let data = {};
312     let sysInfo = Cc["@mozilla.org/system-info;1"].getService(
313       Ci.nsIPropertyBag2
314     );
316     const keys = [
317       "registeredAntiVirus",
318       "registeredAntiSpyware",
319       "registeredFirewall",
320     ];
321     for (let key of keys) {
322       let prop = "";
323       try {
324         prop = sysInfo.getProperty(key);
325       } catch (e) {}
327       data[key] = prop;
328     }
330     done(data);
331   },
333   features: async function features(done) {
334     let features = await AddonManager.getAddonsByTypes(["extension"]);
335     features = features.filter(f => f.isSystem);
336     features.sort(function(a, b) {
337       // In some unfortunate cases addon names can be null.
338       let aname = a.name || null;
339       let bname = b.name || null;
340       let lc = aname.localeCompare(bname);
341       if (lc != 0) {
342         return lc;
343       }
344       if (a.version != b.version) {
345         return a.version > b.version ? 1 : -1;
346       }
347       return 0;
348     });
349     let props = ["name", "version", "id"];
350     done(
351       features.map(function(f) {
352         return props.reduce(function(fData, prop) {
353           fData[prop] = f[prop];
354           return fData;
355         }, {});
356       })
357     );
358   },
360   processes: function processes(done) {
361     let remoteTypes = {};
363     for (let i = 0; i < Services.ppmm.childCount; i++) {
364       let remoteType;
365       try {
366         remoteType = Services.ppmm.getChildAt(i).remoteType;
367       } catch (e) {}
369       // The parent process is also managed by the ppmm (because
370       // of non-remote tabs), but it doesn't have a remoteType.
371       if (!remoteType) {
372         continue;
373       }
375       remoteType = E10SUtils.remoteTypePrefix(remoteType);
377       if (remoteTypes[remoteType]) {
378         remoteTypes[remoteType]++;
379       } else {
380         remoteTypes[remoteType] = 1;
381       }
382     }
384     try {
385       let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
386       if (winUtils.gpuProcessPid != -1) {
387         remoteTypes.gpu = 1;
388       }
389     } catch (e) {}
391     let data = {
392       remoteTypes,
393       maxWebContentProcesses: Services.appinfo.maxWebProcessCount,
394     };
396     done(data);
397   },
399   modifiedPreferences: function modifiedPreferences(done) {
400     done(getPrefList(name => Services.prefs.prefHasUserValue(name)));
401   },
403   lockedPreferences: function lockedPreferences(done) {
404     done(
405       getPrefList(
406         name =>
407           !PREFS_UNIMPORTANT_LOCKED.includes(name) &&
408           Services.prefs.prefIsLocked(name)
409       )
410     );
411   },
413   graphics: function graphics(done) {
414     function statusMsgForFeature(feature) {
415       // We return an object because in the try-newer-driver case we need to
416       // include the suggested version, which the consumer likely needs to plug
417       // into a format string from a localization file. Rather than returning
418       // a string in some cases and an object in others, return an object always.
419       let msg = { key: "" };
420       try {
421         var status = gfxInfo.getFeatureStatus(feature);
422       } catch (e) {}
423       switch (status) {
424         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE:
425         case Ci.nsIGfxInfo.FEATURE_DISCOURAGED:
426           msg = { key: "blocked-gfx-card" };
427           break;
428         case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION:
429           msg = { key: "blocked-os-version" };
430           break;
431         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION:
432           try {
433             var driverVersion = gfxInfo.getFeatureSuggestedDriverVersion(
434               feature
435             );
436           } catch (e) {}
437           msg = driverVersion
438             ? { key: "try-newer-driver", args: { driverVersion } }
439             : { key: "blocked-driver" };
440           break;
441         case Ci.nsIGfxInfo.FEATURE_BLOCKED_MISMATCHED_VERSION:
442           msg = { key: "blocked-mismatched-version" };
443           break;
444       }
445       return msg;
446     }
448     let data = {};
450     try {
451       // nsIGfxInfo may not be implemented on some platforms.
452       var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
453     } catch (e) {}
455     let promises = [];
456     // done will be called upon all pending promises being resolved.
457     // add your pending promise to promises when adding new ones.
458     function completed() {
459       Promise.all(promises).then(() => done(data));
460     }
462     data.numTotalWindows = 0;
463     data.numAcceleratedWindows = 0;
464     for (let win of Services.ww.getWindowEnumerator()) {
465       let winUtils = win.windowUtils;
466       try {
467         // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report
468         if (
469           winUtils.layerManagerType == "None" ||
470           !winUtils.layerManagerRemote
471         ) {
472           continue;
473         }
474         data.numTotalWindows++;
475         data.windowLayerManagerType = winUtils.layerManagerType;
476         data.windowLayerManagerRemote = winUtils.layerManagerRemote;
477         data.windowUsingAdvancedLayers = winUtils.usingAdvancedLayers;
478       } catch (e) {
479         continue;
480       }
481       if (data.windowLayerManagerType != "Basic") {
482         data.numAcceleratedWindows++;
483       }
484     }
486     // If we had no OMTC windows, report back Basic Layers.
487     if (!data.windowLayerManagerType) {
488       data.windowLayerManagerType = "Basic";
489       data.windowLayerManagerRemote = false;
490     }
492     if (!data.numAcceleratedWindows && gfxInfo) {
493       let win = AppConstants.platform == "win";
494       let feature = win
495         ? gfxInfo.FEATURE_DIRECT3D_9_LAYERS
496         : gfxInfo.FEATURE_OPENGL_LAYERS;
497       data.numAcceleratedWindowsMessage = statusMsgForFeature(feature);
498     }
500     if (!gfxInfo) {
501       completed();
502       return;
503     }
505     // keys are the names of attributes on nsIGfxInfo, values become the names
506     // of the corresponding properties in our data object.  A null value means
507     // no change.  This is needed so that the names of properties in the data
508     // object are the same as the names of keys in aboutSupport.properties.
509     let gfxInfoProps = {
510       adapterDescription: null,
511       adapterVendorID: null,
512       adapterDeviceID: null,
513       adapterSubsysID: null,
514       adapterRAM: null,
515       adapterDriver: "adapterDrivers",
516       adapterDriverVendor: "driverVendor",
517       adapterDriverVersion: "driverVersion",
518       adapterDriverDate: "driverDate",
520       adapterDescription2: null,
521       adapterVendorID2: null,
522       adapterDeviceID2: null,
523       adapterSubsysID2: null,
524       adapterRAM2: null,
525       adapterDriver2: "adapterDrivers2",
526       adapterDriverVendor2: "driverVendor2",
527       adapterDriverVersion2: "driverVersion2",
528       adapterDriverDate2: "driverDate2",
529       isGPU2Active: null,
531       D2DEnabled: "direct2DEnabled",
532       DWriteEnabled: "directWriteEnabled",
533       DWriteVersion: "directWriteVersion",
534       cleartypeParameters: "clearTypeParameters",
535       UsesTiling: "usesTiling",
536       ContentUsesTiling: "contentUsesTiling",
537       OffMainThreadPaintEnabled: "offMainThreadPaintEnabled",
538       OffMainThreadPaintWorkerCount: "offMainThreadPaintWorkerCount",
539       TargetFrameRate: "targetFrameRate",
540       windowProtocol: null,
541       desktopEnvironment: null,
542     };
544     for (let prop in gfxInfoProps) {
545       try {
546         data[gfxInfoProps[prop] || prop] = gfxInfo[prop];
547       } catch (e) {}
548     }
550     if ("direct2DEnabled" in data && !data.direct2DEnabled) {
551       data.direct2DEnabledMessage = statusMsgForFeature(
552         Ci.nsIGfxInfo.FEATURE_DIRECT2D
553       );
554     }
556     let doc = new DOMParser().parseFromString("<html/>", "text/html");
558     function GetWebGLInfo(data, keyPrefix, contextType) {
559       data[keyPrefix + "Renderer"] = "-";
560       data[keyPrefix + "Version"] = "-";
561       data[keyPrefix + "DriverExtensions"] = "-";
562       data[keyPrefix + "Extensions"] = "-";
563       data[keyPrefix + "WSIInfo"] = "-";
565       // //
567       let canvas = doc.createElement("canvas");
568       canvas.width = 1;
569       canvas.height = 1;
571       // //
573       let creationError = null;
575       canvas.addEventListener(
576         "webglcontextcreationerror",
578         function(e) {
579           creationError = e.statusMessage;
580         }
581       );
583       let gl = null;
584       try {
585         gl = canvas.getContext(contextType);
586       } catch (e) {
587         if (!creationError) {
588           creationError = e.toString();
589         }
590       }
591       if (!gl) {
592         data[keyPrefix + "Renderer"] =
593           creationError || "(no creation error info)";
594         return;
595       }
597       // //
599       data[keyPrefix + "Extensions"] = gl.getSupportedExtensions().join(" ");
601       // //
603       let ext = gl.getExtension("MOZ_debug");
604       // This extension is unconditionally available to chrome. No need to check.
605       let vendor = ext.getParameter(gl.VENDOR);
606       let renderer = ext.getParameter(gl.RENDERER);
608       data[keyPrefix + "Renderer"] = vendor + " -- " + renderer;
609       data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION);
610       data[keyPrefix + "DriverExtensions"] = ext.getParameter(ext.EXTENSIONS);
611       data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO);
613       // //
615       // Eagerly free resources.
616       let loseExt = gl.getExtension("WEBGL_lose_context");
617       if (loseExt) {
618         loseExt.loseContext();
619       }
620     }
622     GetWebGLInfo(data, "webgl1", "webgl");
623     GetWebGLInfo(data, "webgl2", "webgl2");
625     let infoInfo = gfxInfo.getInfo();
626     if (infoInfo) {
627       data.info = infoInfo;
628     }
630     let failureIndices = {};
632     let failures = gfxInfo.getFailures(failureIndices);
633     if (failures.length) {
634       data.failures = failures;
635       if (failureIndices.value.length == failures.length) {
636         data.indices = failureIndices.value;
637       }
638     }
640     data.featureLog = gfxInfo.getFeatureLog();
641     data.crashGuards = gfxInfo.getActiveCrashGuards();
643     completed();
644   },
646   media: function media(done) {
647     function convertDevices(devices) {
648       if (!devices) {
649         return undefined;
650       }
651       let infos = [];
652       for (let i = 0; i < devices.length; ++i) {
653         let device = devices.queryElementAt(i, Ci.nsIAudioDeviceInfo);
654         infos.push({
655           name: device.name,
656           groupId: device.groupId,
657           vendor: device.vendor,
658           type: device.type,
659           state: device.state,
660           preferred: device.preferred,
661           supportedFormat: device.supportedFormat,
662           defaultFormat: device.defaultFormat,
663           maxChannels: device.maxChannels,
664           defaultRate: device.defaultRate,
665           maxRate: device.maxRate,
666           minRate: device.minRate,
667           maxLatency: device.maxLatency,
668           minLatency: device.minLatency,
669         });
670       }
671       return infos;
672     }
674     let data = {};
675     let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
676     data.currentAudioBackend = winUtils.currentAudioBackend;
677     data.currentMaxAudioChannels = winUtils.currentMaxAudioChannels;
678     data.currentPreferredSampleRate = winUtils.currentPreferredSampleRate;
679     data.audioOutputDevices = convertDevices(
680       winUtils
681         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_OUTPUT)
682         .QueryInterface(Ci.nsIArray)
683     );
684     data.audioInputDevices = convertDevices(
685       winUtils
686         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_INPUT)
687         .QueryInterface(Ci.nsIArray)
688     );
689     done(data);
690   },
692   javaScript: function javaScript(done) {
693     let data = {};
694     let winEnumer = Services.ww.getWindowEnumerator();
695     if (winEnumer.hasMoreElements()) {
696       data.incrementalGCEnabled = winEnumer
697         .getNext()
698         .windowUtils.isIncrementalGCEnabled();
699     }
700     done(data);
701   },
703   accessibility: function accessibility(done) {
704     let data = {};
705     data.isActive = Services.appinfo.accessibilityEnabled;
706     // eslint-disable-next-line mozilla/use-default-preference-values
707     try {
708       data.forceDisabled = Services.prefs.getIntPref(
709         "accessibility.force_disabled"
710       );
711     } catch (e) {}
712     data.handlerUsed = Services.appinfo.accessibleHandlerUsed;
713     data.instantiator = Services.appinfo.accessibilityInstantiator;
714     done(data);
715   },
717   libraryVersions: function libraryVersions(done) {
718     let data = {};
719     let verInfo = Cc["@mozilla.org/security/nssversion;1"].getService(
720       Ci.nsINSSVersion
721     );
722     for (let prop in verInfo) {
723       let match = /^([^_]+)_((Min)?Version)$/.exec(prop);
724       if (match) {
725         let verProp = match[2][0].toLowerCase() + match[2].substr(1);
726         data[match[1]] = data[match[1]] || {};
727         data[match[1]][verProp] = verInfo[prop];
728       }
729     }
730     done(data);
731   },
733   userJS: function userJS(done) {
734     let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile);
735     userJSFile.append("user.js");
736     done({
737       exists: userJSFile.exists() && userJSFile.fileSize > 0,
738     });
739   },
741   intl: function intl(done) {
742     const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
743       Ci.mozIOSPreferences
744     );
745     done({
746       localeService: {
747         requested: Services.locale.requestedLocales,
748         available: Services.locale.availableLocales,
749         supported: Services.locale.appLocalesAsBCP47,
750         regionalPrefs: Services.locale.regionalPrefsLocales,
751         defaultLocale: Services.locale.defaultLocale,
752       },
753       osPrefs: {
754         systemLocales: osPrefs.systemLocales,
755         regionalPrefsLocales: osPrefs.regionalPrefsLocales,
756       },
757     });
758   },
761 if (AppConstants.MOZ_CRASHREPORTER) {
762   dataProviders.crashes = function crashes(done) {
763     const { CrashReports } = ChromeUtils.import(
764       "resource://gre/modules/CrashReports.jsm"
765     );
766     let reports = CrashReports.getReports();
767     let now = new Date();
768     let reportsNew = reports.filter(
769       report => now - report.date < Troubleshoot.kMaxCrashAge
770     );
771     let reportsSubmitted = reportsNew.filter(report => !report.pending);
772     let reportsPendingCount = reportsNew.length - reportsSubmitted.length;
773     let data = { submitted: reportsSubmitted, pending: reportsPendingCount };
774     done(data);
775   };
778 if (AppConstants.MOZ_SANDBOX) {
779   dataProviders.sandbox = function sandbox(done) {
780     let data = {};
781     if (AppConstants.unixstyle == "linux") {
782       const keys = [
783         "hasSeccompBPF",
784         "hasSeccompTSync",
785         "hasPrivilegedUserNamespaces",
786         "hasUserNamespaces",
787         "canSandboxContent",
788         "canSandboxMedia",
789       ];
791       for (let key of keys) {
792         if (Services.sysinfo.hasKey(key)) {
793           data[key] = Services.sysinfo.getPropertyAsBool(key);
794         }
795       }
797       let reporter = Cc["@mozilla.org/sandbox/syscall-reporter;1"].getService(
798         Ci.mozISandboxReporter
799       );
800       const snapshot = reporter.snapshot();
801       let syscalls = [];
802       for (let index = snapshot.begin; index < snapshot.end; ++index) {
803         let report = snapshot.getElement(index);
804         let { msecAgo, pid, tid, procType, syscall } = report;
805         let args = [];
806         for (let i = 0; i < report.numArgs; ++i) {
807           args.push(report.getArg(i));
808         }
809         syscalls.push({ index, msecAgo, pid, tid, procType, syscall, args });
810       }
811       data.syscallLog = syscalls;
812     }
814     if (AppConstants.MOZ_SANDBOX) {
815       let sandboxSettings = Cc[
816         "@mozilla.org/sandbox/sandbox-settings;1"
817       ].getService(Ci.mozISandboxSettings);
818       data.contentSandboxLevel = Services.prefs.getIntPref(
819         "security.sandbox.content.level"
820       );
821       data.effectiveContentSandboxLevel =
822         sandboxSettings.effectiveContentSandboxLevel;
823     }
825     done(data);
826   };
829 if (AppConstants.ENABLE_REMOTE_AGENT) {
830   dataProviders.remoteAgent = function remoteAgent(done) {
831     const { RemoteAgent } = ChromeUtils.import(
832       "chrome://remote/content/RemoteAgent.jsm"
833     );
834     const { listening, scheme, host, port } = RemoteAgent;
835     let url = "";
836     if (listening) {
837       url = `${scheme}://${host}:${port}/`;
838     }
839     done({ listening, url });
840   };