Bug 1838629 - Part 7: Inline nursery cell allocation method in Allocator.h r=sfink
[gecko.git] / toolkit / modules / Troubleshoot.sys.mjs
bloba3dc13b71abeb9601737380e83d4e5e6f3bfbb92
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 import { AddonManager } from "resource://gre/modules/AddonManager.sys.mjs";
6 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
8 import { FeatureGate } from "resource://featuregates/FeatureGate.sys.mjs";
10 const lazy = {};
12 ChromeUtils.defineESModuleGetters(lazy, {
13   PlacesDBUtils: "resource://gre/modules/PlacesDBUtils.sys.mjs",
14 });
16 // We use a list of prefs for display to make sure we only show prefs that
17 // are useful for support and won't compromise the user's privacy.  Note that
18 // entries are *prefixes*: for example, "accessibility." applies to all prefs
19 // under the "accessibility.*" branch.
20 const PREFS_FOR_DISPLAY = [
21   "accessibility.",
22   "apz.",
23   "browser.cache.",
24   "browser.contentblocking.category",
25   "browser.display.",
26   "browser.download.always_ask_before_handling_new_types",
27   "browser.download.enable_spam_prevention",
28   "browser.download.folderList",
29   "browser.download.improvements_to_download_panel",
30   "browser.download.lastDir.savePerSite",
31   "browser.download.manager.addToRecentDocs",
32   "browser.download.manager.resumeOnWakeDelay",
33   "browser.download.open_pdf_attachments_inline",
34   "browser.download.preferred.",
35   "browser.download.skipConfirmLaunchExecutable",
36   "browser.download.start_downloads_in_tmp_dir",
37   "browser.download.useDownloadDir",
38   "browser.fixup.",
39   "browser.history_expire_",
40   "browser.link.open_newwindow",
41   "browser.places.",
42   "browser.privatebrowsing.",
43   "browser.search.context.loadInBackground",
44   "browser.search.log",
45   "browser.search.openintab",
46   "browser.search.param",
47   "browser.search.region",
48   "browser.search.searchEnginesURL",
49   "browser.search.suggest.enabled",
50   "browser.search.update",
51   "browser.sessionstore.",
52   "browser.startup.homepage",
53   "browser.startup.page",
54   "browser.tabs.",
55   "browser.urlbar.",
56   "browser.zoom.",
57   "doh-rollout.",
58   "dom.",
59   "extensions.checkCompatibility",
60   "extensions.eventPages.enabled",
61   "extensions.formautofill.",
62   "extensions.lastAppVersion",
63   "extensions.manifestV3.enabled",
64   "extensions.quarantinedDomains.enabled",
65   "extensions.InstallTrigger.enabled",
66   "extensions.InstallTriggerImpl.enabled",
67   "fission.autostart",
68   "font.",
69   "general.autoScroll",
70   "general.useragent.",
71   "gfx.",
72   "html5.",
73   "identity.fxaccounts.enabled",
74   "idle.",
75   "image.",
76   "javascript.",
77   "keyword.",
78   "layers.",
79   "layout.css.dpi",
80   "layout.display-list.",
81   "layout.frame_rate",
82   "media.",
83   "mousewheel.",
84   "network.",
85   "permissions.default.image",
86   "places.",
87   "plugin.",
88   "plugins.",
89   "privacy.",
90   "security.",
91   "services.sync.declinedEngines",
92   "services.sync.lastPing",
93   "services.sync.lastSync",
94   "services.sync.numClients",
95   "services.sync.engine.",
96   "signon.",
97   "storage.vacuum.last.",
98   "svg.",
99   "toolkit.startup.recent_crashes",
100   "ui.osk.enabled",
101   "ui.osk.detect_physical_keyboard",
102   "ui.osk.require_tablet_mode",
103   "ui.osk.debug.keyboardDisplayReason",
104   "webgl.",
105   "widget.dmabuf",
106   "widget.use-xdg-desktop-portal",
107   "widget.use-xdg-desktop-portal.file-picker",
108   "widget.use-xdg-desktop-portal.mime-handler",
109   "widget.gtk.overlay-scrollbars.enabled",
110   "widget.wayland",
113 // The list of prefs we don't display, unlike the list of prefs for display,
114 // is a list of regular expressions.
115 const PREF_REGEXES_NOT_TO_DISPLAY = [
116   /^browser[.]fixup[.]domainwhitelist[.]/,
117   /^dom[.]push[.]userAgentID/,
118   /^media[.]webrtc[.]debug[.]aec_log_dir/,
119   /^media[.]webrtc[.]debug[.]log_file/,
120   /^print[.].*print_to_filename$/,
121   /^network[.]proxy[.]/,
124 // Table of getters for various preference types.
125 const PREFS_GETTERS = {};
127 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_STRING] = (prefs, name) =>
128   prefs.getStringPref(name);
129 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_INT] = (prefs, name) =>
130   prefs.getIntPref(name);
131 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_BOOL] = (prefs, name) =>
132   prefs.getBoolPref(name);
134 // List of unimportant locked prefs (won't be shown on the troubleshooting
135 // session)
136 const PREFS_UNIMPORTANT_LOCKED = [
137   "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled",
138   "privacy.restrict3rdpartystorage.url_decorations",
141 function getPref(name) {
142   let type = Services.prefs.getPrefType(name);
143   if (!(type in PREFS_GETTERS)) {
144     throw new Error("Unknown preference type " + type + " for " + name);
145   }
146   return PREFS_GETTERS[type](Services.prefs, name);
149 // Return the preferences filtered by PREF_REGEXES_NOT_TO_DISPLAY and PREFS_FOR_DISPLAY
150 // and also by the custom 'filter'-ing function.
151 function getPrefList(filter, allowlist = PREFS_FOR_DISPLAY) {
152   return allowlist.reduce(function (prefs, branch) {
153     Services.prefs.getChildList(branch).forEach(function (name) {
154       if (
155         filter(name) &&
156         !PREF_REGEXES_NOT_TO_DISPLAY.some(re => re.test(name))
157       ) {
158         prefs[name] = getPref(name);
159       }
160     });
161     return prefs;
162   }, {});
165 export var Troubleshoot = {
166   /**
167    * Captures a snapshot of data that may help troubleshooters troubleshoot
168    * trouble.
169    *
170    * @returns {Promise}
171    *   A promise that is resolved with the snapshot data.
172    */
173   snapshot() {
174     return new Promise(resolve => {
175       let snapshot = {};
176       let numPending = Object.keys(dataProviders).length;
177       function providerDone(providerName, providerData) {
178         snapshot[providerName] = providerData;
179         if (--numPending == 0) {
180           // Ensure that done is always and truly called asynchronously.
181           Services.tm.dispatchToMainThread(() => resolve(snapshot));
182         }
183       }
184       for (let name in dataProviders) {
185         try {
186           dataProviders[name](providerDone.bind(null, name));
187         } catch (err) {
188           let msg = "Troubleshoot data provider failed: " + name + "\n" + err;
189           console.error(msg);
190           providerDone(name, msg);
191         }
192       }
193     });
194   },
196   kMaxCrashAge: 3 * 24 * 60 * 60 * 1000, // 3 days
199 // Each data provider is a name => function mapping.  When a snapshot is
200 // captured, each provider's function is called, and it's the function's job to
201 // generate the provider's data.  The function is passed a "done" callback, and
202 // when done, it must pass its data to the callback.  The resulting snapshot
203 // object will contain a name => data entry for each provider.
204 var dataProviders = {
205   application: async function application(done) {
206     let data = {
207       name: Services.appinfo.name,
208       osVersion:
209         Services.sysinfo.getProperty("name") +
210         " " +
211         Services.sysinfo.getProperty("version") +
212         " " +
213         Services.sysinfo.getProperty("build"),
214       version: AppConstants.MOZ_APP_VERSION_DISPLAY,
215       buildID: Services.appinfo.appBuildID,
216       distributionID: Services.prefs
217         .getDefaultBranch("")
218         .getCharPref("distribution.id", ""),
219       userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].getService(
220         Ci.nsIHttpProtocolHandler
221       ).userAgent,
222       safeMode: Services.appinfo.inSafeMode,
223       memorySizeBytes: Services.sysinfo.getProperty("memsize"),
224       diskAvailableBytes: Services.dirsvc.get("ProfD", Ci.nsIFile)
225         .diskSpaceAvailable,
226     };
228     if (Services.sysinfo.getProperty("name") == "Windows_NT") {
229       if ((await Services.sysinfo.processInfo).isWindowsSMode) {
230         data.osVersion += " S";
231       }
232     }
234     if (AppConstants.MOZ_UPDATER) {
235       data.updateChannel = ChromeUtils.importESModule(
236         "resource://gre/modules/UpdateUtils.sys.mjs"
237       ).UpdateUtils.UpdateChannel;
238     }
240     // eslint-disable-next-line mozilla/use-default-preference-values
241     try {
242       data.vendor = Services.prefs.getCharPref("app.support.vendor");
243     } catch (e) {}
244     try {
245       data.supportURL = Services.urlFormatter.formatURLPref(
246         "app.support.baseURL"
247       );
248     } catch (e) {}
250     data.osTheme = Services.sysinfo.getProperty("osThemeInfo");
252     try {
253       // MacOSX: Check for rosetta status, if it exists
254       data.rosetta = Services.sysinfo.getProperty("rosettaStatus");
255     } catch (e) {}
257     data.numTotalWindows = 0;
258     data.numFissionWindows = 0;
259     data.numRemoteWindows = 0;
260     for (let { docShell } of Services.wm.getEnumerator("navigator:browser")) {
261       docShell.QueryInterface(Ci.nsILoadContext);
262       data.numTotalWindows++;
263       if (docShell.useRemoteSubframes) {
264         data.numFissionWindows++;
265       }
266       if (docShell.useRemoteTabs) {
267         data.numRemoteWindows++;
268       }
269     }
271     try {
272       data.launcherProcessState = Services.appinfo.launcherProcessState;
273     } catch (e) {}
275     data.fissionAutoStart = Services.appinfo.fissionAutostart;
276     data.fissionDecisionStatus = Services.appinfo.fissionDecisionStatusString;
278     data.remoteAutoStart = Services.appinfo.browserTabsRemoteAutostart;
280     if (Services.policies) {
281       data.policiesStatus = Services.policies.status;
282     }
284     const keyLocationServiceGoogle = Services.urlFormatter
285       .formatURL("%GOOGLE_LOCATION_SERVICE_API_KEY%")
286       .trim();
287     data.keyLocationServiceGoogleFound =
288       keyLocationServiceGoogle != "no-google-location-service-api-key" &&
289       !!keyLocationServiceGoogle.length;
291     const keySafebrowsingGoogle = Services.urlFormatter
292       .formatURL("%GOOGLE_SAFEBROWSING_API_KEY%")
293       .trim();
294     data.keySafebrowsingGoogleFound =
295       keySafebrowsingGoogle != "no-google-safebrowsing-api-key" &&
296       !!keySafebrowsingGoogle.length;
298     const keyMozilla = Services.urlFormatter
299       .formatURL("%MOZILLA_API_KEY%")
300       .trim();
301     data.keyMozillaFound =
302       keyMozilla != "no-mozilla-api-key" && !!keyMozilla.length;
304     done(data);
305   },
307   addons: async function addons(done) {
308     let addons = await AddonManager.getAddonsByTypes([
309       "extension",
310       "locale",
311       "dictionary",
312       "sitepermission",
313       "theme",
314     ]);
315     addons = addons.filter(e => !e.isSystem);
316     addons.sort(function (a, b) {
317       if (a.isActive != b.isActive) {
318         return b.isActive ? 1 : -1;
319       }
321       if (a.type != b.type) {
322         return a.type.localeCompare(b.type);
323       }
325       // In some unfortunate cases add-on names can be null.
326       let aname = a.name || "";
327       let bname = b.name || "";
328       let lc = aname.localeCompare(bname);
329       if (lc != 0) {
330         return lc;
331       }
332       if (a.version != b.version) {
333         return a.version > b.version ? 1 : -1;
334       }
335       return 0;
336     });
337     let props = ["name", "type", "version", "isActive", "id"];
338     done(
339       addons.map(function (ext) {
340         return props.reduce(function (extData, prop) {
341           extData[prop] = ext[prop];
342           return extData;
343         }, {});
344       })
345     );
346   },
348   securitySoftware: function securitySoftware(done) {
349     let data = {};
351     const keys = [
352       "registeredAntiVirus",
353       "registeredAntiSpyware",
354       "registeredFirewall",
355     ];
356     for (let key of keys) {
357       let prop = "";
358       try {
359         prop = Services.sysinfo.getProperty(key);
360       } catch (e) {}
362       data[key] = prop;
363     }
365     done(data);
366   },
368   features: async function features(done) {
369     let features = await AddonManager.getAddonsByTypes(["extension"]);
370     features = features.filter(f => f.isSystem);
371     features.sort(function (a, b) {
372       // In some unfortunate cases addon names can be null.
373       let aname = a.name || null;
374       let bname = b.name || null;
375       let lc = aname.localeCompare(bname);
376       if (lc != 0) {
377         return lc;
378       }
379       if (a.version != b.version) {
380         return a.version > b.version ? 1 : -1;
381       }
382       return 0;
383     });
384     let props = ["name", "version", "id"];
385     done(
386       features.map(function (f) {
387         return props.reduce(function (fData, prop) {
388           fData[prop] = f[prop];
389           return fData;
390         }, {});
391       })
392     );
393   },
395   processes: async function processes(done) {
396     let remoteTypes = {};
397     const processInfo = await ChromeUtils.requestProcInfo();
398     for (let i = 0; i < processInfo.children.length; i++) {
399       let remoteType;
400       try {
401         remoteType = processInfo.children[i].type;
402         // Workaround for bug 1790070, since requestProcInfo refers to the preallocated content
403         // process as "preallocated", and the localization string mapping expects "prealloc".
404         remoteType = remoteType === "preallocated" ? "prealloc" : remoteType;
405       } catch (e) {}
407       // The parent process is also managed by the ppmm (because
408       // of non-remote tabs), but it doesn't have a remoteType.
409       if (!remoteType) {
410         continue;
411       }
413       if (remoteTypes[remoteType]) {
414         remoteTypes[remoteType]++;
415       } else {
416         remoteTypes[remoteType] = 1;
417       }
418     }
420     try {
421       let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
422       if (winUtils.gpuProcessPid != -1) {
423         remoteTypes.gpu = 1;
424       }
425     } catch (e) {}
427     if (Services.io.socketProcessLaunched) {
428       remoteTypes.socket = 1;
429     }
431     let data = {
432       remoteTypes,
433       maxWebContentProcesses: Services.appinfo.maxWebProcessCount,
434     };
436     done(data);
437   },
439   async experimentalFeatures(done) {
440     if (AppConstants.platform == "android") {
441       done();
442       return;
443     }
444     let gates = await FeatureGate.all();
445     done(
446       gates.map(gate => {
447         return [
448           gate.title,
449           gate.preference,
450           Services.prefs.getBoolPref(gate.preference),
451         ];
452       })
453     );
454   },
456   async environmentVariables(done) {
457     let Subprocess;
458     try {
459       // Subprocess is not available in all builds
460       Subprocess = ChromeUtils.importESModule(
461         "resource://gre/modules/Subprocess.sys.mjs"
462       ).Subprocess;
463     } catch (ex) {
464       done({});
465       return;
466     }
468     let environment = Subprocess.getEnvironment();
469     let filteredEnvironment = {};
470     // Limit the environment variables to those that we
471     // know may affect Firefox to reduce leaking PII.
472     let filteredEnvironmentKeys = ["xre_", "moz_", "gdk", "display"];
473     for (let key of Object.keys(environment)) {
474       if (filteredEnvironmentKeys.some(k => key.toLowerCase().startsWith(k))) {
475         filteredEnvironment[key] = environment[key];
476       }
477     }
478     done(filteredEnvironment);
479   },
481   modifiedPreferences: function modifiedPreferences(done) {
482     done(getPrefList(name => Services.prefs.prefHasUserValue(name)));
483   },
485   lockedPreferences: function lockedPreferences(done) {
486     done(
487       getPrefList(
488         name =>
489           !PREFS_UNIMPORTANT_LOCKED.includes(name) &&
490           Services.prefs.prefIsLocked(name)
491       )
492     );
493   },
495   places: async function places(done) {
496     const data = AppConstants.MOZ_PLACES
497       ? await lazy.PlacesDBUtils.getEntitiesStatsAndCounts()
498       : [];
499     done(data);
500   },
502   printingPreferences: function printingPreferences(done) {
503     let filter = name => Services.prefs.prefHasUserValue(name);
504     let prefs = getPrefList(filter, ["print."]);
506     // print_printer is special and is the only pref that is outside of the
507     // "print." branch... Maybe we should change it to print.printer or
508     // something...
509     if (filter("print_printer")) {
510       prefs.print_printer = getPref("print_printer");
511     }
513     done(prefs);
514   },
516   graphics: function graphics(done) {
517     function statusMsgForFeature(feature) {
518       // We return an object because in the try-newer-driver case we need to
519       // include the suggested version, which the consumer likely needs to plug
520       // into a format string from a localization file. Rather than returning
521       // a string in some cases and an object in others, return an object always.
522       let msg = { key: "" };
523       try {
524         var status = gfxInfo.getFeatureStatus(feature);
525       } catch (e) {}
526       switch (status) {
527         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE:
528         case Ci.nsIGfxInfo.FEATURE_DISCOURAGED:
529           msg = { key: "blocked-gfx-card" };
530           break;
531         case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION:
532           msg = { key: "blocked-os-version" };
533           break;
534         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION:
535           try {
536             var driverVersion =
537               gfxInfo.getFeatureSuggestedDriverVersion(feature);
538           } catch (e) {}
539           msg = driverVersion
540             ? { key: "try-newer-driver", args: { driverVersion } }
541             : { key: "blocked-driver" };
542           break;
543         case Ci.nsIGfxInfo.FEATURE_BLOCKED_MISMATCHED_VERSION:
544           msg = { key: "blocked-mismatched-version" };
545           break;
546       }
547       return msg;
548     }
550     let data = {};
552     try {
553       // nsIGfxInfo may not be implemented on some platforms.
554       var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
555     } catch (e) {}
557     data.desktopEnvironment = Services.appinfo.desktopEnvironment;
558     data.numTotalWindows = 0;
559     data.numAcceleratedWindows = 0;
561     let devicePixelRatios = [];
563     for (let win of Services.ww.getWindowEnumerator()) {
564       let winUtils = win.windowUtils;
565       try {
566         // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report
567         if (
568           winUtils.layerManagerType == "None" ||
569           !winUtils.layerManagerRemote
570         ) {
571           continue;
572         }
573         devicePixelRatios.push(win.devicePixelRatio);
575         data.numTotalWindows++;
576         data.windowLayerManagerType = winUtils.layerManagerType;
577         data.windowLayerManagerRemote = winUtils.layerManagerRemote;
578       } catch (e) {
579         continue;
580       }
581       if (data.windowLayerManagerType != "Basic") {
582         data.numAcceleratedWindows++;
583       }
584     }
585     data.graphicsDevicePixelRatios = devicePixelRatios;
587     // If we had no OMTC windows, report back Basic Layers.
588     if (!data.windowLayerManagerType) {
589       data.windowLayerManagerType = "Basic";
590       data.windowLayerManagerRemote = false;
591     }
593     if (!data.numAcceleratedWindows && gfxInfo) {
594       let win = AppConstants.platform == "win";
595       let feature = win
596         ? gfxInfo.FEATURE_DIRECT3D_9_LAYERS
597         : gfxInfo.FEATURE_OPENGL_LAYERS;
598       data.numAcceleratedWindowsMessage = statusMsgForFeature(feature);
599     }
601     if (gfxInfo) {
602       // keys are the names of attributes on nsIGfxInfo, values become the names
603       // of the corresponding properties in our data object.  A null value means
604       // no change.  This is needed so that the names of properties in the data
605       // object are the same as the names of keys in aboutSupport.properties.
606       let gfxInfoProps = {
607         adapterDescription: null,
608         adapterVendorID: null,
609         adapterDeviceID: null,
610         adapterSubsysID: null,
611         adapterRAM: null,
612         adapterDriver: "adapterDrivers",
613         adapterDriverVendor: "driverVendor",
614         adapterDriverVersion: "driverVersion",
615         adapterDriverDate: "driverDate",
617         adapterDescription2: null,
618         adapterVendorID2: null,
619         adapterDeviceID2: null,
620         adapterSubsysID2: null,
621         adapterRAM2: null,
622         adapterDriver2: "adapterDrivers2",
623         adapterDriverVendor2: "driverVendor2",
624         adapterDriverVersion2: "driverVersion2",
625         adapterDriverDate2: "driverDate2",
626         isGPU2Active: null,
628         D2DEnabled: "direct2DEnabled",
629         DWriteEnabled: "directWriteEnabled",
630         DWriteVersion: "directWriteVersion",
631         cleartypeParameters: "clearTypeParameters",
632         TargetFrameRate: "targetFrameRate",
633         windowProtocol: null,
634       };
636       for (let prop in gfxInfoProps) {
637         try {
638           data[gfxInfoProps[prop] || prop] = gfxInfo[prop];
639         } catch (e) {}
640       }
642       if ("direct2DEnabled" in data && !data.direct2DEnabled) {
643         data.direct2DEnabledMessage = statusMsgForFeature(
644           Ci.nsIGfxInfo.FEATURE_DIRECT2D
645         );
646       }
647     }
649     let doc = new DOMParser().parseFromString("<html/>", "text/html");
651     function GetWebGLInfo(data, keyPrefix, contextType) {
652       data[keyPrefix + "Renderer"] = "-";
653       data[keyPrefix + "Version"] = "-";
654       data[keyPrefix + "DriverExtensions"] = "-";
655       data[keyPrefix + "Extensions"] = "-";
656       data[keyPrefix + "WSIInfo"] = "-";
658       // //
660       let canvas = doc.createElement("canvas");
661       canvas.width = 1;
662       canvas.height = 1;
664       // //
666       let creationError = null;
668       canvas.addEventListener(
669         "webglcontextcreationerror",
671         function (e) {
672           creationError = e.statusMessage;
673         }
674       );
676       let gl = null;
677       try {
678         gl = canvas.getContext(contextType);
679       } catch (e) {
680         if (!creationError) {
681           creationError = e.toString();
682         }
683       }
684       if (!gl) {
685         data[keyPrefix + "Renderer"] =
686           creationError || "(no creation error info)";
687         return;
688       }
690       // //
692       data[keyPrefix + "Extensions"] = gl.getSupportedExtensions().join(" ");
694       // //
696       let ext = gl.getExtension("MOZ_debug");
697       // This extension is unconditionally available to chrome. No need to check.
698       let vendor = ext.getParameter(gl.VENDOR);
699       let renderer = ext.getParameter(gl.RENDERER);
701       data[keyPrefix + "Renderer"] = vendor + " -- " + renderer;
702       data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION);
703       data[keyPrefix + "DriverExtensions"] = ext.getParameter(ext.EXTENSIONS);
704       data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO);
706       // //
708       // Eagerly free resources.
709       let loseExt = gl.getExtension("WEBGL_lose_context");
710       if (loseExt) {
711         loseExt.loseContext();
712       }
713     }
715     GetWebGLInfo(data, "webgl1", "webgl");
716     GetWebGLInfo(data, "webgl2", "webgl2");
718     if (gfxInfo) {
719       let infoInfo = gfxInfo.getInfo();
720       if (infoInfo) {
721         data.info = infoInfo;
722       }
724       let failureIndices = {};
726       let failures = gfxInfo.getFailures(failureIndices);
727       if (failures.length) {
728         data.failures = failures;
729         if (failureIndices.value.length == failures.length) {
730           data.indices = failureIndices.value;
731         }
732       }
734       data.featureLog = gfxInfo.getFeatureLog();
735       data.crashGuards = gfxInfo.getActiveCrashGuards();
736     }
738     function getNavigator() {
739       for (let win of Services.ww.getWindowEnumerator()) {
740         let winUtils = win.windowUtils;
741         try {
742           // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report
743           if (
744             winUtils.layerManagerType == "None" ||
745             !winUtils.layerManagerRemote
746           ) {
747             continue;
748           }
749           const nav = win.navigator;
750           if (nav) {
751             return nav;
752           }
753         } catch (e) {
754           continue;
755         }
756       }
757       throw new Error("No window had window.navigator.");
758     }
760     const navigator = getNavigator();
762     async function GetWebgpuInfo(adapterOpts) {
763       const ret = {};
764       if (!navigator.gpu) {
765         ret["navigator.gpu"] = null;
766         return ret;
767       }
769       const requestAdapterkey = `navigator.gpu.requestAdapter(${JSON.stringify(
770         adapterOpts
771       )})`;
773       let adapter;
774       try {
775         adapter = await navigator.gpu.requestAdapter(adapterOpts);
776       } catch (e) {
777         // If WebGPU isn't supported or is blocked somehow, include
778         // that in the report. Anything else is an error which should
779         // have consequences (test failures, etc).
780         if (DOMException.isInstance(e) && e.name == "NotSupportedError") {
781           return { [requestAdapterkey]: { not_supported: e.message } };
782         }
783         throw e;
784       }
786       if (!adapter) {
787         ret[requestAdapterkey] = null;
788         return ret;
789       }
790       const desc = (ret[requestAdapterkey] = {});
792       desc.isFallbackAdapter = adapter.isFallbackAdapter;
794       const adapterInfo = await adapter.requestAdapterInfo();
795       // We can't directly enumerate properties of instances of `GPUAdapterInfo`s, so use the prototype instead.
796       const adapterInfoObj = {};
797       for (const k of Object.keys(Object.getPrototypeOf(adapterInfo)).sort()) {
798         adapterInfoObj[k] = adapterInfo[k];
799       }
800       desc[`requestAdapterInfo()`] = adapterInfoObj;
802       desc.features = Array.from(adapter.features).sort();
804       desc.limits = {};
805       const keys = Object.keys(Object.getPrototypeOf(adapter.limits)).sort(); // limits not directly enumerable?
806       for (const k of keys) {
807         desc.limits[k] = adapter.limits[k];
808       }
810       return ret;
811     }
813     // Webgpu info is going to need awaits.
814     (async () => {
815       data.webgpuDefaultAdapter = await GetWebgpuInfo({});
816       data.webgpuFallbackAdapter = await GetWebgpuInfo({
817         forceFallbackAdapter: true,
818       });
820       done(data);
821     })();
822   },
824   media: function media(done) {
825     function convertDevices(devices) {
826       if (!devices) {
827         return undefined;
828       }
829       let infos = [];
830       for (let i = 0; i < devices.length; ++i) {
831         let device = devices.queryElementAt(i, Ci.nsIAudioDeviceInfo);
832         infos.push({
833           name: device.name,
834           groupId: device.groupId,
835           vendor: device.vendor,
836           type: device.type,
837           state: device.state,
838           preferred: device.preferred,
839           supportedFormat: device.supportedFormat,
840           defaultFormat: device.defaultFormat,
841           maxChannels: device.maxChannels,
842           defaultRate: device.defaultRate,
843           maxRate: device.maxRate,
844           minRate: device.minRate,
845           maxLatency: device.maxLatency,
846           minLatency: device.minLatency,
847         });
848       }
849       return infos;
850     }
852     let data = {};
853     let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
854     data.currentAudioBackend = winUtils.currentAudioBackend;
855     data.currentMaxAudioChannels = winUtils.currentMaxAudioChannels;
856     data.currentPreferredSampleRate = winUtils.currentPreferredSampleRate;
857     data.audioOutputDevices = convertDevices(
858       winUtils
859         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_OUTPUT)
860         .QueryInterface(Ci.nsIArray)
861     );
862     data.audioInputDevices = convertDevices(
863       winUtils
864         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_INPUT)
865         .QueryInterface(Ci.nsIArray)
866     );
868     data.codecSupportInfo = "Unknown";
870     // We initialize gfxInfo here in the same way as in the media
871     // section -- should we break this out into a separate function?
872     try {
873       // nsIGfxInfo may not be implemented on some platforms.
874       var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
876       // Note: CodecSupportInfo is not populated until we have
877       // actually instantiated a PDM. We may want to add a button
878       // or some other means of allowing the user to manually
879       // instantiate a PDM to ensure that the data is available.
880       data.codecSupportInfo = gfxInfo.CodecSupportInfo;
881     } catch (e) {}
883     done(data);
884   },
886   accessibility: function accessibility(done) {
887     let data = {};
888     data.isActive = Services.appinfo.accessibilityEnabled;
889     // eslint-disable-next-line mozilla/use-default-preference-values
890     try {
891       data.forceDisabled = Services.prefs.getIntPref(
892         "accessibility.force_disabled"
893       );
894     } catch (e) {}
895     data.instantiator = Services.appinfo.accessibilityInstantiator;
896     done(data);
897   },
899   startupCache: function startupCache(done) {
900     const startupInfo = Cc["@mozilla.org/startupcacheinfo;1"].getService(
901       Ci.nsIStartupCacheInfo
902     );
903     done({
904       DiskCachePath: startupInfo.DiskCachePath,
905       IgnoreDiskCache: startupInfo.IgnoreDiskCache,
906       FoundDiskCacheOnInit: startupInfo.FoundDiskCacheOnInit,
907       WroteToDiskCache: startupInfo.WroteToDiskCache,
908     });
909   },
911   libraryVersions: function libraryVersions(done) {
912     let data = {};
913     let verInfo = Cc["@mozilla.org/security/nssversion;1"].getService(
914       Ci.nsINSSVersion
915     );
916     for (let prop in verInfo) {
917       let match = /^([^_]+)_((Min)?Version)$/.exec(prop);
918       if (match) {
919         let verProp = match[2][0].toLowerCase() + match[2].substr(1);
920         data[match[1]] = data[match[1]] || {};
921         data[match[1]][verProp] = verInfo[prop];
922       }
923     }
924     done(data);
925   },
927   userJS: function userJS(done) {
928     let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile);
929     userJSFile.append("user.js");
930     done({
931       exists: userJSFile.exists() && userJSFile.fileSize > 0,
932     });
933   },
935   intl: function intl(done) {
936     const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
937       Ci.mozIOSPreferences
938     );
939     done({
940       localeService: {
941         requested: Services.locale.requestedLocales,
942         available: Services.locale.availableLocales,
943         supported: Services.locale.appLocalesAsBCP47,
944         regionalPrefs: Services.locale.regionalPrefsLocales,
945         defaultLocale: Services.locale.defaultLocale,
946       },
947       osPrefs: {
948         systemLocales: osPrefs.systemLocales,
949         regionalPrefsLocales: osPrefs.regionalPrefsLocales,
950       },
951     });
952   },
954   async normandy(done) {
955     if (!AppConstants.MOZ_NORMANDY) {
956       done();
957       return;
958     }
960     const { PreferenceExperiments: NormandyPreferenceStudies } =
961       ChromeUtils.importESModule(
962         "resource://normandy/lib/PreferenceExperiments.sys.mjs"
963       );
964     const { AddonStudies: NormandyAddonStudies } = ChromeUtils.importESModule(
965       "resource://normandy/lib/AddonStudies.sys.mjs"
966     );
967     const { PreferenceRollouts: NormandyPreferenceRollouts } =
968       ChromeUtils.importESModule(
969         "resource://normandy/lib/PreferenceRollouts.sys.mjs"
970       );
971     const { ExperimentManager } = ChromeUtils.importESModule(
972       "resource://nimbus/lib/ExperimentManager.sys.mjs"
973     );
975     // Get Normandy data in parallel, and sort each group by slug.
976     const [
977       addonStudies,
978       prefRollouts,
979       prefStudies,
980       nimbusExperiments,
981       nimbusRollouts,
982     ] = await Promise.all(
983       [
984         NormandyAddonStudies.getAllActive(),
985         NormandyPreferenceRollouts.getAllActive(),
986         NormandyPreferenceStudies.getAllActive(),
987         ExperimentManager.store
988           .ready()
989           .then(() => ExperimentManager.store.getAllActiveExperiments()),
990         ExperimentManager.store
991           .ready()
992           .then(() => ExperimentManager.store.getAllActiveRollouts()),
993       ].map(promise =>
994         promise
995           .catch(error => {
996             console.error(error);
997             return [];
998           })
999           .then(items => items.sort((a, b) => a.slug.localeCompare(b.slug)))
1000       )
1001     );
1003     done({
1004       addonStudies,
1005       prefRollouts,
1006       prefStudies,
1007       nimbusExperiments,
1008       nimbusRollouts,
1009     });
1010   },
1013 if (AppConstants.MOZ_CRASHREPORTER) {
1014   dataProviders.crashes = function crashes(done) {
1015     const { CrashReports } = ChromeUtils.importESModule(
1016       "resource://gre/modules/CrashReports.sys.mjs"
1017     );
1018     let reports = CrashReports.getReports();
1019     let now = new Date();
1020     let reportsNew = reports.filter(
1021       report => now - report.date < Troubleshoot.kMaxCrashAge
1022     );
1023     let reportsSubmitted = reportsNew.filter(report => !report.pending);
1024     let reportsPendingCount = reportsNew.length - reportsSubmitted.length;
1025     let data = { submitted: reportsSubmitted, pending: reportsPendingCount };
1026     done(data);
1027   };
1030 if (AppConstants.MOZ_SANDBOX) {
1031   dataProviders.sandbox = function sandbox(done) {
1032     let data = {};
1033     if (AppConstants.unixstyle == "linux") {
1034       const keys = [
1035         "hasSeccompBPF",
1036         "hasSeccompTSync",
1037         "hasPrivilegedUserNamespaces",
1038         "hasUserNamespaces",
1039         "canSandboxContent",
1040         "canSandboxMedia",
1041       ];
1043       for (let key of keys) {
1044         if (Services.sysinfo.hasKey(key)) {
1045           data[key] = Services.sysinfo.getPropertyAsBool(key);
1046         }
1047       }
1049       let reporter = Cc["@mozilla.org/sandbox/syscall-reporter;1"].getService(
1050         Ci.mozISandboxReporter
1051       );
1052       const snapshot = reporter.snapshot();
1053       let syscalls = [];
1054       for (let index = snapshot.begin; index < snapshot.end; ++index) {
1055         let report = snapshot.getElement(index);
1056         let { msecAgo, pid, tid, procType, syscall } = report;
1057         let args = [];
1058         for (let i = 0; i < report.numArgs; ++i) {
1059           args.push(report.getArg(i));
1060         }
1061         syscalls.push({ index, msecAgo, pid, tid, procType, syscall, args });
1062       }
1063       data.syscallLog = syscalls;
1064     }
1066     if (AppConstants.MOZ_SANDBOX) {
1067       let sandboxSettings = Cc[
1068         "@mozilla.org/sandbox/sandbox-settings;1"
1069       ].getService(Ci.mozISandboxSettings);
1070       data.contentSandboxLevel = Services.prefs.getIntPref(
1071         "security.sandbox.content.level"
1072       );
1073       data.effectiveContentSandboxLevel =
1074         sandboxSettings.effectiveContentSandboxLevel;
1075       data.contentWin32kLockdownState =
1076         sandboxSettings.contentWin32kLockdownStateString;
1077       data.supportSandboxGpuLevel = Services.prefs.getIntPref(
1078         "security.sandbox.gpu.level"
1079       );
1080     }
1082     done(data);
1083   };
1086 if (AppConstants.ENABLE_WEBDRIVER) {
1087   dataProviders.remoteAgent = function remoteAgent(done) {
1088     const { RemoteAgent } = ChromeUtils.importESModule(
1089       "chrome://remote/content/components/RemoteAgent.sys.mjs"
1090     );
1091     const { running, scheme, host, port } = RemoteAgent;
1092     let url = "";
1093     if (running) {
1094       url = `${scheme}://${host}:${port}/`;
1095     }
1096     done({ running, url });
1097   };