tor-browser

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

browser.sys.mjs (9402B)


      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  error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
     10  EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
     11  MessageManagerDestroyedPromise:
     12    "chrome://remote/content/marionette/sync.sys.mjs",
     13  NavigableManager: "chrome://remote/content/shared/NavigableManager.sys.mjs",
     14  TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
     15  windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs",
     16 });
     17 
     18 /** @namespace */
     19 export const browser = {};
     20 
     21 /**
     22 * Variations of Marionette contexts.
     23 *
     24 * Choosing a context through the <tt>Marionette:SetContext</tt>
     25 * command directs all subsequent browsing context scoped commands
     26 * to that context.
     27 *
     28 * @class Marionette.Context
     29 */
     30 export class Context {
     31  /**
     32   * Gets the correct context from a string.
     33   *
     34   * @param {string} s
     35   *     Context string serialisation.
     36   *
     37   * @returns {Context}
     38   *     Context.
     39   *
     40   * @throws {TypeError}
     41   *     If <var>s</var> is not a context.
     42   */
     43  static fromString(s) {
     44    switch (s) {
     45      case "chrome":
     46        return Context.Chrome;
     47 
     48      case "content":
     49        return Context.Content;
     50 
     51      default:
     52        throw new TypeError(`Unknown context: ${s}`);
     53    }
     54  }
     55 }
     56 
     57 Context.Chrome = "chrome";
     58 Context.Content = "content";
     59 
     60 /**
     61 * Creates a browsing context wrapper.
     62 *
     63 * Browsing contexts handle interactions with the browser, according to
     64 * the current environment.
     65 */
     66 browser.Context = class {
     67  /**
     68   * @param {ChromeWindow} window
     69   *     ChromeWindow that contains the top-level browsing context.
     70   * @param {GeckoDriver} driver
     71   *     Reference to driver instance.
     72   */
     73  constructor(window, driver) {
     74    this.window = window;
     75    this.driver = driver;
     76 
     77    // In Firefox this is <xul:tabbrowser> (not <xul:browser>!)
     78    // and MobileTabBrowser in GeckoView.
     79    this.tabBrowser = lazy.TabManager.getTabBrowser(this.window);
     80 
     81    // Used to set curFrameId upon new session
     82    this.newSession = true;
     83 
     84    // A reference to the tab corresponding to the current window handle,
     85    // if any.  Specifically, this.tab refers to the last tab that Marionette
     86    // switched to in this browser window. Note that this may not equal the
     87    // currently selected tab.  For example, if Marionette switches to tab
     88    // A, and then clicks on a button that opens a new tab B in the same
     89    // browser window, this.tab will still point to tab A, despite tab B
     90    // being the currently selected tab.
     91    this.tab = null;
     92  }
     93 
     94  /**
     95   * Returns the content browser for the currently selected tab.
     96   * If there is no tab selected, null will be returned.
     97   */
     98  get contentBrowser() {
     99    if (this.tab) {
    100      return lazy.TabManager.getBrowserForTab(this.tab);
    101    } else if (
    102      this.tabBrowser &&
    103      this.driver.isReftestBrowser(this.tabBrowser)
    104    ) {
    105      return this.tabBrowser;
    106    }
    107 
    108    return null;
    109  }
    110 
    111  /**
    112   * Return the unique id of the content browser.
    113   */
    114  get contentBrowserId() {
    115    return lazy.NavigableManager.getIdForBrowser(this.contentBrowser);
    116  }
    117 
    118  get messageManager() {
    119    if (this.contentBrowser) {
    120      return this.contentBrowser.messageManager;
    121    }
    122 
    123    return null;
    124  }
    125 
    126  /**
    127   * Checks if the browsing context has been discarded.
    128   *
    129   * The browsing context will have been discarded if the content
    130   * browser, represented by the <code>&lt;xul:browser&gt;</code>,
    131   * has been detached.
    132   *
    133   * @returns {boolean}
    134   *     True if browsing context has been discarded, false otherwise.
    135   */
    136  get closed() {
    137    return this.contentBrowser === null;
    138  }
    139 
    140  /**
    141   * Gets the position and dimensions of the top-level browsing context.
    142   *
    143   * @returns {Map.<string, number>}
    144   *     Object with |x|, |y|, |width|, and |height| properties.
    145   */
    146  get rect() {
    147    return {
    148      x: this.window.screenX,
    149      y: this.window.screenY,
    150      width: this.window.outerWidth,
    151      height: this.window.outerHeight,
    152    };
    153  }
    154 
    155  /**
    156   * Close the current window.
    157   *
    158   * @returns {Promise}
    159   *     A promise which is resolved when the current window has been closed.
    160   */
    161  async closeWindow() {
    162    return lazy.windowManager.closeWindow(this.window);
    163  }
    164 
    165  /**
    166   * Focus the current window.
    167   *
    168   * @returns {Promise}
    169   *     A promise which is resolved when the current window has been focused.
    170   */
    171  async focusWindow() {
    172    await lazy.windowManager.focusWindow(this.window);
    173 
    174    // Also focus the currently selected tab if present.
    175    this.contentBrowser?.focus();
    176  }
    177 
    178  /**
    179   * Open a new browser window.
    180   *
    181   * @returns {Promise}
    182   *     A promise resolving to the newly created chrome window.
    183   */
    184  openBrowserWindow(focus = false, isPrivate = false) {
    185    return lazy.windowManager.openBrowserWindow({
    186      openerWindow: this.window,
    187      focus,
    188      isPrivate,
    189    });
    190  }
    191 
    192  /**
    193   * Close the current tab.
    194   *
    195   * @returns {Promise}
    196   *     A promise which is resolved when the current tab has been closed.
    197   *
    198   * @throws UnsupportedOperationError
    199   *     If tab handling for the current application isn't supported.
    200   */
    201  async closeTab() {
    202    // If the current window is not a browser then close it directly. Do the
    203    // same if only one remaining tab is open, or no tab selected at all.
    204    //
    205    // Note: For GeckoView there will always be a single tab only. But for
    206    // consistency with other platforms a specific condition has been added
    207    // below as well even it's not really used.
    208    if (
    209      !this.tabBrowser ||
    210      !this.tabBrowser.tabs ||
    211      this.tabBrowser.tabs.length === 1 ||
    212      !this.tab
    213    ) {
    214      return this.closeWindow();
    215    }
    216 
    217    let destroyed = new lazy.MessageManagerDestroyedPromise(
    218      this.messageManager
    219    );
    220    let tabClosed;
    221 
    222    if (lazy.AppInfo.isAndroid) {
    223      await lazy.TabManager.removeTab(this.tab);
    224    } else if (lazy.AppInfo.isFirefox) {
    225      tabClosed = new lazy.EventPromise(this.tab, "TabClose");
    226      await this.tabBrowser.removeTab(this.tab);
    227    } else {
    228      throw new lazy.error.UnsupportedOperationError(
    229        `closeTab() not supported for ${lazy.AppInfo.name}`
    230      );
    231    }
    232 
    233    return Promise.all([destroyed, tabClosed]);
    234  }
    235 
    236  /**
    237   * Open a new tab in the currently selected chrome window.
    238   */
    239  async openTab(focus = false) {
    240    let tab = null;
    241 
    242    // Bug 1795841 - For Firefox the TabManager cannot be used yet. As such
    243    // handle opening a tab differently for Android.
    244    if (lazy.AppInfo.isAndroid) {
    245      tab = await lazy.TabManager.addTab({ focus, window: this.window });
    246    } else if (lazy.AppInfo.isFirefox) {
    247      const opened = new lazy.EventPromise(this.window, "TabOpen");
    248      this.window.BrowserCommands.openTab({ url: "about:blank" });
    249      await opened;
    250 
    251      tab = this.tabBrowser.selectedTab;
    252 
    253      // The new tab is always selected by default. If focus is not wanted,
    254      // the previously tab needs to be selected again.
    255      if (!focus) {
    256        await lazy.TabManager.selectTab(this.tab);
    257      }
    258    } else {
    259      throw new lazy.error.UnsupportedOperationError(
    260        `openTab() not supported for ${lazy.AppInfo.name}`
    261      );
    262    }
    263 
    264    return tab;
    265  }
    266 
    267  /**
    268   * Set the current tab.
    269   *
    270   * @param {number=} index
    271   *     Tab index to switch to. If the parameter is undefined,
    272   *     the currently selected tab will be used.
    273   * @param {ChromeWindow=} window
    274   *     Switch to this window before selecting the tab.
    275   * @param {boolean=} focus
    276   *      A boolean value which determines whether to focus
    277   *      the window. Defaults to true.
    278   *
    279   * @returns {Tab}
    280   *     The selected tab.
    281   *
    282   * @throws UnsupportedOperationError
    283   *     If tab handling for the current application isn't supported.
    284   */
    285  async switchToTab(index, window = undefined, focus = true) {
    286    if (window) {
    287      this.window = window;
    288      this.tabBrowser = lazy.TabManager.getTabBrowser(this.window);
    289    }
    290 
    291    if (!this.tabBrowser || this.driver.isReftestBrowser(this.tabBrowser)) {
    292      return null;
    293    }
    294 
    295    if (typeof index == "undefined") {
    296      this.tab = this.tabBrowser.selectedTab;
    297    } else {
    298      this.tab = this.tabBrowser.tabs[index];
    299    }
    300 
    301    if (focus) {
    302      await lazy.TabManager.selectTab(this.tab);
    303    }
    304 
    305    // By accessing the content browser's message manager a new browsing
    306    // context is created for browserless tabs, which is needed to successfully
    307    // run the WebDriver's is browsing context open step. This is temporary
    308    // until we find a better solution on bug 1812258.
    309    this.messageManager;
    310 
    311    return this.tab;
    312  }
    313 
    314  /**
    315   * Registers a new frame, and sets its current frame id to this frame
    316   * if it is not already assigned, and if a) we already have a session
    317   * or b) we're starting a new session and it is the right start frame.
    318   */
    319  register() {
    320    if (!this.tabBrowser) {
    321      return;
    322    }
    323 
    324    // If we're setting up a new session on Firefox, we only process the
    325    // registration for this frame if it belongs to the current tab.
    326    if (!this.tab) {
    327      this.switchToTab();
    328    }
    329  }
    330 };