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