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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
8 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
9 const { XPCOMUtils } = ChromeUtils.import(
10 "resource://gre/modules/XPCOMUtils.jsm"
13 XPCOMUtils.defineLazyServiceGetter(
16 "@mozilla.org/process/environment;1",
20 const { PREF_BOOL, PREF_INT, PREF_INVALID, PREF_STRING } = Ci.nsIPrefBranch;
22 this.EXPORTED_SYMBOLS = ["Branch", "MarionettePrefs"];
26 * @param {string=} branch
27 * Preference subtree. Uses root tree given `null`.
30 this._branch = Services.prefs.getBranch(branch);
34 * Gets value of `pref` in its known type.
36 * @param {string} pref
38 * @param {?=} fallback
39 * Fallback value to return if `pref` does not exist.
41 * @return {(string|boolean|number)}
42 * Value of `pref`, or the `fallback` value if `pref` does
46 * If `pref` is not a recognised preference and no `fallback`
47 * value has been provided.
49 get(pref, fallback = null) {
50 switch (this._branch.getPrefType(pref)) {
52 return this._branch.getStringPref(pref);
55 return this._branch.getBoolPref(pref);
58 return this._branch.getIntPref(pref);
62 if (fallback != null) {
65 throw new TypeError(`Unrecognised preference: ${pref}`);
70 * Sets the value of `pref`.
72 * @param {string} pref
74 * @param {(string|boolean|number)} value
78 * If `value` is not the correct type for `pref`.
82 if (typeof value != "undefined" && value != null) {
83 typ = value.constructor.name;
89 return this._branch.setStringPref(pref, value);
92 return this._branch.setBoolPref(pref, value);
95 return this._branch.setIntPref(pref, value);
98 throw new TypeError(`Illegal preference type value: ${typ}`);
104 * Provides shortcuts for lazily getting and setting typed Marionette
107 * Some of Marionette's preferences are stored using primitive values
108 * that internally are represented by complex types. One such example
109 * is `marionette.log.level` which stores a string such as `info` or
110 * `DEBUG`, and which is represented as `Log.Level`.
112 * Because we cannot trust the input of many of these preferences,
113 * this class provides abstraction that lets us safely deal with
114 * potentially malformed input. In the `marionette.log.level` example,
115 * `DEBUG`, `Debug`, and `dEbUg` are considered valid inputs and the
116 * `LogBranch` specialisation deserialises the string value to the
117 * correct `Log.Level` by sanitising the input data first.
119 * A further complication is that we cannot rely on `Preferences.jsm`
120 * in Marionette. See https://bugzilla.mozilla.org/show_bug.cgi?id=1357517
121 * for further details.
123 class MarionetteBranch extends Branch {
124 constructor(branch = "marionette.") {
129 * The `marionette.enabled` preference. When it returns true,
130 * this signifies that the Marionette server is running.
135 return this.get("enabled", false);
138 set enabled(isEnabled) {
139 this.set("enabled", isEnabled);
143 * The `marionette.debugging.clicktostart` preference delays
144 * server startup until a modal dialogue has been clicked to allow
145 * time for user to set breakpoints in the Browser Toolbox.
150 return this.get("debugging.clicktostart", false);
154 * Whether content scripts can be safely reused.
159 get contentListener() {
160 return this.get("contentListener", false);
163 set contentListener(value) {
164 this.set("contentListener", value);
168 * The `marionette.port` preference, detailing which port
169 * the TCP server should listen on.
174 return this.get("port", 2828);
178 this.set("port", newPort);
182 * Fail-safe return of the current log level from preference
183 * `marionette.log.level`.
185 * @return {Log.Level}
188 // TODO: when geckodriver's minimum supported Firefox version reaches 62,
189 // the lower-casing here can be dropped (https://bugzil.la/1482829)
190 switch (this.get("log.level", "info").toLowerCase()) {
192 return Log.Level.Fatal;
194 return Log.Level.Error;
196 return Log.Level.Warn;
198 return Log.Level.Config;
200 return Log.Level.Debug;
202 return Log.Level.Trace;
205 return Log.Level.Info;
210 * Certain log messages that are known to be long are truncated
211 * before they are dumped to stdout. The `marionette.log.truncate`
212 * preference indicates that the values should not be truncated.
217 return this.get("log.truncate");
221 * Gets the `marionette.prefs.recommended` preference, signifying
222 * whether recommended automation preferences will be set when
223 * Marionette is started.
227 get recommendedPrefs() {
228 return this.get("prefs.recommended", true);
232 /** Reads a JSON serialised blob stored in the environment. */
233 class EnvironmentPrefs {
235 * Reads the environment variable `key` and tries to parse it as
236 * JSON Object, then provides an iterator over its keys and values.
238 * If the environment variable is not set, this function returns empty.
240 * @param {string} key
241 * Environment variable.
243 * @return {Iterable.<string, (string|boolean|number)>
246 if (!env.exists(key)) {
252 prefs = JSON.parse(env.get(key));
254 throw new TypeError(`Unable to parse prefs from ${key}`, e);
257 for (let prefName of Object.keys(prefs)) {
258 yield [prefName, prefs[prefName]];
263 this.Branch = Branch;
264 this.EnvironmentPrefs = EnvironmentPrefs;
266 // There is a future potential of exposing this as Marionette.prefs.port
267 // if we introduce a Marionette.jsm module.
268 this.MarionettePrefs = new MarionetteBranch();