summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/tests/browser/browser_Troubleshoot.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/tests/browser/browser_Troubleshoot.js')
-rw-r--r--toolkit/modules/tests/browser/browser_Troubleshoot.js1078
1 files changed, 1078 insertions, 0 deletions
diff --git a/toolkit/modules/tests/browser/browser_Troubleshoot.js b/toolkit/modules/tests/browser/browser_Troubleshoot.js
new file mode 100644
index 0000000000..107b8faa98
--- /dev/null
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -0,0 +1,1078 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Ideally this would be an xpcshell test, but Troubleshoot relies on things
+// that aren't initialized outside of a XUL app environment like AddonManager
+// and the "@mozilla.org/xre/app-info;1" component.
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+const { Troubleshoot } = ChromeUtils.import(
+ "resource://gre/modules/Troubleshoot.jsm"
+);
+
+const { FeatureGate } = ChromeUtils.import(
+ "resource://featuregates/FeatureGate.jsm"
+);
+
+function test() {
+ waitForExplicitFinish();
+ function doNextTest() {
+ if (!tests.length) {
+ finish();
+ return;
+ }
+ tests.shift()(doNextTest);
+ }
+ doNextTest();
+}
+
+registerCleanupFunction(function() {
+ // Troubleshoot.jsm is imported into the global scope -- the window -- above.
+ // If it's not deleted, it outlives the test and is reported as a leak.
+ delete window.Troubleshoot;
+});
+
+var tests = [
+ function snapshotSchema(done) {
+ Troubleshoot.snapshot(function(snapshot) {
+ try {
+ validateObject(snapshot, SNAPSHOT_SCHEMA);
+ ok(true, "The snapshot should conform to the schema.");
+ } catch (err) {
+ ok(false, "Schema mismatch, " + err);
+ }
+ done();
+ });
+ },
+
+ async function experimentalFeatures(done) {
+ let featureGates = await FeatureGate.all();
+ ok(featureGates.length, "Should be at least one FeatureGate");
+
+ Troubleshoot.snapshot(snapshot => {
+ for (let i = 0; i < snapshot.experimentalFeatures.length; i++) {
+ let experimentalFeature = snapshot.experimentalFeatures[i];
+ is(
+ experimentalFeature[0],
+ featureGates[i].title,
+ "The first item in the array should be the title's l10n-id of the FeatureGate"
+ );
+ is(
+ experimentalFeature[1],
+ featureGates[i].preference,
+ "The second item in the array should be the preference name for the FeatureGate"
+ );
+ is(
+ experimentalFeature[2],
+ Services.prefs.getBoolPref(featureGates[i].preference),
+ "The third item in the array should be the preference value of the FeatureGate"
+ );
+ }
+ done();
+ });
+ },
+
+ function modifiedPreferences(done) {
+ let prefs = [
+ "javascript.troubleshoot",
+ "troubleshoot.foo",
+ "network.proxy.troubleshoot",
+ "print.print_to_filename",
+ ];
+ prefs.forEach(function(p) {
+ Services.prefs.setBoolPref(p, true);
+ is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p);
+ });
+ Troubleshoot.snapshot(function(snapshot) {
+ let p = snapshot.modifiedPreferences;
+ is(
+ p["javascript.troubleshoot"],
+ true,
+ "The pref should be present because it's whitelisted " +
+ "but not blacklisted."
+ );
+ ok(
+ !("troubleshoot.foo" in p),
+ "The pref should be absent because it's not in the whitelist."
+ );
+ ok(
+ !("network.proxy.troubleshoot" in p),
+ "The pref should be absent because it's blacklisted."
+ );
+ ok(
+ !("print.print_to_filename" in p),
+ "The pref should be absent because it's not whitelisted."
+ );
+ prefs.forEach(p => Services.prefs.deleteBranch(p));
+ done();
+ });
+ },
+
+ function unicodePreferences(done) {
+ let name = "font.name.sans-serif.x-western";
+ let utf8Value = "\xc4\x8capk\xc5\xafv Krasopis";
+ let unicodeValue = "\u010Capk\u016Fv Krasopis";
+
+ // set/getCharPref work with 8bit strings (utf8)
+ Services.prefs.setCharPref(name, utf8Value);
+
+ Troubleshoot.snapshot(function(snapshot) {
+ let p = snapshot.modifiedPreferences;
+ is(p[name], unicodeValue, "The pref should have correct Unicode value.");
+ Services.prefs.deleteBranch(name);
+ done();
+ });
+ },
+
+ function printingPreferences(done) {
+ let prefs = ["javascript.print_to_filename", "print.print_to_filename"];
+ prefs.forEach(function(p) {
+ Services.prefs.setBoolPref(p, true);
+ is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p);
+ });
+ Troubleshoot.snapshot(function(snapshot) {
+ let p = snapshot.printingPreferences;
+ is(p["print.print_to_filename"], true, "The pref should be present");
+ ok(
+ !("javascript.print_to_filename" in p),
+ "The pref should be absent because it's not a print pref."
+ );
+ prefs.forEach(p => Services.prefs.deleteBranch(p));
+ done();
+ });
+ },
+];
+
+// This is inspired by JSON Schema, or by the example on its Wikipedia page
+// anyway.
+const SNAPSHOT_SCHEMA = {
+ type: "object",
+ required: true,
+ properties: {
+ application: {
+ required: true,
+ type: "object",
+ properties: {
+ name: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ buildID: {
+ required: true,
+ type: "string",
+ },
+ distributionID: {
+ required: true,
+ type: "string",
+ },
+ userAgent: {
+ required: true,
+ type: "string",
+ },
+ osVersion: {
+ required: true,
+ type: "string",
+ },
+ rosetta: {
+ required: false,
+ type: "boolean",
+ },
+ vendor: {
+ type: "string",
+ },
+ updateChannel: {
+ type: "string",
+ },
+ supportURL: {
+ type: "string",
+ },
+ launcherProcessState: {
+ type: "number",
+ },
+ remoteAutoStart: {
+ type: "boolean",
+ required: true,
+ },
+ fissionAutoStart: {
+ type: "boolean",
+ },
+ fissionDecisionStatus: {
+ type: "string",
+ },
+ numTotalWindows: {
+ type: "number",
+ },
+ numFissionWindows: {
+ type: "number",
+ },
+ numRemoteWindows: {
+ type: "number",
+ },
+ policiesStatus: {
+ type: "number",
+ },
+ keyLocationServiceGoogleFound: {
+ type: "boolean",
+ },
+ keySafebrowsingGoogleFound: {
+ type: "boolean",
+ },
+ keyMozillaFound: {
+ type: "boolean",
+ },
+ safeMode: {
+ type: "boolean",
+ },
+ },
+ },
+ crashes: {
+ required: false,
+ type: "object",
+ properties: {
+ pending: {
+ required: true,
+ type: "number",
+ },
+ submitted: {
+ required: true,
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ id: {
+ required: true,
+ type: "string",
+ },
+ date: {
+ required: true,
+ type: "number",
+ },
+ pending: {
+ required: true,
+ type: "boolean",
+ },
+ },
+ },
+ },
+ },
+ },
+ addons: {
+ required: true,
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: {
+ required: true,
+ type: "string",
+ },
+ type: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ id: {
+ required: true,
+ type: "string",
+ },
+ isActive: {
+ required: true,
+ type: "boolean",
+ },
+ },
+ },
+ },
+ securitySoftware: {
+ required: false,
+ type: "object",
+ properties: {
+ registeredAntiVirus: {
+ required: true,
+ type: "string",
+ },
+ registeredAntiSpyware: {
+ required: true,
+ type: "string",
+ },
+ registeredFirewall: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ features: {
+ required: true,
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ id: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ },
+ processes: {
+ required: true,
+ type: "object",
+ properties: {
+ maxWebContentProcesses: {
+ required: true,
+ type: "number",
+ },
+ remoteTypes: {
+ required: true,
+ type: "object",
+ },
+ },
+ },
+ experimentalFeatures: {
+ required: true,
+ type: "array",
+ },
+ environmentVariables: {
+ required: true,
+ type: "object",
+ },
+ modifiedPreferences: {
+ required: true,
+ type: "object",
+ },
+ printingPreferences: {
+ required: true,
+ type: "object",
+ },
+ lockedPreferences: {
+ required: true,
+ type: "object",
+ properties: {
+ "fission.autostart": {
+ required: false,
+ type: "boolean",
+ },
+ "fission.autostart.session": {
+ required: false,
+ type: "boolean",
+ },
+ },
+ },
+ graphics: {
+ required: true,
+ type: "object",
+ properties: {
+ numTotalWindows: {
+ required: true,
+ type: "number",
+ },
+ numAcceleratedWindows: {
+ required: true,
+ type: "number",
+ },
+ windowLayerManagerType: {
+ type: "string",
+ },
+ windowLayerManagerRemote: {
+ type: "boolean",
+ },
+ windowUsingAdvancedLayers: {
+ type: "boolean",
+ },
+ numAcceleratedWindowsMessage: {
+ type: "object",
+ properties: {
+ key: {
+ required: true,
+ type: "string",
+ },
+ args: {
+ required: false,
+ type: "object",
+ },
+ },
+ },
+ adapterDescription: {
+ type: "string",
+ },
+ adapterVendorID: {
+ type: "string",
+ },
+ adapterDeviceID: {
+ type: "string",
+ },
+ adapterSubsysID: {
+ type: "string",
+ },
+ adapterRAM: {
+ type: "number",
+ },
+ adapterDrivers: {
+ type: "string",
+ },
+ driverVendor: {
+ type: "string",
+ },
+ driverVersion: {
+ type: "string",
+ },
+ driverDate: {
+ type: "string",
+ },
+ adapterDescription2: {
+ type: "string",
+ },
+ adapterVendorID2: {
+ type: "string",
+ },
+ adapterDeviceID2: {
+ type: "string",
+ },
+ adapterSubsysID2: {
+ type: "string",
+ },
+ adapterRAM2: {
+ type: "number",
+ },
+ adapterDrivers2: {
+ type: "string",
+ },
+ driverVendor2: {
+ type: "string",
+ },
+ driverVersion2: {
+ type: "string",
+ },
+ driverDate2: {
+ type: "string",
+ },
+ isGPU2Active: {
+ type: "boolean",
+ },
+ direct2DEnabled: {
+ type: "boolean",
+ },
+ directWriteEnabled: {
+ type: "boolean",
+ },
+ directWriteVersion: {
+ type: "string",
+ },
+ usesTiling: {
+ type: "boolean",
+ },
+ contentUsesTiling: {
+ type: "boolean",
+ },
+ offMainThreadPaintEnabled: {
+ type: "boolean",
+ },
+ offMainThreadPaintWorkerCount: {
+ type: "number",
+ },
+ clearTypeParameters: {
+ type: "string",
+ },
+ webgl1Renderer: {
+ type: "string",
+ },
+ webgl1Version: {
+ type: "string",
+ },
+ webgl1DriverExtensions: {
+ type: "string",
+ },
+ webgl1Extensions: {
+ type: "string",
+ },
+ webgl1WSIInfo: {
+ type: "string",
+ },
+ webgl2Renderer: {
+ type: "string",
+ },
+ webgl2Version: {
+ type: "string",
+ },
+ webgl2DriverExtensions: {
+ type: "string",
+ },
+ webgl2Extensions: {
+ type: "string",
+ },
+ webgl2WSIInfo: {
+ type: "string",
+ },
+ info: {
+ type: "object",
+ },
+ failures: {
+ type: "object",
+ properties: {
+ key: {
+ required: true,
+ type: "string",
+ },
+ args: {
+ required: false,
+ type: "object",
+ },
+ },
+ },
+ indices: {
+ type: "array",
+ items: {
+ type: "number",
+ },
+ },
+ featureLog: {
+ type: "object",
+ },
+ crashGuards: {
+ type: "array",
+ },
+ direct2DEnabledMessage: {
+ type: "object",
+ properties: {
+ key: {
+ required: true,
+ type: "string",
+ },
+ args: {
+ required: false,
+ type: "object",
+ },
+ },
+ },
+ targetFrameRate: {
+ type: "number",
+ },
+ windowProtocol: {
+ type: "string",
+ },
+ desktopEnvironment: {
+ type: "string",
+ },
+ },
+ },
+ media: {
+ required: true,
+ type: "object",
+ properties: {
+ currentAudioBackend: {
+ required: true,
+ type: "string",
+ },
+ currentMaxAudioChannels: {
+ required: true,
+ type: "number",
+ },
+ currentPreferredSampleRate: {
+ required: true,
+ type: "number",
+ },
+ audioOutputDevices: {
+ required: true,
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: {
+ required: true,
+ type: "string",
+ },
+ groupId: {
+ required: true,
+ type: "string",
+ },
+ vendor: {
+ required: true,
+ type: "string",
+ },
+ type: {
+ required: true,
+ type: "number",
+ },
+ state: {
+ required: true,
+ type: "number",
+ },
+ preferred: {
+ required: true,
+ type: "number",
+ },
+ supportedFormat: {
+ required: true,
+ type: "number",
+ },
+ defaultFormat: {
+ required: true,
+ type: "number",
+ },
+ maxChannels: {
+ required: true,
+ type: "number",
+ },
+ defaultRate: {
+ required: true,
+ type: "number",
+ },
+ maxRate: {
+ required: true,
+ type: "number",
+ },
+ minRate: {
+ required: true,
+ type: "number",
+ },
+ maxLatency: {
+ required: true,
+ type: "number",
+ },
+ minLatency: {
+ required: true,
+ type: "number",
+ },
+ },
+ },
+ },
+ audioInputDevices: {
+ required: true,
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: {
+ required: true,
+ type: "string",
+ },
+ groupId: {
+ required: true,
+ type: "string",
+ },
+ vendor: {
+ required: true,
+ type: "string",
+ },
+ type: {
+ required: true,
+ type: "number",
+ },
+ state: {
+ required: true,
+ type: "number",
+ },
+ preferred: {
+ required: true,
+ type: "number",
+ },
+ supportedFormat: {
+ required: true,
+ type: "number",
+ },
+ defaultFormat: {
+ required: true,
+ type: "number",
+ },
+ maxChannels: {
+ required: true,
+ type: "number",
+ },
+ defaultRate: {
+ required: true,
+ type: "number",
+ },
+ maxRate: {
+ required: true,
+ type: "number",
+ },
+ minRate: {
+ required: true,
+ type: "number",
+ },
+ maxLatency: {
+ required: true,
+ type: "number",
+ },
+ minLatency: {
+ required: true,
+ type: "number",
+ },
+ },
+ },
+ },
+ },
+ },
+ accessibility: {
+ required: true,
+ type: "object",
+ properties: {
+ isActive: {
+ required: true,
+ type: "boolean",
+ },
+ forceDisabled: {
+ type: "number",
+ },
+ handlerUsed: {
+ type: "boolean",
+ },
+ instantiator: {
+ type: "string",
+ },
+ },
+ },
+ libraryVersions: {
+ required: true,
+ type: "object",
+ properties: {
+ NSPR: {
+ required: true,
+ type: "object",
+ properties: {
+ minVersion: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ NSS: {
+ required: true,
+ type: "object",
+ properties: {
+ minVersion: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ NSSUTIL: {
+ required: true,
+ type: "object",
+ properties: {
+ minVersion: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ NSSSSL: {
+ required: true,
+ type: "object",
+ properties: {
+ minVersion: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ NSSSMIME: {
+ required: true,
+ type: "object",
+ properties: {
+ minVersion: {
+ required: true,
+ type: "string",
+ },
+ version: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ },
+ },
+ userJS: {
+ required: true,
+ type: "object",
+ properties: {
+ exists: {
+ required: true,
+ type: "boolean",
+ },
+ },
+ },
+ sandbox: {
+ required: false,
+ type: "object",
+ properties: {
+ hasSeccompBPF: {
+ required: AppConstants.platform == "linux",
+ type: "boolean",
+ },
+ hasSeccompTSync: {
+ required: AppConstants.platform == "linux",
+ type: "boolean",
+ },
+ hasUserNamespaces: {
+ required: AppConstants.platform == "linux",
+ type: "boolean",
+ },
+ hasPrivilegedUserNamespaces: {
+ required: AppConstants.platform == "linux",
+ type: "boolean",
+ },
+ canSandboxContent: {
+ required: false,
+ type: "boolean",
+ },
+ canSandboxMedia: {
+ required: false,
+ type: "boolean",
+ },
+ contentSandboxLevel: {
+ required: AppConstants.MOZ_SANDBOX,
+ type: "number",
+ },
+ effectiveContentSandboxLevel: {
+ required: AppConstants.MOZ_SANDBOX,
+ type: "number",
+ },
+ syscallLog: {
+ required: AppConstants.platform == "linux",
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ index: {
+ required: true,
+ type: "number",
+ },
+ pid: {
+ required: true,
+ type: "number",
+ },
+ tid: {
+ required: true,
+ type: "number",
+ },
+ procType: {
+ required: true,
+ type: "string",
+ },
+ syscall: {
+ required: true,
+ type: "number",
+ },
+ args: {
+ required: true,
+ type: "array",
+ items: {
+ type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ startupCache: {
+ required: false,
+ type: "object",
+ properties: {
+ DiskCachePath: {
+ required: true,
+ type: "string",
+ },
+ IgnoreDiskCache: {
+ required: true,
+ type: "boolean",
+ },
+ FoundDiskCacheOnInit: {
+ required: true,
+ type: "boolean",
+ },
+ WroteToDiskCache: {
+ required: true,
+ type: "boolean",
+ },
+ },
+ },
+ intl: {
+ required: true,
+ type: "object",
+ properties: {
+ localeService: {
+ required: true,
+ type: "object",
+ properties: {
+ requested: {
+ required: true,
+ type: "array",
+ },
+ available: {
+ required: true,
+ type: "array",
+ },
+ supported: {
+ required: true,
+ type: "array",
+ },
+ regionalPrefs: {
+ required: true,
+ type: "array",
+ },
+ defaultLocale: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ osPrefs: {
+ required: true,
+ type: "object",
+ properties: {
+ systemLocales: {
+ required: true,
+ type: "array",
+ },
+ regionalPrefsLocales: {
+ required: true,
+ type: "array",
+ },
+ },
+ },
+ },
+ },
+ remoteAgent: {
+ type: "object",
+ properties: {
+ listening: {
+ required: true,
+ type: "boolean",
+ },
+ url: {
+ required: true,
+ type: "string",
+ },
+ },
+ },
+ thirdPartyModules: {
+ required:
+ AppConstants.platform == "win" &&
+ Services.prefs.getBoolPref("browser.enableAboutThirdParty"),
+ type: "array",
+ },
+ },
+};
+
+/**
+ * Throws an Error if obj doesn't conform to schema. That way you get a nice
+ * error message and a stack to help you figure out what went wrong, which you
+ * wouldn't get if this just returned true or false instead. There's still
+ * room for improvement in communicating validation failures, however.
+ *
+ * @param obj The object to validate.
+ * @param schema The schema that obj should conform to.
+ */
+function validateObject(obj, schema) {
+ if (obj === undefined && !schema.required) {
+ return;
+ }
+ if (typeof schema.type != "string") {
+ throw schemaErr("'type' must be a string", schema);
+ }
+ if (objType(obj) != schema.type) {
+ throw validationErr("Object is not of the expected type", obj, schema);
+ }
+ let validatorFnName = "validateObject_" + schema.type;
+ if (!(validatorFnName in this)) {
+ throw schemaErr("Validator function not defined for type", schema);
+ }
+ this[validatorFnName](obj, schema);
+}
+
+function validateObject_object(obj, schema) {
+ if (typeof schema.properties != "object") {
+ // Don't care what obj's properties are.
+ return;
+ }
+ // First check that all the schema's properties match the object.
+ for (let prop in schema.properties) {
+ validateObject(obj[prop], schema.properties[prop]);
+ }
+ // Now check that the object doesn't have any properties not in the schema.
+ for (let prop in obj) {
+ if (!(prop in schema.properties)) {
+ throw validationErr(
+ "Object has property " + prop + " not in schema",
+ obj,
+ schema
+ );
+ }
+ }
+}
+
+function validateObject_array(array, schema) {
+ if (typeof schema.items != "object") {
+ // Don't care what the array's elements are.
+ return;
+ }
+ array.forEach(elt => validateObject(elt, schema.items));
+}
+
+function validateObject_string(str, schema) {}
+function validateObject_boolean(bool, schema) {}
+function validateObject_number(num, schema) {}
+
+function validationErr(msg, obj, schema) {
+ return new Error(
+ "Validation error: " +
+ msg +
+ ": object=" +
+ JSON.stringify(obj) +
+ ", schema=" +
+ JSON.stringify(schema)
+ );
+}
+
+function schemaErr(msg, schema) {
+ return new Error("Schema error: " + msg + ": " + JSON.stringify(schema));
+}
+
+function objType(obj) {
+ let type = typeof obj;
+ if (type != "object") {
+ return type;
+ }
+ if (Array.isArray(obj)) {
+ return "array";
+ }
+ if (obj === null) {
+ return "null";
+ }
+ return type;
+}