Bug 1867925 - Mark some storage-access-api tests as intermittent after wpt-sync....
[gecko.git] / toolkit / modules / Troubleshoot.sys.mjs
bloba724ded24895c13bd89ede752fbc931da40f4211
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   "extensions.backgroundServiceWorkerEnabled.enabled",
139   "privacy.restrict3rdpartystorage.url_decorations",
142 function getPref(name) {
143   let type = Services.prefs.getPrefType(name);
144   if (!(type in PREFS_GETTERS)) {
145     throw new Error("Unknown preference type " + type + " for " + name);
146   }
147   return PREFS_GETTERS[type](Services.prefs, name);
150 // Return the preferences filtered by PREF_REGEXES_NOT_TO_DISPLAY and PREFS_FOR_DISPLAY
151 // and also by the custom 'filter'-ing function.
152 function getPrefList(filter, allowlist = PREFS_FOR_DISPLAY) {
153   return allowlist.reduce(function (prefs, branch) {
154     Services.prefs.getChildList(branch).forEach(function (name) {
155       if (
156         filter(name) &&
157         !PREF_REGEXES_NOT_TO_DISPLAY.some(re => re.test(name))
158       ) {
159         prefs[name] = getPref(name);
160       }
161     });
162     return prefs;
163   }, {});
166 export var Troubleshoot = {
167   /**
168    * Captures a snapshot of data that may help troubleshooters troubleshoot
169    * trouble.
170    *
171    * @returns {Promise}
172    *   A promise that is resolved with the snapshot data.
173    */
174   snapshot() {
175     return new Promise(resolve => {
176       let snapshot = {};
177       let numPending = Object.keys(dataProviders).length;
178       function providerDone(providerName, providerData) {
179         snapshot[providerName] = providerData;
180         if (--numPending == 0) {
181           // Ensure that done is always and truly called asynchronously.
182           Services.tm.dispatchToMainThread(() => resolve(snapshot));
183         }
184       }
185       for (let name in dataProviders) {
186         try {
187           dataProviders[name](providerDone.bind(null, name));
188         } catch (err) {
189           let msg = "Troubleshoot data provider failed: " + name + "\n" + err;
190           console.error(msg);
191           providerDone(name, msg);
192         }
193       }
194     });
195   },
197   kMaxCrashAge: 3 * 24 * 60 * 60 * 1000, // 3 days
200 // Each data provider is a name => function mapping.  When a snapshot is
201 // captured, each provider's function is called, and it's the function's job to
202 // generate the provider's data.  The function is passed a "done" callback, and
203 // when done, it must pass its data to the callback.  The resulting snapshot
204 // object will contain a name => data entry for each provider.
205 var dataProviders = {
206   application: async function application(done) {
207     let data = {
208       name: Services.appinfo.name,
209       osVersion:
210         Services.sysinfo.getProperty("name") +
211         " " +
212         Services.sysinfo.getProperty("version") +
213         " " +
214         Services.sysinfo.getProperty("build"),
215       version: AppConstants.MOZ_APP_VERSION_DISPLAY,
216       buildID: Services.appinfo.appBuildID,
217       distributionID: Services.prefs
218         .getDefaultBranch("")
219         .getCharPref("distribution.id", ""),
220       userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].getService(
221         Ci.nsIHttpProtocolHandler
222       ).userAgent,
223       safeMode: Services.appinfo.inSafeMode,
224       memorySizeBytes: Services.sysinfo.getProperty("memsize"),
225       diskAvailableBytes: Services.dirsvc.get("ProfD", Ci.nsIFile)
226         .diskSpaceAvailable,
227     };
229     if (Services.sysinfo.getProperty("name") == "Windows_NT") {
230       if ((await Services.sysinfo.processInfo).isWindowsSMode) {
231         data.osVersion += " S";
232       }
233     }
235     if (AppConstants.MOZ_UPDATER) {
236       data.updateChannel = ChromeUtils.importESModule(
237         "resource://gre/modules/UpdateUtils.sys.mjs"
238       ).UpdateUtils.UpdateChannel;
239     }
241     // eslint-disable-next-line mozilla/use-default-preference-values
242     try {
243       data.vendor = Services.prefs.getCharPref("app.support.vendor");
244     } catch (e) {}
245     try {
246       data.supportURL = Services.urlFormatter.formatURLPref(
247         "app.support.baseURL"
248       );
249     } catch (e) {}
251     data.osTheme = Services.sysinfo.getProperty("osThemeInfo");
253     try {
254       // MacOSX: Check for rosetta status, if it exists
255       data.rosetta = Services.sysinfo.getProperty("rosettaStatus");
256     } catch (e) {}
258     try {
259       // Windows - Get info about attached pointing devices
260       data.pointingDevices = Services.sysinfo
261         .getProperty("pointingDevices")
262         .split(",");
263     } catch (e) {}
265     data.numTotalWindows = 0;
266     data.numFissionWindows = 0;
267     data.numRemoteWindows = 0;
268     for (let { docShell } of Services.wm.getEnumerator("navigator:browser")) {
269       docShell.QueryInterface(Ci.nsILoadContext);
270       data.numTotalWindows++;
271       if (docShell.useRemoteSubframes) {
272         data.numFissionWindows++;
273       }
274       if (docShell.useRemoteTabs) {
275         data.numRemoteWindows++;
276       }
277     }
279     try {
280       data.launcherProcessState = Services.appinfo.launcherProcessState;
281     } catch (e) {}
283     data.fissionAutoStart = Services.appinfo.fissionAutostart;
284     data.fissionDecisionStatus = Services.appinfo.fissionDecisionStatusString;
286     data.remoteAutoStart = Services.appinfo.browserTabsRemoteAutostart;
288     if (Services.policies) {
289       data.policiesStatus = Services.policies.status;
290     }
292     const keyLocationServiceGoogle = Services.urlFormatter
293       .formatURL("%GOOGLE_LOCATION_SERVICE_API_KEY%")
294       .trim();
295     data.keyLocationServiceGoogleFound =
296       keyLocationServiceGoogle != "no-google-location-service-api-key" &&
297       !!keyLocationServiceGoogle.length;
299     const keySafebrowsingGoogle = Services.urlFormatter
300       .formatURL("%GOOGLE_SAFEBROWSING_API_KEY%")
301       .trim();
302     data.keySafebrowsingGoogleFound =
303       keySafebrowsingGoogle != "no-google-safebrowsing-api-key" &&
304       !!keySafebrowsingGoogle.length;
306     const keyMozilla = Services.urlFormatter
307       .formatURL("%MOZILLA_API_KEY%")
308       .trim();
309     data.keyMozillaFound =
310       keyMozilla != "no-mozilla-api-key" && !!keyMozilla.length;
312     done(data);
313   },
315   addons: async function addons(done) {
316     let addons = await AddonManager.getAddonsByTypes([
317       "extension",
318       "locale",
319       "dictionary",
320       "sitepermission",
321       "theme",
322     ]);
323     addons = addons.filter(e => !e.isSystem);
324     addons.sort(function (a, b) {
325       if (a.isActive != b.isActive) {
326         return b.isActive ? 1 : -1;
327       }
329       if (a.type != b.type) {
330         return a.type.localeCompare(b.type);
331       }
333       // In some unfortunate cases add-on names can be null.
334       let aname = a.name || "";
335       let bname = b.name || "";
336       let lc = aname.localeCompare(bname);
337       if (lc != 0) {
338         return lc;
339       }
340       if (a.version != b.version) {
341         return a.version > b.version ? 1 : -1;
342       }
343       return 0;
344     });
345     let props = ["name", "type", "version", "isActive", "id"];
346     done(
347       addons.map(function (ext) {
348         return props.reduce(function (extData, prop) {
349           extData[prop] = ext[prop];
350           return extData;
351         }, {});
352       })
353     );
354   },
356   securitySoftware: function securitySoftware(done) {
357     let data = {};
359     const keys = [
360       "registeredAntiVirus",
361       "registeredAntiSpyware",
362       "registeredFirewall",
363     ];
364     for (let key of keys) {
365       let prop = "";
366       try {
367         prop = Services.sysinfo.getProperty(key);
368       } catch (e) {}
370       data[key] = prop;
371     }
373     done(data);
374   },
376   features: async function features(done) {
377     let features = await AddonManager.getAddonsByTypes(["extension"]);
378     features = features.filter(f => f.isSystem);
379     features.sort(function (a, b) {
380       // In some unfortunate cases addon names can be null.
381       let aname = a.name || null;
382       let bname = b.name || null;
383       let lc = aname.localeCompare(bname);
384       if (lc != 0) {
385         return lc;
386       }
387       if (a.version != b.version) {
388         return a.version > b.version ? 1 : -1;
389       }
390       return 0;
391     });
392     let props = ["name", "version", "id"];
393     done(
394       features.map(function (f) {
395         return props.reduce(function (fData, prop) {
396           fData[prop] = f[prop];
397           return fData;
398         }, {});
399       })
400     );
401   },
403   processes: async function processes(done) {
404     let remoteTypes = {};
405     const processInfo = await ChromeUtils.requestProcInfo();
406     for (let i = 0; i < processInfo.children.length; i++) {
407       let remoteType;
408       try {
409         remoteType = processInfo.children[i].type;
410         // Workaround for bug 1790070, since requestProcInfo refers to the preallocated content
411         // process as "preallocated", and the localization string mapping expects "prealloc".
412         remoteType = remoteType === "preallocated" ? "prealloc" : remoteType;
413       } catch (e) {}
415       // The parent process is also managed by the ppmm (because
416       // of non-remote tabs), but it doesn't have a remoteType.
417       if (!remoteType) {
418         continue;
419       }
421       if (remoteTypes[remoteType]) {
422         remoteTypes[remoteType]++;
423       } else {
424         remoteTypes[remoteType] = 1;
425       }
426     }
428     try {
429       let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
430       if (winUtils.gpuProcessPid != -1) {
431         remoteTypes.gpu = 1;
432       }
433     } catch (e) {}
435     if (Services.io.socketProcessLaunched) {
436       remoteTypes.socket = 1;
437     }
439     let data = {
440       remoteTypes,
441       maxWebContentProcesses: Services.appinfo.maxWebProcessCount,
442     };
444     done(data);
445   },
447   async experimentalFeatures(done) {
448     if (AppConstants.platform == "android") {
449       done();
450       return;
451     }
452     let gates = await FeatureGate.all();
453     done(
454       gates.map(gate => {
455         return [
456           gate.title,
457           gate.preference,
458           Services.prefs.getBoolPref(gate.preference),
459         ];
460       })
461     );
462   },
464   async legacyUserStylesheets(done) {
465     if (AppConstants.platform == "android") {
466       done({ active: false, types: [] });
467       return;
468     }
470     let active = Services.prefs.getBoolPref(
471       "toolkit.legacyUserProfileCustomizations.stylesheets"
472     );
473     let types = [];
474     for (let name of ["userChrome.css", "userContent.css"]) {
475       let path = PathUtils.join(PathUtils.profileDir, "chrome", name);
476       if (await IOUtils.exists(path)) {
477         types.push(name);
478       }
479     }
480     done({ active, types });
481   },
483   async environmentVariables(done) {
484     let Subprocess;
485     try {
486       // Subprocess is not available in all builds
487       Subprocess = ChromeUtils.importESModule(
488         "resource://gre/modules/Subprocess.sys.mjs"
489       ).Subprocess;
490     } catch (ex) {
491       done({});
492       return;
493     }
495     let environment = Subprocess.getEnvironment();
496     let filteredEnvironment = {};
497     // Limit the environment variables to those that we
498     // know may affect Firefox to reduce leaking PII.
499     let filteredEnvironmentKeys = ["xre_", "moz_", "gdk", "display"];
500     for (let key of Object.keys(environment)) {
501       if (filteredEnvironmentKeys.some(k => key.toLowerCase().startsWith(k))) {
502         filteredEnvironment[key] = environment[key];
503       }
504     }
505     done(filteredEnvironment);
506   },
508   modifiedPreferences: function modifiedPreferences(done) {
509     done(getPrefList(name => Services.prefs.prefHasUserValue(name)));
510   },
512   lockedPreferences: function lockedPreferences(done) {
513     done(
514       getPrefList(
515         name =>
516           !PREFS_UNIMPORTANT_LOCKED.includes(name) &&
517           Services.prefs.prefIsLocked(name)
518       )
519     );
520   },
522   places: async function places(done) {
523     const data = AppConstants.MOZ_PLACES
524       ? await lazy.PlacesDBUtils.getEntitiesStatsAndCounts()
525       : [];
526     done(data);
527   },
529   printingPreferences: function printingPreferences(done) {
530     let filter = name => Services.prefs.prefHasUserValue(name);
531     let prefs = getPrefList(filter, ["print."]);
533     // print_printer is special and is the only pref that is outside of the
534     // "print." branch... Maybe we should change it to print.printer or
535     // something...
536     if (filter("print_printer")) {
537       prefs.print_printer = getPref("print_printer");
538     }
540     done(prefs);
541   },
543   graphics: function graphics(done) {
544     function statusMsgForFeature(feature) {
545       // We return an object because in the try-newer-driver case we need to
546       // include the suggested version, which the consumer likely needs to plug
547       // into a format string from a localization file. Rather than returning
548       // a string in some cases and an object in others, return an object always.
549       let msg = { key: "" };
550       try {
551         var status = gfxInfo.getFeatureStatus(feature);
552       } catch (e) {}
553       switch (status) {
554         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE:
555         case Ci.nsIGfxInfo.FEATURE_DISCOURAGED:
556           msg = { key: "blocked-gfx-card" };
557           break;
558         case Ci.nsIGfxInfo.FEATURE_BLOCKED_OS_VERSION:
559           msg = { key: "blocked-os-version" };
560           break;
561         case Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION:
562           try {
563             var driverVersion =
564               gfxInfo.getFeatureSuggestedDriverVersion(feature);
565           } catch (e) {}
566           msg = driverVersion
567             ? { key: "try-newer-driver", args: { driverVersion } }
568             : { key: "blocked-driver" };
569           break;
570         case Ci.nsIGfxInfo.FEATURE_BLOCKED_MISMATCHED_VERSION:
571           msg = { key: "blocked-mismatched-version" };
572           break;
573       }
574       return msg;
575     }
577     let data = {};
579     try {
580       // nsIGfxInfo may not be implemented on some platforms.
581       var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
582     } catch (e) {}
584     data.desktopEnvironment = Services.appinfo.desktopEnvironment;
585     data.numTotalWindows = 0;
586     data.numAcceleratedWindows = 0;
588     let devicePixelRatios = [];
590     for (let win of Services.ww.getWindowEnumerator()) {
591       let winUtils = win.windowUtils;
592       try {
593         // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report
594         if (
595           winUtils.layerManagerType == "None" ||
596           !winUtils.layerManagerRemote
597         ) {
598           continue;
599         }
600         devicePixelRatios.push(win.devicePixelRatio);
602         data.numTotalWindows++;
603         data.windowLayerManagerType = winUtils.layerManagerType;
604         data.windowLayerManagerRemote = winUtils.layerManagerRemote;
605       } catch (e) {
606         continue;
607       }
608       if (data.windowLayerManagerType != "Basic") {
609         data.numAcceleratedWindows++;
610       }
611     }
612     data.graphicsDevicePixelRatios = devicePixelRatios;
614     // If we had no OMTC windows, report back Basic Layers.
615     if (!data.windowLayerManagerType) {
616       data.windowLayerManagerType = "Basic";
617       data.windowLayerManagerRemote = false;
618     }
620     if (!data.numAcceleratedWindows && gfxInfo) {
621       let win = AppConstants.platform == "win";
622       let feature = win
623         ? gfxInfo.FEATURE_DIRECT3D_9_LAYERS
624         : gfxInfo.FEATURE_OPENGL_LAYERS;
625       data.numAcceleratedWindowsMessage = statusMsgForFeature(feature);
626     }
628     if (gfxInfo) {
629       // keys are the names of attributes on nsIGfxInfo, values become the names
630       // of the corresponding properties in our data object.  A null value means
631       // no change.  This is needed so that the names of properties in the data
632       // object are the same as the names of keys in aboutSupport.properties.
633       let gfxInfoProps = {
634         adapterDescription: null,
635         adapterVendorID: null,
636         adapterDeviceID: null,
637         adapterSubsysID: null,
638         adapterRAM: null,
639         adapterDriver: "adapterDrivers",
640         adapterDriverVendor: "driverVendor",
641         adapterDriverVersion: "driverVersion",
642         adapterDriverDate: "driverDate",
644         adapterDescription2: null,
645         adapterVendorID2: null,
646         adapterDeviceID2: null,
647         adapterSubsysID2: null,
648         adapterRAM2: null,
649         adapterDriver2: "adapterDrivers2",
650         adapterDriverVendor2: "driverVendor2",
651         adapterDriverVersion2: "driverVersion2",
652         adapterDriverDate2: "driverDate2",
653         isGPU2Active: null,
655         D2DEnabled: "direct2DEnabled",
656         DWriteEnabled: "directWriteEnabled",
657         DWriteVersion: "directWriteVersion",
658         cleartypeParameters: "clearTypeParameters",
659         TargetFrameRate: "targetFrameRate",
660         windowProtocol: null,
661       };
663       for (let prop in gfxInfoProps) {
664         try {
665           data[gfxInfoProps[prop] || prop] = gfxInfo[prop];
666         } catch (e) {}
667       }
669       if ("direct2DEnabled" in data && !data.direct2DEnabled) {
670         data.direct2DEnabledMessage = statusMsgForFeature(
671           Ci.nsIGfxInfo.FEATURE_DIRECT2D
672         );
673       }
674     }
676     let doc = new DOMParser().parseFromString("<html/>", "text/html");
678     function GetWebGLInfo(data, keyPrefix, contextType) {
679       data[keyPrefix + "Renderer"] = "-";
680       data[keyPrefix + "Version"] = "-";
681       data[keyPrefix + "DriverExtensions"] = "-";
682       data[keyPrefix + "Extensions"] = "-";
683       data[keyPrefix + "WSIInfo"] = "-";
685       // //
687       let canvas = doc.createElement("canvas");
688       canvas.width = 1;
689       canvas.height = 1;
691       // //
693       let creationError = null;
695       canvas.addEventListener(
696         "webglcontextcreationerror",
698         function (e) {
699           creationError = e.statusMessage;
700         }
701       );
703       let gl = null;
704       try {
705         gl = canvas.getContext(contextType);
706       } catch (e) {
707         if (!creationError) {
708           creationError = e.toString();
709         }
710       }
711       if (!gl) {
712         data[keyPrefix + "Renderer"] =
713           creationError || "(no creation error info)";
714         return;
715       }
717       // //
719       data[keyPrefix + "Extensions"] = gl.getSupportedExtensions().join(" ");
721       // //
723       let ext = gl.getExtension("MOZ_debug");
724       // This extension is unconditionally available to chrome. No need to check.
725       let vendor = ext.getParameter(gl.VENDOR);
726       let renderer = ext.getParameter(gl.RENDERER);
728       data[keyPrefix + "Renderer"] = vendor + " -- " + renderer;
729       data[keyPrefix + "Version"] = ext.getParameter(gl.VERSION);
730       data[keyPrefix + "DriverExtensions"] = ext.getParameter(ext.EXTENSIONS);
731       data[keyPrefix + "WSIInfo"] = ext.getParameter(ext.WSI_INFO);
733       // //
735       // Eagerly free resources.
736       let loseExt = gl.getExtension("WEBGL_lose_context");
737       if (loseExt) {
738         loseExt.loseContext();
739       }
740     }
742     GetWebGLInfo(data, "webgl1", "webgl");
743     GetWebGLInfo(data, "webgl2", "webgl2");
745     if (gfxInfo) {
746       let infoInfo = gfxInfo.getInfo();
747       if (infoInfo) {
748         data.info = infoInfo;
749       }
751       let failureIndices = {};
753       let failures = gfxInfo.getFailures(failureIndices);
754       if (failures.length) {
755         data.failures = failures;
756         if (failureIndices.value.length == failures.length) {
757           data.indices = failureIndices.value;
758         }
759       }
761       data.featureLog = gfxInfo.getFeatureLog();
762       data.crashGuards = gfxInfo.getActiveCrashGuards();
763     }
765     function getNavigator() {
766       for (let win of Services.ww.getWindowEnumerator()) {
767         let winUtils = win.windowUtils;
768         try {
769           // NOTE: windowless browser's windows should not be reported in the graphics troubleshoot report
770           if (
771             winUtils.layerManagerType == "None" ||
772             !winUtils.layerManagerRemote
773           ) {
774             continue;
775           }
776           const nav = win.navigator;
777           if (nav) {
778             return nav;
779           }
780         } catch (e) {
781           continue;
782         }
783       }
784       throw new Error("No window had window.navigator.");
785     }
787     const navigator = getNavigator();
789     async function GetWebgpuInfo(adapterOpts) {
790       const ret = {};
791       if (!navigator.gpu) {
792         ret["navigator.gpu"] = null;
793         return ret;
794       }
796       const requestAdapterkey = `navigator.gpu.requestAdapter(${JSON.stringify(
797         adapterOpts
798       )})`;
800       let adapter;
801       try {
802         adapter = await navigator.gpu.requestAdapter(adapterOpts);
803       } catch (e) {
804         // If WebGPU isn't supported or is blocked somehow, include
805         // that in the report. Anything else is an error which should
806         // have consequences (test failures, etc).
807         if (DOMException.isInstance(e) && e.name == "NotSupportedError") {
808           return { [requestAdapterkey]: { not_supported: e.message } };
809         }
810         throw e;
811       }
813       if (!adapter) {
814         ret[requestAdapterkey] = null;
815         return ret;
816       }
817       const desc = (ret[requestAdapterkey] = {});
819       desc.isFallbackAdapter = adapter.isFallbackAdapter;
821       const adapterInfo = await adapter.requestAdapterInfo();
822       // We can't directly enumerate properties of instances of `GPUAdapterInfo`s, so use the prototype instead.
823       const adapterInfoObj = {};
824       for (const k of Object.keys(Object.getPrototypeOf(adapterInfo)).sort()) {
825         adapterInfoObj[k] = adapterInfo[k];
826       }
827       desc[`requestAdapterInfo()`] = adapterInfoObj;
829       desc.features = Array.from(adapter.features).sort();
831       desc.limits = {};
832       const keys = Object.keys(Object.getPrototypeOf(adapter.limits)).sort(); // limits not directly enumerable?
833       for (const k of keys) {
834         desc.limits[k] = adapter.limits[k];
835       }
837       return ret;
838     }
840     // Webgpu info is going to need awaits.
841     (async () => {
842       data.webgpuDefaultAdapter = await GetWebgpuInfo({});
843       data.webgpuFallbackAdapter = await GetWebgpuInfo({
844         forceFallbackAdapter: true,
845       });
847       done(data);
848     })();
849   },
851   media: function media(done) {
852     function convertDevices(devices) {
853       if (!devices) {
854         return undefined;
855       }
856       let infos = [];
857       for (let i = 0; i < devices.length; ++i) {
858         let device = devices.queryElementAt(i, Ci.nsIAudioDeviceInfo);
859         infos.push({
860           name: device.name,
861           groupId: device.groupId,
862           vendor: device.vendor,
863           type: device.type,
864           state: device.state,
865           preferred: device.preferred,
866           supportedFormat: device.supportedFormat,
867           defaultFormat: device.defaultFormat,
868           maxChannels: device.maxChannels,
869           defaultRate: device.defaultRate,
870           maxRate: device.maxRate,
871           minRate: device.minRate,
872           maxLatency: device.maxLatency,
873           minLatency: device.minLatency,
874         });
875       }
876       return infos;
877     }
879     let data = {};
880     let winUtils = Services.wm.getMostRecentWindow("").windowUtils;
881     data.currentAudioBackend = winUtils.currentAudioBackend;
882     data.currentMaxAudioChannels = winUtils.currentMaxAudioChannels;
883     data.currentPreferredSampleRate = winUtils.currentPreferredSampleRate;
884     data.audioOutputDevices = convertDevices(
885       winUtils
886         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_OUTPUT)
887         .QueryInterface(Ci.nsIArray)
888     );
889     data.audioInputDevices = convertDevices(
890       winUtils
891         .audioDevices(Ci.nsIDOMWindowUtils.AUDIO_INPUT)
892         .QueryInterface(Ci.nsIArray)
893     );
895     data.codecSupportInfo = "Unknown";
897     // We initialize gfxInfo here in the same way as in the media
898     // section -- should we break this out into a separate function?
899     try {
900       // nsIGfxInfo may not be implemented on some platforms.
901       var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
903       // Note: CodecSupportInfo is not populated until we have
904       // actually instantiated a PDM. We may want to add a button
905       // or some other means of allowing the user to manually
906       // instantiate a PDM to ensure that the data is available.
907       data.codecSupportInfo = gfxInfo.CodecSupportInfo;
908     } catch (e) {}
910     done(data);
911   },
913   accessibility: function accessibility(done) {
914     let data = {};
915     data.isActive = Services.appinfo.accessibilityEnabled;
916     // eslint-disable-next-line mozilla/use-default-preference-values
917     try {
918       data.forceDisabled = Services.prefs.getIntPref(
919         "accessibility.force_disabled"
920       );
921     } catch (e) {}
922     data.instantiator = Services.appinfo.accessibilityInstantiator;
923     done(data);
924   },
926   startupCache: function startupCache(done) {
927     const startupInfo = Cc["@mozilla.org/startupcacheinfo;1"].getService(
928       Ci.nsIStartupCacheInfo
929     );
930     done({
931       DiskCachePath: startupInfo.DiskCachePath,
932       IgnoreDiskCache: startupInfo.IgnoreDiskCache,
933       FoundDiskCacheOnInit: startupInfo.FoundDiskCacheOnInit,
934       WroteToDiskCache: startupInfo.WroteToDiskCache,
935     });
936   },
938   libraryVersions: function libraryVersions(done) {
939     let data = {};
940     let verInfo = Cc["@mozilla.org/security/nssversion;1"].getService(
941       Ci.nsINSSVersion
942     );
943     for (let prop in verInfo) {
944       let match = /^([^_]+)_((Min)?Version)$/.exec(prop);
945       if (match) {
946         let verProp = match[2][0].toLowerCase() + match[2].substr(1);
947         data[match[1]] = data[match[1]] || {};
948         data[match[1]][verProp] = verInfo[prop];
949       }
950     }
951     done(data);
952   },
954   userJS: function userJS(done) {
955     let userJSFile = Services.dirsvc.get("PrefD", Ci.nsIFile);
956     userJSFile.append("user.js");
957     done({
958       exists: userJSFile.exists() && userJSFile.fileSize > 0,
959     });
960   },
962   intl: function intl(done) {
963     const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
964       Ci.mozIOSPreferences
965     );
966     done({
967       localeService: {
968         requested: Services.locale.requestedLocales,
969         available: Services.locale.availableLocales,
970         supported: Services.locale.appLocalesAsBCP47,
971         regionalPrefs: Services.locale.regionalPrefsLocales,
972         defaultLocale: Services.locale.defaultLocale,
973       },
974       osPrefs: {
975         systemLocales: osPrefs.systemLocales,
976         regionalPrefsLocales: osPrefs.regionalPrefsLocales,
977       },
978     });
979   },
981   async normandy(done) {
982     if (!AppConstants.MOZ_NORMANDY) {
983       done();
984       return;
985     }
987     const { PreferenceExperiments: NormandyPreferenceStudies } =
988       ChromeUtils.importESModule(
989         "resource://normandy/lib/PreferenceExperiments.sys.mjs"
990       );
991     const { AddonStudies: NormandyAddonStudies } = ChromeUtils.importESModule(
992       "resource://normandy/lib/AddonStudies.sys.mjs"
993     );
994     const { PreferenceRollouts: NormandyPreferenceRollouts } =
995       ChromeUtils.importESModule(
996         "resource://normandy/lib/PreferenceRollouts.sys.mjs"
997       );
998     const { ExperimentManager } = ChromeUtils.importESModule(
999       "resource://nimbus/lib/ExperimentManager.sys.mjs"
1000     );
1002     // Get Normandy data in parallel, and sort each group by slug.
1003     const [
1004       addonStudies,
1005       prefRollouts,
1006       prefStudies,
1007       nimbusExperiments,
1008       nimbusRollouts,
1009     ] = await Promise.all(
1010       [
1011         NormandyAddonStudies.getAllActive(),
1012         NormandyPreferenceRollouts.getAllActive(),
1013         NormandyPreferenceStudies.getAllActive(),
1014         ExperimentManager.store
1015           .ready()
1016           .then(() => ExperimentManager.store.getAllActiveExperiments()),
1017         ExperimentManager.store
1018           .ready()
1019           .then(() => ExperimentManager.store.getAllActiveRollouts()),
1020       ].map(promise =>
1021         promise
1022           .catch(error => {
1023             console.error(error);
1024             return [];
1025           })
1026           .then(items => items.sort((a, b) => a.slug.localeCompare(b.slug)))
1027       )
1028     );
1030     done({
1031       addonStudies,
1032       prefRollouts,
1033       prefStudies,
1034       nimbusExperiments,
1035       nimbusRollouts,
1036     });
1037   },
1040 if (AppConstants.MOZ_CRASHREPORTER) {
1041   dataProviders.crashes = function crashes(done) {
1042     const { CrashReports } = ChromeUtils.importESModule(
1043       "resource://gre/modules/CrashReports.sys.mjs"
1044     );
1045     let reports = CrashReports.getReports();
1046     let now = new Date();
1047     let reportsNew = reports.filter(
1048       report => now - report.date < Troubleshoot.kMaxCrashAge
1049     );
1050     let reportsSubmitted = reportsNew.filter(report => !report.pending);
1051     let reportsPendingCount = reportsNew.length - reportsSubmitted.length;
1052     let data = { submitted: reportsSubmitted, pending: reportsPendingCount };
1053     done(data);
1054   };
1057 if (AppConstants.MOZ_SANDBOX) {
1058   dataProviders.sandbox = function sandbox(done) {
1059     let data = {};
1060     if (AppConstants.unixstyle == "linux") {
1061       const keys = [
1062         "hasSeccompBPF",
1063         "hasSeccompTSync",
1064         "hasPrivilegedUserNamespaces",
1065         "hasUserNamespaces",
1066         "canSandboxContent",
1067         "canSandboxMedia",
1068       ];
1070       for (let key of keys) {
1071         if (Services.sysinfo.hasKey(key)) {
1072           data[key] = Services.sysinfo.getPropertyAsBool(key);
1073         }
1074       }
1076       let reporter = Cc["@mozilla.org/sandbox/syscall-reporter;1"].getService(
1077         Ci.mozISandboxReporter
1078       );
1079       const snapshot = reporter.snapshot();
1080       let syscalls = [];
1081       for (let index = snapshot.begin; index < snapshot.end; ++index) {
1082         let report = snapshot.getElement(index);
1083         let { msecAgo, pid, tid, procType, syscall } = report;
1084         let args = [];
1085         for (let i = 0; i < report.numArgs; ++i) {
1086           args.push(report.getArg(i));
1087         }
1088         syscalls.push({ index, msecAgo, pid, tid, procType, syscall, args });
1089       }
1090       data.syscallLog = syscalls;
1091     }
1093     if (AppConstants.MOZ_SANDBOX) {
1094       let sandboxSettings = Cc[
1095         "@mozilla.org/sandbox/sandbox-settings;1"
1096       ].getService(Ci.mozISandboxSettings);
1097       data.contentSandboxLevel = Services.prefs.getIntPref(
1098         "security.sandbox.content.level"
1099       );
1100       data.effectiveContentSandboxLevel =
1101         sandboxSettings.effectiveContentSandboxLevel;
1103       if (AppConstants.platform == "win") {
1104         data.contentWin32kLockdownState =
1105           sandboxSettings.contentWin32kLockdownStateString;
1107         data.supportSandboxGpuLevel = Services.prefs.getIntPref(
1108           "security.sandbox.gpu.level"
1109         );
1110       }
1111     }
1113     done(data);
1114   };
1117 if (AppConstants.ENABLE_WEBDRIVER) {
1118   dataProviders.remoteAgent = function remoteAgent(done) {
1119     const { RemoteAgent } = ChromeUtils.importESModule(
1120       "chrome://remote/content/components/RemoteAgent.sys.mjs"
1121     );
1122     const { running, scheme, host, port } = RemoteAgent;
1123     let url = "";
1124     if (running) {
1125       url = `${scheme}://${host}:${port}/`;
1126     }
1127     done({ running, url });
1128   };