Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / browser / modules / NewTabPagePreloading.sys.mjs
blobbf6b0501954c860e6a939879b8302e3c2a6d3579
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 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
12 const lazy = {};
14 ChromeUtils.defineESModuleGetters(lazy, {
15   AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
16   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
17   E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
18   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
19 });
21 export let NewTabPagePreloading = {
22   // Maximum number of instances of a given page we'll preload at any time.
23   // Because we preload about:newtab for normal windows, and about:privatebrowsing
24   // for private ones, we could have 3 of each.
25   MAX_COUNT: 3,
27   // How many preloaded tabs we have, across all windows, for the private and non-private
28   // case:
29   browserCounts: {
30     normal: 0,
31     private: 0,
32   },
34   get enabled() {
35     return (
36       this.prefEnabled &&
37       this.newTabEnabled &&
38       !lazy.AboutNewTab.newTabURLOverridden
39     );
40   },
42   /**
43    * Create a browser in the right process type.
44    */
45   _createBrowser(win) {
46     const {
47       gBrowser,
48       gMultiProcessBrowser,
49       gFissionBrowser,
50       BROWSER_NEW_TAB_URL,
51     } = win;
53     let oa = lazy.E10SUtils.predictOriginAttributes({ window: win });
55     let remoteType = lazy.E10SUtils.getRemoteTypeForURI(
56       BROWSER_NEW_TAB_URL,
57       gMultiProcessBrowser,
58       gFissionBrowser,
59       lazy.E10SUtils.DEFAULT_REMOTE_TYPE,
60       null,
61       oa
62     );
63     let browser = gBrowser.createBrowser({
64       isPreloadBrowser: true,
65       remoteType,
66     });
67     gBrowser.preloadedBrowser = browser;
69     let panel = gBrowser.getPanel(browser);
70     gBrowser.tabpanels.appendChild(panel);
72     return browser;
73   },
75   /**
76    * Move the contents of a preload browser across to a different window.
77    */
78   _adoptBrowserFromOtherWindow(window) {
79     let winPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(window);
80     // Grab the least-recently-focused window with a preloaded browser:
81     let oldWin = lazy.BrowserWindowTracker.orderedWindows
82       .filter(w => {
83         return (
84           winPrivate == lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
85           w.gBrowser &&
86           w.gBrowser.preloadedBrowser
87         );
88       })
89       .pop();
90     if (!oldWin) {
91       return null;
92     }
93     // Don't call getPreloadedBrowser because it'll consume the browser:
94     let oldBrowser = oldWin.gBrowser.preloadedBrowser;
95     oldWin.gBrowser.preloadedBrowser = null;
97     let newBrowser = this._createBrowser(window);
99     oldBrowser.swapBrowsers(newBrowser);
101     newBrowser.permanentKey = oldBrowser.permanentKey;
103     oldWin.gBrowser.getPanel(oldBrowser).remove();
104     return newBrowser;
105   },
107   maybeCreatePreloadedBrowser(window) {
108     // If we're not enabled, have already got one, are in a popup window, or the
109     // window is minimized / occluded, don't bother creating a preload browser -
110     // there's no point.
111     if (
112       !this.enabled ||
113       window.gBrowser.preloadedBrowser ||
114       !window.toolbar.visible ||
115       window.document.hidden
116     ) {
117       return;
118     }
120     // Don't bother creating a preload browser if we're not in the top set of windows:
121     let windowPrivate = lazy.PrivateBrowsingUtils.isWindowPrivate(window);
122     let countKey = windowPrivate ? "private" : "normal";
123     let topWindows = lazy.BrowserWindowTracker.orderedWindows.filter(
124       w => lazy.PrivateBrowsingUtils.isWindowPrivate(w) == windowPrivate
125     );
126     if (topWindows.indexOf(window) >= this.MAX_COUNT) {
127       return;
128     }
130     // If we're in the top set of windows, and we already have enough preloaded
131     // tabs, don't create yet another one, just steal an existing one:
132     if (this.browserCounts[countKey] >= this.MAX_COUNT) {
133       let browser = this._adoptBrowserFromOtherWindow(window);
134       // We can potentially get null here if we couldn't actually find another
135       // browser to adopt from. This can be the case when there's a mix of
136       // private and non-private windows, for instance.
137       if (browser) {
138         return;
139       }
140     }
142     let browser = this._createBrowser(window);
143     browser.loadURI(Services.io.newURI(window.BROWSER_NEW_TAB_URL), {
144       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
145     });
146     browser.docShellIsActive = false;
147     browser._urlbarFocused = true;
149     // Make sure the preloaded browser is loaded with desired zoom level
150     let tabURI = Services.io.newURI(window.BROWSER_NEW_TAB_URL);
151     window.FullZoom.onLocationChange(tabURI, false, browser);
153     this.browserCounts[countKey]++;
154   },
156   getPreloadedBrowser(window) {
157     if (!this.enabled) {
158       return null;
159     }
161     // The preloaded browser might be null.
162     let browser = window.gBrowser.preloadedBrowser;
164     // Consume the browser.
165     window.gBrowser.preloadedBrowser = null;
167     // Attach the nsIFormFillController now that we know the browser
168     // will be used. If we do that before and the preloaded browser
169     // won't be consumed until shutdown then we leak a docShell.
170     // Also, we do not need to take care of attaching nsIFormFillControllers
171     // in the case that the browser is remote, as remote browsers take
172     // care of that themselves.
173     if (browser) {
174       let countKey = lazy.PrivateBrowsingUtils.isWindowPrivate(window)
175         ? "private"
176         : "normal";
177       this.browserCounts[countKey]--;
178       browser.removeAttribute("preloadedState");
179       browser.setAttribute("autocompletepopup", "PopupAutoComplete");
180     }
182     return browser;
183   },
185   removePreloadedBrowser(window) {
186     let browser = this.getPreloadedBrowser(window);
187     if (browser) {
188       window.gBrowser.getPanel(browser).remove();
189     }
190   },
193 XPCOMUtils.defineLazyPreferenceGetter(
194   NewTabPagePreloading,
195   "prefEnabled",
196   "browser.newtab.preload",
197   true
199 XPCOMUtils.defineLazyPreferenceGetter(
200   NewTabPagePreloading,
201   "newTabEnabled",
202   "browser.newtabpage.enabled",
203   true