tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UserPromptHandler.sys.mjs (8497B)


      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/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
      9  pprint: "chrome://remote/content/shared/Format.sys.mjs",
     10 });
     11 
     12 /**
     13 * @typedef {string} PromptHandlers
     14 */
     15 
     16 /**
     17 * Enum of known prompt handlers.
     18 *
     19 * @readonly
     20 * @enum {PromptHandlers}
     21 *
     22 * @see https://w3c.github.io/webdriver/#dfn-known-prompt-handlers
     23 */
     24 export const PromptHandlers = {
     25  /** All simple dialogs encountered should be accepted. */
     26  Accept: "accept",
     27  /**
     28   * All simple dialogs encountered should be accepted, and an error
     29   * returned that the dialog was handled.
     30   */
     31  AcceptAndNotify: "accept and notify",
     32  /** All simple dialogs encountered should be dismissed. */
     33  Dismiss: "dismiss",
     34  /**
     35   * All simple dialogs encountered should be dismissed, and an error
     36   * returned that the dialog was handled.
     37   */
     38  DismissAndNotify: "dismiss and notify",
     39  /** All simple dialogs encountered should be left to the user to handle. */
     40  Ignore: "ignore",
     41 };
     42 
     43 /**
     44 * @typedef {string} PromptTypes
     45 */
     46 
     47 /**
     48 * Enum of valid prompt types.
     49 *
     50 * @readonly
     51 * @enum {PromptTypes}
     52 *
     53 * @see https://w3c.github.io/webdriver/#dfn-valid-prompt-types
     54 */
     55 export const PromptTypes = {
     56  // A simple alert dialog
     57  Alert: "alert",
     58  // A simple beforeunload dialog. If no handler is set it falls back to the
     59  // `accept` handler to keep backward compatibility with WebDriver classic,
     60  // which doesn't customize this prompt type.
     61  BeforeUnload: "beforeUnload",
     62  // A simple confirm dialog
     63  Confirm: "confirm",
     64  // Default value when no specific handler is configured. Can only be set when
     65  // specifying the unhandlePromptBehavior capability with a map containing a
     66  // "default" entry. See FALLBACK_DEFAULT_PROMPT_TYPE.
     67  Default: "default",
     68  // A file picker dialog
     69  File: "file",
     70  // A simple prompt dialog
     71  Prompt: "prompt",
     72 };
     73 
     74 /**
     75 * Internal prompt type used when the unhandledPromptBehavior capability is
     76 * set as a string. The "fallbackDefault" type will apply to "alert", "confirm"
     77 * and "prompt" prompts, but will not apply to "beforeUnload" prompts.
     78 */
     79 const FALLBACK_DEFAULT_PROMPT_TYPE = "fallbackDefault";
     80 
     81 export class PromptHandlerConfiguration {
     82  #handler;
     83  #notify;
     84 
     85  /**
     86   * Create an instance of a prompt handler configuration.
     87   *
     88   * @param {string} handler
     89   *     Handler to use for the user prompt. One of "accept", "dismiss" or "ignore".
     90   * @param {boolean} notify
     91   *     Flag to indicate if the user needs to be notified when the dialog was
     92   *     handled.
     93   *
     94   * @see https://w3c.github.io/webdriver/#dfn-prompt-handler-configuration
     95   */
     96  constructor(handler, notify) {
     97    this.#handler = handler;
     98    this.#notify = notify;
     99  }
    100 
    101  get handler() {
    102    return this.#handler;
    103  }
    104 
    105  get notify() {
    106    return this.#notify;
    107  }
    108 
    109  toString() {
    110    return "[object PromptHandlerConfiguration]";
    111  }
    112 
    113  /**
    114   * JSON serialization of the prompt handler configuration object.
    115   *
    116   * @returns {Record<string, ?>} json
    117   *
    118   * @see https://w3c.github.io/webdriver/#dfn-serialize-a-prompt-handler-configuration
    119   */
    120  toJSON() {
    121    let serialized = this.#handler;
    122    if (["accept", "dismiss"].includes(serialized) && this.#notify) {
    123      serialized += " and notify";
    124    }
    125 
    126    return serialized;
    127  }
    128 }
    129 
    130 export class UserPromptHandler {
    131  #promptTypeToHandlerMap;
    132 
    133  constructor() {
    134    // Note: this map is null until update-the-user-prompt-handler initializes
    135    // it.
    136    this.#promptTypeToHandlerMap = null;
    137  }
    138 
    139  get activePromptHandlers() {
    140    return this.#promptTypeToHandlerMap;
    141  }
    142 
    143  get PromptHandlers() {
    144    return PromptHandlers;
    145  }
    146 
    147  get PromptTypes() {
    148    return PromptTypes;
    149  }
    150 
    151  /**
    152   * Unmarshal a JSON object representation of the unhandledPromptBehavior capability.
    153   *
    154   * @param {Record<string, ?>} json
    155   *     JSON Object to unmarshal.
    156   *
    157   * @throws {InvalidArgumentError}
    158   *     When the value of the unhandledPromptBehavior capability is invalid.
    159   *
    160   * @returns {UserPromptHandler}
    161   *
    162   * @see https://w3c.github.io/webdriver/#dfn-deserialize-as-an-unhandled-prompt-behavior
    163   */
    164  static fromJSON(json) {
    165    let isStringValue = false;
    166    let value = json;
    167    if (typeof value === "string") {
    168      // A single specified prompt behavior or for WebDriver classic.
    169      value = { [FALLBACK_DEFAULT_PROMPT_TYPE]: value };
    170      isStringValue = true;
    171    }
    172 
    173    lazy.assert.object(
    174      value,
    175      lazy.pprint`Expected "unhandledPromptBehavior" to be an object, got ${value}`
    176    );
    177 
    178    const userPromptHandlerCapability = new Map();
    179    for (let [promptType, handler] of Object.entries(value)) {
    180      if (!isStringValue) {
    181        const validPromptTypes = Object.values(PromptTypes);
    182        lazy.assert.in(
    183          promptType,
    184          validPromptTypes,
    185          lazy.pprint`Expected "promptType" to be one of ${validPromptTypes}, got ${promptType}`
    186        );
    187      }
    188 
    189      const knownPromptHandlers = Object.values(PromptHandlers);
    190      lazy.assert.in(
    191        handler,
    192        knownPromptHandlers,
    193        lazy.pprint`Expected "handler" to be one of ${knownPromptHandlers}, got ${handler}`
    194      );
    195 
    196      let notify = false;
    197      switch (handler) {
    198        case PromptHandlers.AcceptAndNotify:
    199          handler = PromptHandlers.Accept;
    200          notify = true;
    201          break;
    202        case PromptHandlers.DismissAndNotify:
    203          handler = PromptHandlers.Dismiss;
    204          notify = true;
    205          break;
    206        case PromptHandlers.Ignore:
    207          notify = true;
    208          break;
    209      }
    210 
    211      const configuration = new PromptHandlerConfiguration(handler, notify);
    212      userPromptHandlerCapability.set(promptType, configuration);
    213    }
    214    const userPromptHandler = new UserPromptHandler();
    215    userPromptHandler.update(userPromptHandlerCapability);
    216    return userPromptHandler;
    217  }
    218 
    219  /**
    220   * Get the handler for the given prompt type.
    221   *
    222   * @param {string} promptType
    223   *     The prompt type to retrieve the handler for.
    224   *
    225   * @returns {PromptHandlerConfiguration}
    226   *
    227   * @see https://w3c.github.io/webdriver/#dfn-get-the-prompt-handler
    228   */
    229  getPromptHandler(promptType) {
    230    let handlers;
    231 
    232    if (this.#promptTypeToHandlerMap === null) {
    233      handlers = new Map();
    234    } else {
    235      handlers = this.#promptTypeToHandlerMap;
    236    }
    237 
    238    if (handlers.has(promptType)) {
    239      return handlers.get(promptType);
    240    }
    241 
    242    if (handlers.has(PromptTypes.Default)) {
    243      return handlers.get(PromptTypes.Default);
    244    }
    245 
    246    if (promptType === PromptTypes.BeforeUnload) {
    247      return new PromptHandlerConfiguration(PromptHandlers.Accept, false);
    248    }
    249 
    250    if (handlers.has(FALLBACK_DEFAULT_PROMPT_TYPE)) {
    251      return handlers.get(FALLBACK_DEFAULT_PROMPT_TYPE);
    252    }
    253 
    254    return new PromptHandlerConfiguration(PromptHandlers.Dismiss, true);
    255  }
    256 
    257  /**
    258   * Updates the prompt handler configuration for a given requested prompt
    259   * handler map.
    260   *
    261   * @param {Map} requestedPromptHandler
    262   *     The request prompt handler configuration map.
    263   *
    264   * @see https://w3c.github.io/webdriver/#dfn-update-the-user-prompt-handler
    265   */
    266  update(requestedPromptHandler) {
    267    if (this.#promptTypeToHandlerMap === null) {
    268      this.#promptTypeToHandlerMap = new Map();
    269    }
    270 
    271    for (const [promptType, handler] of requestedPromptHandler) {
    272      this.#promptTypeToHandlerMap.set(promptType, handler);
    273    }
    274  }
    275 
    276  /**
    277   * JSON serialization of the user prompt handler object.
    278   *
    279   * @returns {Record<string, ?>} json
    280   *
    281   * @see https://w3c.github.io/webdriver/#dfn-serialize-the-user-prompt-handler
    282   */
    283  toJSON() {
    284    if (this.#promptTypeToHandlerMap === null) {
    285      // Fallback to "dismiss and notify" if no handler is set
    286      return PromptHandlers.DismissAndNotify;
    287    }
    288 
    289    if (
    290      this.#promptTypeToHandlerMap.size === 1 &&
    291      this.#promptTypeToHandlerMap.has(FALLBACK_DEFAULT_PROMPT_TYPE)
    292    ) {
    293      return this.#promptTypeToHandlerMap
    294        .get(FALLBACK_DEFAULT_PROMPT_TYPE)
    295        .toJSON();
    296    }
    297 
    298    const serialized = {};
    299    for (const [key, value] of this.#promptTypeToHandlerMap.entries()) {
    300      serialized[key] = value.toJSON();
    301    }
    302 
    303    return serialized;
    304  }
    305 
    306  toString() {
    307    return "[object UserPromptHandler]";
    308  }
    309 }