tor-browser

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

Prompt.sys.mjs (5640B)


      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  AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
      9  Log: "chrome://remote/content/shared/Log.sys.mjs",
     10 });
     11 
     12 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
     13 
     14 const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
     15 
     16 /** @namespace */
     17 export const modal = {
     18  ACTION_CLOSED: "closed",
     19  ACTION_OPENED: "opened",
     20 };
     21 
     22 /**
     23 * Check for already existing modal or tab modal dialogs and
     24 * return the first one.
     25 *
     26 * @param {browser.Context} context
     27 *     Reference to the browser context to check for existent dialogs.
     28 *
     29 * @returns {modal.Dialog}
     30 *     Returns instance of the Dialog class, or `null` if no modal dialog
     31 *     is present.
     32 */
     33 modal.findPrompt = function (context) {
     34  // First check if there is a modal dialog already present for the
     35  // current browser window.
     36  for (let win of Services.wm.getEnumerator(null)) {
     37    // TODO: Use BrowserWindowTracker.getTopWindow for modal dialogs without
     38    // an opener.
     39    if (
     40      win.document.documentURI === COMMON_DIALOG &&
     41      win.opener &&
     42      win.opener === context.window
     43    ) {
     44      lazy.logger.trace("Found open window modal prompt");
     45      return new modal.Dialog(win);
     46    }
     47  }
     48 
     49  if (lazy.AppInfo.isAndroid) {
     50    const geckoViewPrompts = context.window.prompts();
     51    if (geckoViewPrompts.length) {
     52      lazy.logger.trace("Found open GeckoView prompt");
     53      const prompt = geckoViewPrompts[0];
     54      return new modal.Dialog(prompt);
     55    }
     56  }
     57 
     58  const contentBrowser = context.contentBrowser;
     59 
     60  // If no modal dialog has been found yet, also check for tab and content modal
     61  // dialogs for the current tab.
     62  //
     63  // TODO: Find an adequate implementation for Firefox on Android (bug 1708105)
     64  if (contentBrowser?.tabDialogBox) {
     65    let dialogs = contentBrowser.tabDialogBox.getTabDialogManager().dialogs;
     66    if (dialogs.length) {
     67      lazy.logger.trace("Found open tab modal prompt");
     68      return new modal.Dialog(dialogs[0].frameContentWindow);
     69    }
     70 
     71    dialogs = contentBrowser.tabDialogBox.getContentDialogManager().dialogs;
     72 
     73    // Even with the dialog manager handing back a dialog, the `Dialog` property
     74    // gets lazily added. If it's not set yet, ignore the dialog for now.
     75    if (dialogs.length && dialogs[0].frameContentWindow.Dialog) {
     76      lazy.logger.trace("Found open content prompt");
     77      return new modal.Dialog(dialogs[0].frameContentWindow);
     78    }
     79  }
     80  return null;
     81 };
     82 
     83 /**
     84 * Represents a modal dialog.
     85 *
     86 * @param {DOMWindow} dialog
     87 *     DOMWindow of the dialog.
     88 */
     89 modal.Dialog = class {
     90  #win;
     91 
     92  constructor(dialog) {
     93    this.#win = Cu.getWeakReference(dialog);
     94  }
     95 
     96  get args() {
     97    if (lazy.AppInfo.isAndroid) {
     98      return this.window.args;
     99    }
    100    let tm = this.tabModal;
    101    return tm ? tm.args : null;
    102  }
    103 
    104  get isOpen() {
    105    if (lazy.AppInfo.isAndroid) {
    106      return this.window !== null;
    107    }
    108    if (!this.ui) {
    109      return false;
    110    }
    111    return true;
    112  }
    113 
    114  get isWindowModal() {
    115    return [
    116      Services.prompt.MODAL_TYPE_WINDOW,
    117      Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
    118    ].includes(this.args.modalType);
    119  }
    120 
    121  get tabModal() {
    122    return this.window?.Dialog;
    123  }
    124 
    125  get promptType() {
    126    return this.args.inPermitUnload ? "beforeunload" : this.args.promptType;
    127  }
    128 
    129  get ui() {
    130    let tm = this.tabModal;
    131    return tm ? tm.ui : null;
    132  }
    133 
    134  /**
    135   * For Android, this returns a GeckoViewPrompter, which can be used to control prompts.
    136   * Otherwise, this returns the ChromeWindow associated with an open dialog window if
    137   * it is currently attached to the DOM.
    138   */
    139  get window() {
    140    if (this.#win) {
    141      let win = this.#win.get();
    142      if (win && (lazy.AppInfo.isAndroid || win.parent)) {
    143        return win;
    144      }
    145    }
    146    return null;
    147  }
    148 
    149  /**
    150   * Sets the text of a prompt's input field.
    151   *
    152   * @param {string} inputText
    153   *     The text to set for the input field.
    154   */
    155  set text(inputText) {
    156    if (lazy.AppInfo.isAndroid) {
    157      this.window.setInputText(inputText);
    158    } else {
    159      // see toolkit/components/prompts/content/commonDialog.js
    160      let { loginTextbox } = this.ui;
    161      loginTextbox.value = inputText;
    162    }
    163  }
    164 
    165  /**
    166   * Accept the user prompt.
    167   */
    168  accept() {
    169    if (lazy.AppInfo.isAndroid) {
    170      // GeckoView does not have a UI, so the methods are called directly
    171      this.window.acceptPrompt();
    172    } else {
    173      const { button0 } = this.ui;
    174      button0.click();
    175    }
    176  }
    177 
    178  /**
    179   * Dismiss the user prompt.
    180   */
    181  dismiss() {
    182    if (lazy.AppInfo.isAndroid) {
    183      // GeckoView does not have a UI, so the methods are called directly
    184      this.window.dismissPrompt();
    185    } else {
    186      const { button0, button1 } = this.ui;
    187      (button1 ? button1 : button0).click();
    188    }
    189  }
    190 
    191  /**
    192   * Returns text of the prompt.
    193   *
    194   * @returns {Promise<string>}
    195   *     Returns a Promise resolving to the prompt text.
    196   */
    197  getText() {
    198    if (lazy.AppInfo.isAndroid) {
    199      return this.window.getPromptText();
    200    }
    201 
    202    return Promise.resolve(this.ui.infoBody.textContent);
    203  }
    204 
    205  /**
    206   * Returns text of the prompt input.
    207   *
    208   * @returns {Promise<string>}
    209   *     Returns a Promise resolving to the input's text.
    210   */
    211  getInputText() {
    212    if (lazy.AppInfo.isAndroid) {
    213      return this.window.getInputText();
    214    }
    215 
    216    return Promise.resolve(this.ui.loginTextbox.value);
    217  }
    218 };