tor-browser

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

NewTabPagePreloading.sys.mjs (7473B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * This module is in charge of preloading 'new tab' pages for use when
      7 * the user opens a new tab.
      8 */
      9 
     10 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
     11 
     12 const lazy = {};
     13 
     14 ChromeUtils.defineESModuleGetters(lazy, {
     15  AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
     16  AIWindow:
     17    "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs",
     18  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
     19  E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
     20  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
     21 });
     22 
     23 export let NewTabPagePreloading = {
     24  // Maximum number of instances of a given page we'll preload at any time.
     25  // Because we preload about:newtab for normal windows, and about:privatebrowsing
     26  // for private ones, we could have 3 of each.
     27  MAX_COUNT: 3,
     28 
     29  // How many preloaded tabs we have, across all windows, for the private and non-private
     30  // case:
     31  browserCounts: {
     32    normal: 0,
     33    private: 0,
     34  },
     35 
     36  get enabled() {
     37    return (
     38      this.prefEnabled &&
     39      this.newTabEnabled &&
     40      !lazy.AboutNewTab.newTabURLOverridden
     41    );
     42  },
     43 
     44  /**
     45   * Create a browser in the right process type.
     46   */
     47  _createBrowser(win) {
     48    const {
     49      gBrowser,
     50      gMultiProcessBrowser,
     51      gFissionBrowser,
     52      BROWSER_NEW_TAB_URL,
     53    } = win;
     54 
     55    let oa = lazy.E10SUtils.predictOriginAttributes({ window: win });
     56 
     57    let remoteType = lazy.E10SUtils.getRemoteTypeForURI(
     58      BROWSER_NEW_TAB_URL,
     59      gMultiProcessBrowser,
     60      gFissionBrowser,
     61      lazy.E10SUtils.DEFAULT_REMOTE_TYPE,
     62      null,
     63      oa
     64    );
     65    let browser = gBrowser.createBrowser({
     66      isPreloadBrowser: true,
     67      remoteType,
     68    });
     69    gBrowser.preloadedBrowser = browser;
     70 
     71    let panel = gBrowser.getPanel(browser);
     72    gBrowser.tabpanels.appendChild(panel);
     73 
     74    return browser;
     75  },
     76 
     77  /**
     78   * Move the contents of a preload browser across to a different window.
     79   */
     80  _adoptBrowserFromOtherWindow(window) {
     81    let winPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(window);
     82    let winAIWindow = lazy.AIWindow.isAIWindowActive(window);
     83    // Grab the least-recently-focused window with a preloaded browser:
     84    let oldWin = lazy.BrowserWindowTracker.orderedWindows
     85      .filter(w => {
     86        return (
     87          winPrivate == lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
     88          winAIWindow == lazy.AIWindow.isAIWindowActive(w) &&
     89          w.gBrowser &&
     90          w.gBrowser.preloadedBrowser
     91        );
     92      })
     93      .pop();
     94    if (!oldWin) {
     95      return null;
     96    }
     97    // Don't call getPreloadedBrowser because it'll consume the browser:
     98    let oldBrowser = oldWin.gBrowser.preloadedBrowser;
     99    oldWin.gBrowser.preloadedBrowser = null;
    100 
    101    let newBrowser = this._createBrowser(window);
    102 
    103    oldBrowser.swapBrowsers(newBrowser);
    104 
    105    newBrowser.permanentKey = oldBrowser.permanentKey;
    106 
    107    oldWin.gBrowser.getPanel(oldBrowser).remove();
    108    return newBrowser;
    109  },
    110 
    111  maybeCreatePreloadedBrowser(window) {
    112    // If we're not enabled, have already got one, are in a popup window, or the
    113    // window is minimized / occluded, don't bother creating a preload browser -
    114    // there's no point.
    115    if (
    116      !this.enabled ||
    117      window.gBrowser.preloadedBrowser ||
    118      !window.toolbar.visible ||
    119      window.document.hidden
    120    ) {
    121      return;
    122    }
    123 
    124    // Don't bother creating a preload browser if we're not in the top set of windows:
    125    let windowPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(window);
    126    let countKey = windowPrivate ? "private" : "normal";
    127    let topWindows = lazy.BrowserWindowTracker.orderedWindows.filter(
    128      w => lazy.PrivateBrowsingUtils.isWindowPrivate(w) == windowPrivate
    129    );
    130    if (topWindows.indexOf(window) >= this.MAX_COUNT) {
    131      return;
    132    }
    133 
    134    // If we're in the top set of windows, and we already have enough preloaded
    135    // tabs, don't create yet another one, just steal an existing one:
    136    if (this.browserCounts[countKey] >= this.MAX_COUNT) {
    137      let browser = this._adoptBrowserFromOtherWindow(window);
    138      // We can potentially get null here if we couldn't actually find another
    139      // browser to adopt from. This can be the case when there's a mix of
    140      // private and non-private windows, for instance.
    141      if (browser) {
    142        return;
    143      }
    144    }
    145 
    146    let browser = this._createBrowser(window);
    147    browser.loadURI(Services.io.newURI(window.BROWSER_NEW_TAB_URL), {
    148      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
    149    });
    150    browser.docShellIsActive = false;
    151 
    152    window.gURLBar.getBrowserState(browser).urlbarFocused = true;
    153 
    154    // Make sure the preloaded browser is loaded with desired zoom level
    155    let tabURI = Services.io.newURI(window.BROWSER_NEW_TAB_URL);
    156    window.FullZoom.onLocationChange(tabURI, false, browser);
    157 
    158    this.browserCounts[countKey]++;
    159  },
    160 
    161  getPreloadedBrowser(window) {
    162    if (!this.enabled) {
    163      return null;
    164    }
    165 
    166    // The preloaded browser might be null.
    167    let browser = window.gBrowser.preloadedBrowser;
    168 
    169    // Consume the browser.
    170    window.gBrowser.preloadedBrowser = null;
    171 
    172    // Attach the nsIFormFillController now that we know the browser
    173    // will be used. If we do that before and the preloaded browser
    174    // won't be consumed until shutdown then we leak a docShell.
    175    // Also, we do not need to take care of attaching nsIFormFillControllers
    176    // in the case that the browser is remote, as remote browsers take
    177    // care of that themselves.
    178    if (browser) {
    179      let countKey = lazy.PrivateBrowsingUtils.isWindowPrivate(window)
    180        ? "private"
    181        : "normal";
    182      this.browserCounts[countKey]--;
    183      browser.removeAttribute("preloadedState");
    184      browser.setAttribute("autocompletepopup", "PopupAutoComplete");
    185      // Let a preloaded about:tor page know that it is no longer preloaded
    186      // (about to be shown). See tor-browser#44314.
    187      // NOTE: We call the AboutTorParent instance directly because it is not
    188      // reliable for the AboutTorParent to wait for the "preloadedState"
    189      // attribute to change via a MutationObserver on the browsingContext's
    190      // browser element because the AboutTorParent's browsingContext's browser
    191      // element may be swapped out. E.g. see the "SwapDocShells" event.
    192      // NOTE: We assume that this is the only place that removes the
    193      // "preloadedState" attribute.
    194      // NOTE: Alternatively, we could have the AboutTorParent wait for
    195      // MozAfterPaint, but this would be slightly delayed.
    196      try {
    197        browser.browsingContext?.currentWindowGlobal
    198          ?.getActor("AboutTor")
    199          .preloadedRemoved();
    200      } catch {
    201        // Not an about:tor page with an AboutTorParent instance.
    202      }
    203    }
    204 
    205    return browser;
    206  },
    207 
    208  removePreloadedBrowser(window) {
    209    let browser = this.getPreloadedBrowser(window);
    210    if (browser) {
    211      window.gBrowser.getPanel(browser).remove();
    212    }
    213  },
    214 };
    215 
    216 XPCOMUtils.defineLazyPreferenceGetter(
    217  NewTabPagePreloading,
    218  "prefEnabled",
    219  "browser.newtab.preload",
    220  true
    221 );
    222 XPCOMUtils.defineLazyPreferenceGetter(
    223  NewTabPagePreloading,
    224  "newTabEnabled",
    225  "browser.newtabpage.enabled",
    226  true
    227 );