tor-browser

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

TabStateFlusher.sys.mjs (5102B)


      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 /**
      6 * A module that enables async flushes. Updates from frame scripts are
      7 * throttled to be sent only once per second. If an action wants a tab's latest
      8 * state without waiting for a second then it can request an async flush and
      9 * wait until the frame scripts reported back. At this point the parent has the
     10 * latest data and the action can continue.
     11 */
     12 export var TabStateFlusher = Object.freeze({
     13  /**
     14   * Requests an async flush for the given browser. Returns a promise that will
     15   * resolve when we heard back from the content process and the parent has
     16   * all the latest data.
     17   */
     18  flush(browser) {
     19    return TabStateFlusherInternal.flush(browser);
     20  },
     21 
     22  /**
     23   * Requests an async flush for all browsers of a given window. Returns a Promise
     24   * that will resolve when we've heard back from all browsers.
     25   */
     26  flushWindow(window) {
     27    return TabStateFlusherInternal.flushWindow(window);
     28  },
     29 
     30  /**
     31   * Resolves all active flush requests for a given browser. This should be
     32   * used when the content process crashed or the final update message was
     33   * seen. In those cases we can't guarantee to ever hear back from the frame
     34   * script so we just resolve all requests instead of discarding them.
     35   *
     36   * @param browser (<xul:browser>)
     37   *        The browser for which all flushes are being resolved.
     38   * @param success (bool, optional)
     39   *        Whether or not the flushes succeeded.
     40   * @param message (string, optional)
     41   *        An error message that will be sent to the Console in the
     42   *        event that the flushes failed.
     43   */
     44  resolveAll(browser, success = true, message = "") {
     45    TabStateFlusherInternal.resolveAll(browser, success, message);
     46  },
     47 });
     48 
     49 var TabStateFlusherInternal = {
     50  // A map storing all active requests per browser. A request is a
     51  // triple of a map containing all flush requests, a promise that
     52  // resolve when a request for a browser is canceled, and the
     53  // function to call to cancel a reqeust.
     54  _requests: new WeakMap(),
     55 
     56  initEntry(entry) {
     57    entry.cancelPromise = new Promise(resolve => {
     58      entry.cancel = resolve;
     59    }).then(result => {
     60      TabStateFlusherInternal.initEntry(entry);
     61      return result;
     62    });
     63 
     64    return entry;
     65  },
     66 
     67  /**
     68   * Requests an async flush for the given browser. Returns a promise that will
     69   * resolve when we heard back from the content process and the parent has
     70   * all the latest data.
     71   */
     72  flush(browser) {
     73    let nativePromise = Promise.resolve();
     74    if (browser && browser.frameLoader) {
     75      /*
     76        Request native listener to flush the tabState.
     77        Resolves when flush is complete.
     78      */
     79      nativePromise = browser.frameLoader.requestTabStateFlush();
     80    }
     81 
     82    // Retrieve active requests for given browser.
     83    let permanentKey = browser.permanentKey;
     84    let request = this._requests.get(permanentKey);
     85    if (!request) {
     86      // If we don't have any requests for this browser, create a new
     87      // entry for browser.
     88      request = this.initEntry({});
     89      this._requests.set(permanentKey, request);
     90    }
     91 
     92    // It's fine to resolve the request immediately after the native promise
     93    // resolves, since SessionStore will have processed all updates from this
     94    // browser by that point.
     95    return Promise.race([nativePromise, request.cancelPromise]);
     96  },
     97 
     98  /**
     99   * Requests an async flush for all non-lazy browsers of a given window.
    100   * Returns a Promise that will resolve when we've heard back from all browsers.
    101   */
    102  flushWindow(window) {
    103    let promises = [];
    104    for (let browser of window.gBrowser.browsers) {
    105      if (window.gBrowser.getTabForBrowser(browser).linkedPanel) {
    106        promises.push(this.flush(browser));
    107      }
    108    }
    109    return Promise.all(promises);
    110  },
    111 
    112  /**
    113   * Resolves all active flush requests for a given browser. This should be
    114   * used when the content process crashed or the final update message was
    115   * seen. In those cases we can't guarantee to ever hear back from the frame
    116   * script so we just resolve all requests instead of discarding them.
    117   *
    118   * @param browser (<xul:browser>)
    119   *        The browser for which all flushes are being resolved.
    120   * @param success (bool, optional)
    121   *        Whether or not the flushes succeeded.
    122   * @param message (string, optional)
    123   *        An error message that will be sent to the Console in the
    124   *        event that the flushes failed.
    125   */
    126  resolveAll(browser, success = true, message = "") {
    127    // Nothing to do if there are no pending flushes for the given browser.
    128    if (!this._requests.has(browser.permanentKey)) {
    129      return;
    130    }
    131 
    132    // Retrieve the cancel function for a given browser.
    133    let { cancel } = this._requests.get(browser.permanentKey);
    134 
    135    if (!success) {
    136      console.error("Failed to flush browser: ", message);
    137    }
    138 
    139    // Resolve all requests.
    140    cancel(success);
    141  },
    142 };