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