Bug 1791785 - When dumping symbols never emit INLINE_ORIGIN directives with an empty...
[gecko.git] / browser / modules / NewTabPagePreloading.jsm
blob286feafd4b49aa697ea40213dac8e58d2e886487
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/. */
5 /*
6  * This module is in charge of preloading 'new tab' pages for use when
7  * the user opens a new tab.
8  */
10 var EXPORTED_SYMBOLS = ["NewTabPagePreloading"];
12 const { XPCOMUtils } = ChromeUtils.importESModule(
13   "resource://gre/modules/XPCOMUtils.sys.mjs"
16 const lazy = {};
18 XPCOMUtils.defineLazyModuleGetters(lazy, {
19   AboutNewTab: "resource:///modules/AboutNewTab.jsm",
20   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
21   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
22   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
23 });
25 let NewTabPagePreloading = {
26   // Maximum number of instances of a given page we'll preload at any time.
27   // Because we preload about:newtab for normal windows, and about:privatebrowsing
28   // for private ones, we could have 3 of each.
29   MAX_COUNT: 3,
31   // How many preloaded tabs we have, across all windows, for the private and non-private
32   // case:
33   browserCounts: {
34     normal: 0,
35     private: 0,
36   },
38   get enabled() {
39     return (
40       this.prefEnabled &&
41       this.newTabEnabled &&
42       !lazy.AboutNewTab.newTabURLOverridden
43     );
44   },
46   /**
47    * Create a browser in the right process type.
48    */
49   _createBrowser(win) {
50     const {
51       gBrowser,
52       gMultiProcessBrowser,
53       gFissionBrowser,
54       BROWSER_NEW_TAB_URL,
55     } = win;
57     let oa = lazy.E10SUtils.predictOriginAttributes({ window: win });
59     let remoteType = lazy.E10SUtils.getRemoteTypeForURI(
60       BROWSER_NEW_TAB_URL,
61       gMultiProcessBrowser,
62       gFissionBrowser,
63       lazy.E10SUtils.DEFAULT_REMOTE_TYPE,
64       null,
65       oa
66     );
67     let browser = gBrowser.createBrowser({
68       isPreloadBrowser: true,
69       remoteType,
70     });
71     gBrowser.preloadedBrowser = browser;
73     let panel = gBrowser.getPanel(browser);
74     gBrowser.tabpanels.appendChild(panel);
76     return browser;
77   },
79   /**
80    * Move the contents of a preload browser across to a different window.
81    */
82   _adoptBrowserFromOtherWindow(window) {
83     let winPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(window);
84     // Grab the least-recently-focused window with a preloaded browser:
85     let oldWin = lazy.BrowserWindowTracker.orderedWindows
86       .filter(w => {
87         return (
88           winPrivate == lazy.PrivateBrowsingUtils.isWindowPrivate(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;
101     let newBrowser = this._createBrowser(window);
103     oldBrowser.swapBrowsers(newBrowser);
105     newBrowser.permanentKey = oldBrowser.permanentKey;
107     oldWin.gBrowser.getPanel(oldBrowser).remove();
108     return newBrowser;
109   },
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     }
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     }
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     }
146     let browser = this._createBrowser(window);
147     browser.loadURI(window.BROWSER_NEW_TAB_URL, {
148       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
149     });
150     browser.docShellIsActive = false;
151     browser._urlbarFocused = true;
153     // Make sure the preloaded browser is loaded with desired zoom level
154     let tabURI = Services.io.newURI(window.BROWSER_NEW_TAB_URL);
155     window.FullZoom.onLocationChange(tabURI, false, browser);
157     this.browserCounts[countKey]++;
158   },
160   getPreloadedBrowser(window) {
161     if (!this.enabled) {
162       return null;
163     }
165     // The preloaded browser might be null.
166     let browser = window.gBrowser.preloadedBrowser;
168     // Consume the browser.
169     window.gBrowser.preloadedBrowser = null;
171     // Attach the nsIFormFillController now that we know the browser
172     // will be used. If we do that before and the preloaded browser
173     // won't be consumed until shutdown then we leak a docShell.
174     // Also, we do not need to take care of attaching nsIFormFillControllers
175     // in the case that the browser is remote, as remote browsers take
176     // care of that themselves.
177     if (browser) {
178       let countKey = lazy.PrivateBrowsingUtils.isWindowPrivate(window)
179         ? "private"
180         : "normal";
181       this.browserCounts[countKey]--;
182       browser.removeAttribute("preloadedState");
183       browser.setAttribute("autocompletepopup", "PopupAutoComplete");
184     }
186     return browser;
187   },
189   removePreloadedBrowser(window) {
190     let browser = this.getPreloadedBrowser(window);
191     if (browser) {
192       window.gBrowser.getPanel(browser).remove();
193     }
194   },
197 XPCOMUtils.defineLazyPreferenceGetter(
198   NewTabPagePreloading,
199   "prefEnabled",
200   "browser.newtab.preload",
201   true
203 XPCOMUtils.defineLazyPreferenceGetter(
204   NewTabPagePreloading,
205   "newTabEnabled",
206   "browser.newtabpage.enabled",
207   true