gecko-dev/browser/modules/NewTabPagePreloading.jsm
J. Ryan Stinnett a43303bc1b Bug 1580117 - Skip preloaded browsers in minimized windows. r=mattwoodrow,Gijs
If the browser window is minimized, it's unlikely that a preloaded browser will
be useful in the near future. This change skips creating one in such a case.

This avoids an issue in the graphics layer where about:newtab content marks
itself as `renderLayers = true`, and will try to composite content in windows
which are not compositing (because they are hidden).

As an additional level of safety, we also check the preloaded browser's owner
window's minimized state just before setting `renderLayers`, in case it may have
changed after the preloaded browser was created.

Differential Revision: https://phabricator.services.mozilla.com/D85955
2020-09-17 16:57:35 +00:00

202 lines
6.1 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* This module is in charge of preloading 'new tab' pages for use when
* the user opens a new tab.
*/
var EXPORTED_SYMBOLS = ["NewTabPagePreloading"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.jsm",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
});
let NewTabPagePreloading = {
// Maximum number of instances of a given page we'll preload at any time.
// Because we preload about:newtab for normal windows, and about:privatebrowsing
// for private ones, we could have 3 of each.
MAX_COUNT: 3,
// How many preloaded tabs we have, across all windows, for the private and non-private
// case:
browserCounts: {
normal: 0,
private: 0,
},
get enabled() {
return (
this.prefEnabled && this.newTabEnabled && !AboutNewTab.newTabURLOverridden
);
},
/**
* Create a browser in the right process type.
*/
_createBrowser(win) {
const {
gBrowser,
gMultiProcessBrowser,
gFissionBrowser,
BROWSER_NEW_TAB_URL,
} = win;
let remoteType = E10SUtils.getRemoteTypeForURI(
BROWSER_NEW_TAB_URL,
gMultiProcessBrowser,
gFissionBrowser
);
let browser = gBrowser.createBrowser({
isPreloadBrowser: true,
remoteType,
});
gBrowser.preloadedBrowser = browser;
let panel = gBrowser.getPanel(browser);
gBrowser.tabpanels.appendChild(panel);
return browser;
},
/**
* Move the contents of a preload browser across to a different window.
*/
_adoptBrowserFromOtherWindow(window) {
let winPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
// Grab the least-recently-focused window with a preloaded browser:
let oldWin = BrowserWindowTracker.orderedWindows
.filter(w => {
return (
winPrivate == PrivateBrowsingUtils.isWindowPrivate(w) &&
w.gBrowser &&
w.gBrowser.preloadedBrowser
);
})
.pop();
if (!oldWin) {
return null;
}
// Don't call getPreloadedBrowser because it'll consume the browser:
let oldBrowser = oldWin.gBrowser.preloadedBrowser;
oldWin.gBrowser.preloadedBrowser = null;
let newBrowser = this._createBrowser(window);
oldBrowser.swapBrowsers(newBrowser);
newBrowser.permanentKey = oldBrowser.permanentKey;
oldWin.gBrowser.getPanel(oldBrowser).remove();
return newBrowser;
},
maybeCreatePreloadedBrowser(window) {
// If we're not enabled, have already got one, are in a popup window, or the
// window is minimized / occluded, don't bother creating a preload browser -
// there's no point.
if (
!this.enabled ||
window.gBrowser.preloadedBrowser ||
!window.toolbar.visible ||
window.windowState == window.STATE_MINIMIZED ||
window.isFullyOccluded
) {
return;
}
// Don't bother creating a preload browser if we're not in the top set of windows:
let windowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
let countKey = windowPrivate ? "private" : "normal";
let topWindows = BrowserWindowTracker.orderedWindows.filter(
w => PrivateBrowsingUtils.isWindowPrivate(w) == windowPrivate
);
if (topWindows.indexOf(window) >= this.MAX_COUNT) {
return;
}
// If we're in the top set of windows, and we already have enough preloaded
// tabs, don't create yet another one, just steal an existing one:
if (this.browserCounts[countKey] >= this.MAX_COUNT) {
let browser = this._adoptBrowserFromOtherWindow(window);
// We can potentially get null here if we couldn't actually find another
// browser to adopt from. This can be the case when there's a mix of
// private and non-private windows, for instance.
if (browser) {
return;
}
}
let browser = this._createBrowser(window);
browser.loadURI(window.BROWSER_NEW_TAB_URL, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
browser.docShellIsActive = false;
browser._urlbarFocused = true;
// Make sure the preloaded browser is loaded with desired zoom level
let tabURI = Services.io.newURI(window.BROWSER_NEW_TAB_URL);
window.FullZoom.onLocationChange(tabURI, false, browser);
this.browserCounts[countKey]++;
},
getPreloadedBrowser(window) {
if (!this.enabled) {
return null;
}
// The preloaded browser might be null.
let browser = window.gBrowser.preloadedBrowser;
// Consume the browser.
window.gBrowser.preloadedBrowser = null;
// Attach the nsIFormFillController now that we know the browser
// will be used. If we do that before and the preloaded browser
// won't be consumed until shutdown then we leak a docShell.
// Also, we do not need to take care of attaching nsIFormFillControllers
// in the case that the browser is remote, as remote browsers take
// care of that themselves.
if (browser) {
let countKey = PrivateBrowsingUtils.isWindowPrivate(window)
? "private"
: "normal";
this.browserCounts[countKey]--;
browser.setAttribute("preloadedState", "consumed");
browser.setAttribute("autocompletepopup", "PopupAutoComplete");
}
return browser;
},
removePreloadedBrowser(window) {
let browser = this.getPreloadedBrowser(window);
if (browser) {
window.gBrowser.getPanel(browser).remove();
}
},
};
XPCOMUtils.defineLazyPreferenceGetter(
NewTabPagePreloading,
"prefEnabled",
"browser.newtab.preload",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
NewTabPagePreloading,
"newTabEnabled",
"browser.newtabpage.enabled",
true
);