h.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.
 *
 * Because we cannot trust the input of many of these preferences,
 * this class provides abstraction that lets us safely deal with
 * potentially malformed input.
 *
 * A further complication is that we cannot rely on `Preferences.sys.mjs`
 * 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.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.
   *
   * @returns {boolean}
   */
  get clickToStart() {
    return this.get("debugging.clicktostart", false);
  }

  /**
   * The `marionette.navigate-after-click.enabled` preference enables the
   * fallback logic for Selenium clients to wait for a potential navigation
   * after clicking an element via "WebDriver:ElementClick".
   *
   * @returns {boolean}
   */
  get navigateAfterClickEnabled() {
    return this.get("marionette.navigate-after-click.enabled", true);
  }

  /**
   * The `marionette.navigate-after-click.timeout` preference defines the
   * amount of milliseconds to wait for a potential navigation after a click
   * event was dispatches via "WebDriver:ElementClick".
   *
   * @returns {number}
   */
  get navigateAfterClickTimeout() {
    return this.get("marionette.navigate-after-click.timeout", 50);
  }

  /**
   * The `marionette.port` preference, detailing which port
   * the TCP server should listen on.
   *
   * @returns {number}
   */
  get port() {
    return this.get("port", 2828);
  }

  set port(newPort) {
    this.set("port", newPort);
  }
}

/** Reads a JSON serialised blob stored in the environment. */
export 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.
   *
   * @returns {Iterable.<string, (string|boolean|number)>}
   */
  static *from(key) {
    if (!Services.env.exists(key)) {
      return;
    }

    let prefs;
    try {
      prefs = JSON.parse(Services.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]];
    }
  }
}

// There is a future potential of exposing this as Marionette.prefs.port
// if we introduce a Marionette.sys.mjs module.
export const MarionettePrefs = new MarionetteBranch();
PK