Bug 1675375 Part 7: Update expectations in helper_hittest_clippath.html. r=botond
[gecko.git] / browser / modules / NewTabPagePreloading.jsm
blob55911d23a8777d4f8cdd473b8736ae0693cba60c
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 oa = E10SUtils.predictOriginAttributes({ window: win });
56     let remoteType = E10SUtils.getRemoteTypeForURI(
57       BROWSER_NEW_TAB_URL,
58       gMultiProcessBrowser,
59       gFissionBrowser,
60       E10SUtils.DEFAULT_REMOTE_TYPE,
61       null,
62       oa
63     );
64     let browser = gBrowser.createBrowser({
65       isPreloadBrowser: true,
66       remoteType,
67     });
68     gBrowser.preloadedBrowser = browser;
70     let panel = gBrowser.getPanel(browser);
71     gBrowser.tabpanels.appendChild(panel);
73     return browser;
74   },
76   /**
77    * Move the contents of a preload browser across to a different window.
78    */
79   _adoptBrowserFromOtherWindow(window) {
80     let winPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
81     // Grab the least-recently-focused window with a preloaded browser:
82     let oldWin = BrowserWindowTracker.orderedWindows
83       .filter(w => {
84         return (
85           winPrivate == PrivateBrowsingUtils.isWindowPrivate(w) &&
86           w.gBrowser &&
87           w.gBrowser.preloadedBrowser
88         );
89       })
90       .pop();
91     if (!oldWin) {
92       return null;
93     }
94     // Don't call getPreloadedBrowser because it'll consume the browser:
95     let oldBrowser = oldWin.gBrowser.preloadedBrowser;
96     oldWin.gBrowser.preloadedBrowser = null;
98     let newBrowser = this._createBrowser(window);
100     oldBrowser.swapBrowsers(newBrowser);
102     newBrowser.permanentKey = oldBrowser.permanentKey;
104     oldWin.gBrowser.getPanel(oldBrowser).remove();
105     return newBrowser;
106   },
108   maybeCreatePreloadedBrowser(window) {
109     // If we're not enabled, have already got one, are in a popup window, or the
110     // window is minimized / occluded, don't bother creating a preload browser -
111     // there's no point.
112     if (
113       !this.enabled ||
114       window.gBrowser.preloadedBrowser ||
115       !window.toolbar.visible ||
116       window.windowState == window.STATE_MINIMIZED ||
117       window.isFullyOccluded
118     ) {
119       return;
120     }
122     // Don't bother creating a preload browser if we're not in the top set of windows:
123     let windowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
124     let countKey = windowPrivate ? "private" : "normal";
125     let topWindows = BrowserWindowTracker.orderedWindows.filter(
126       w => PrivateBrowsingUtils.isWindowPrivate(w) == windowPrivate
127     );
128     if (topWindows.indexOf(window) >= this.MAX_COUNT) {
129       return;
130     }
132     // If we're in the top set of windows, and we already have enough preloaded
133     // tabs, don't create yet another one, just steal an existing one:
134     if (this.browserCounts[countKey] >= this.MAX_COUNT) {
135       let browser = this._adoptBrowserFromOtherWindow(window);
136       // We can potentially get null here if we couldn't actually find another
137       // browser to adopt from. This can be the case when there's a mix of
138       // private and non-private windows, for instance.
139       if (browser) {
140         return;
141       }
142     }
144     let browser = this._createBrowser(window);
145     browser.loadURI(window.BROWSER_NEW_TAB_URL, {
146       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
147     });
148     browser.docShellIsActive = false;
149     browser._urlbarFocused = true;
151     // Make sure the preloaded browser is loaded with desired zoom level
152     let tabURI = Services.io.newURI(window.BROWSER_NEW_TAB_URL);
153     window.FullZoom.onLocationChange(tabURI, false, browser);
155     this.browserCounts[countKey]++;
156   },
158   getPreloadedBrowser(window) {
159     if (!this.enabled) {
160       return null;
161     }
163     // The preloaded browser might be null.
164     let browser = window.gBrowser.preloadedBrowser;
166     // Consume the browser.
167     window.gBrowser.preloadedBrowser = null;
169     // Attach the nsIFormFillController now that we know the browser
170     // will be used. If we do that before and the preloaded browser
171     // won't be consumed until shutdown then we leak a docShell.
172     // Also, we do not need to take care of attaching nsIFormFillControllers
173     // in the case that the browser is remote, as remote browsers take
174     // care of that themselves.
175     if (browser) {
176       let countKey = PrivateBrowsingUtils.isWindowPrivate(window)
177         ? "private"
178         : "normal";
179       this.browserCounts[countKey]--;
180       browser.setAttribute("preloadedState", "consumed");
181       browser.setAttribute("autocompletepopup", "PopupAutoComplete");
182     }
184     return browser;
185   },
187   removePreloadedBrowser(window) {
188     let browser = this.getPreloadedBrowser(window);
189     if (browser) {
190       window.gBrowser.getPanel(browser).remove();
191     }
192   },
195 XPCOMUtils.defineLazyPreferenceGetter(
196   NewTabPagePreloading,
197   "prefEnabled",
198   "browser.newtab.preload",
199   true
201 XPCOMUtils.defineLazyPreferenceGetter(
202   NewTabPagePreloading,
203   "newTabEnabled",
204   "browser.newtabpage.enabled",
205   true