summaryrefslogtreecommitdiffstats
path: root/testing/marionette/prefs.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/marionette/prefs.js')
-rw-r--r--testing/marionette/prefs.js280
1 files changed, 280 insertions, 0 deletions
diff --git a/testing/marionette/prefs.js b/testing/marionette/prefs.js
new file mode 100644
index 0000000000..937ec647d1
--- /dev/null
+++ b/testing/marionette/prefs.js
@@ -0,0 +1,280 @@
+/* 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/. */
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["Branch", "MarionettePrefs"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ Log: "resource://gre/modules/Log.jsm",
+});
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "env",
+ "@mozilla.org/process/environment;1",
+ "nsIEnvironment"
+);
+
+const { PREF_BOOL, PREF_INT, PREF_INVALID, PREF_STRING } = Ci.nsIPrefBranch;
+
+class Branch {
+ /**
+ * @param {string=} branch
+ * Preference subtree. Uses root tree given `null`.
+ */
+ constructor(branch) {
+ this._branch = Services.prefs.getBranch(branch);
+ }
+
+ /**
+ * Gets value of `pref` in its known type.
+ *
+ * @param {string} pref
+ * Preference name.
+ * @param {?=} fallback
+ * Fallback value to return if `pref` does not exist.
+ *
+ * @return {(string|boolean|number)}
+ * Value of `pref`, or the `fallback` value if `pref` does
+ * not exist.
+ *
+ * @throws {TypeError}
+ * If `pref` is not a recognised preference and no `fallback`
+ * value has been provided.
+ */
+ get(pref, fallback = null) {
+ switch (this._branch.getPrefType(pref)) {
+ case PREF_STRING:
+ return this._branch.getStringPref(pref);
+
+ case PREF_BOOL:
+ return this._branch.getBoolPref(pref);
+
+ case PREF_INT:
+ return this._branch.getIntPref(pref);
+
+ case PREF_INVALID:
+ default:
+ if (fallback != null) {
+ return fallback;
+ }
+ throw new TypeError(`Unrecognised preference: ${pref}`);
+ }
+ }
+
+ /**
+ * Sets the value of `pref`.
+ *
+ * @param {string} pref
+ * Preference name.
+ * @param {(string|boolean|number)} value
+ * `pref`'s new value.
+ *
+ * @throws {TypeError}
+ * If `value` is not the correct type for `pref`.
+ */
+ set(pref, value) {
+ let typ;
+ if (typeof value != "undefined" && value != null) {
+ typ = value.constructor.name;
+ }
+
+ switch (typ) {
+ case "String":
+ // Unicode compliant
+ return this._branch.setStringPref(pref, value);
+
+ case "Boolean":
+ return this._branch.setBoolPref(pref, value);
+
+ case "Number":
+ return this._branch.setIntPref(pref, value);
+
+ default:
+ throw new TypeError(`Illegal preference type value: ${typ}`);
+ }
+ }
+}
+
+/**
+ * Provides shortcuts for lazily getting and setting typed Marionette
+ * preferences.
+ *
+ * Some of Marionette's preferences are stored using primitive values
+ * that internally are represented by complex types. One such example
+ * is `marionette.log.level` which stores a string such as `info` or
+ * `DEBUG`, and which is represented as `Log.Level`.
+ *
+ * Because we cannot trust the input of many of these preferences,
+ * this class provides abstraction that lets us safely deal with
+ * potentially malformed input. In the `marionette.log.level` example,
+ * `DEBUG`, `Debug`, and `dEbUg` are considered valid inputs and the
+ * `LogBranch` specialisation deserialises the string value to the
+ * correct `Log.Level` by sanitising the input data first.
+ *
+ * A further complication is that we cannot rely on `Preferences.jsm`
+ * in Marionette. See https://bugzilla.mozilla.org/show_bug.cgi?id=1357517
+ * for further details.
+ */
+class MarionetteBranch extends Branch {
+ constructor(branch = "marionette.") {
+ super(branch);
+ }
+
+ /**
+ * The `marionette.enabled` preference. When it returns true,
+ * this signifies that the Marionette server is running.
+ *
+ * @return {boolean}
+ */
+ get enabled() {
+ return this.get("enabled", false);
+ }
+
+ set enabled(isEnabled) {
+ this.set("enabled", isEnabled);
+ }
+
+ /**
+ * The `marionette.debugging.clicktostart` preference delays
+ * server startup until a modal dialogue has been clicked to allow
+ * time for user to set breakpoints in the Browser Toolbox.
+ *
+ * @return {boolean}
+ */
+ get clickToStart() {
+ return this.get("debugging.clicktostart", false);
+ }
+
+ /**
+ * Whether content scripts can be safely reused.
+ *
+ * @deprecated
+ * @return {boolean}
+ */
+ get contentListener() {
+ return this.get("contentListener", false);
+ }
+
+ set contentListener(value) {
+ this.set("contentListener", value);
+ }
+
+ /**
+ * The `marionette.port` preference, detailing which port
+ * the TCP server should listen on.
+ *
+ * @return {number}
+ */
+ get port() {
+ return this.get("port", 2828);
+ }
+
+ set port(newPort) {
+ this.set("port", newPort);
+ }
+
+ /**
+ * Fail-safe return of the current log level from preference
+ * `marionette.log.level`.
+ *
+ * @return {Log.Level}
+ */
+ get logLevel() {
+ // TODO: when geckodriver's minimum supported Firefox version reaches 62,
+ // the lower-casing here can be dropped (https://bugzil.la/1482829)
+ switch (this.get("log.level", "info").toLowerCase()) {
+ case "fatal":
+ return Log.Level.Fatal;
+ case "error":
+ return Log.Level.Error;
+ case "warn":
+ return Log.Level.Warn;
+ case "config":
+ return Log.Level.Config;
+ case "debug":
+ return Log.Level.Debug;
+ case "trace":
+ return Log.Level.Trace;
+ case "info":
+ default:
+ dump(`*** log: ${Log}\n\n`);
+ return Log.Level.Info;
+ }
+ }
+
+ /**
+ * Certain log messages that are known to be long are truncated
+ * before they are dumped to stdout. The `marionette.log.truncate`
+ * preference indicates that the values should not be truncated.
+ *
+ * @return {boolean}
+ */
+ get truncateLog() {
+ return this.get("log.truncate");
+ }
+
+ /**
+ * Gets the `marionette.prefs.recommended` preference, signifying
+ * whether recommended automation preferences will be set when
+ * Marionette is started.
+ *
+ * @return {boolean}
+ */
+ get recommendedPrefs() {
+ return this.get("prefs.recommended", true);
+ }
+
+ /**
+ * Temporary preference to enable the usage of the JSWindowActor
+ * implementation for commands that already support Fission.
+ */
+ get useActors() {
+ return this.get("actors.enabled", true);
+ }
+}
+
+/** Reads a JSON serialised blob stored in the environment. */
+class EnvironmentPrefs {
+ /**
+ * Reads the environment variable `key` and tries to parse it as
+ * JSON Object, then provides an iterator over its keys and values.
+ *
+ * If the environment variable is not set, this function returns empty.
+ *
+ * @param {string} key
+ * Environment variable.
+ *
+ * @return {Iterable.<string, (string|boolean|number)>
+ */
+ static *from(key) {
+ if (!env.exists(key)) {
+ return;
+ }
+
+ let prefs;
+ try {
+ prefs = JSON.parse(env.get(key));
+ } catch (e) {
+ throw new TypeError(`Unable to parse prefs from ${key}`, e);
+ }
+
+ for (let prefName of Object.keys(prefs)) {
+ yield [prefName, prefs[prefName]];
+ }
+ }
+}
+
+this.Branch = Branch;
+this.EnvironmentPrefs = EnvironmentPrefs;
+
+// There is a future potential of exposing this as Marionette.prefs.port
+// if we introduce a Marionette.jsm module.
+this.MarionettePrefs = new MarionetteBranch();