зеркало из https://github.com/mozilla/gecko-dev.git
211 строки
6.4 KiB
JavaScript
211 строки
6.4 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);
|
|
|
|
oldWin.gBrowser._outerWindowIDBrowserMap.delete(oldBrowser.outerWindowID);
|
|
window.gBrowser._outerWindowIDBrowserMap.delete(newBrowser.outerWindowID);
|
|
|
|
oldBrowser.swapBrowsers(newBrowser);
|
|
|
|
// Switch outerWindowIDs for remote browsers.
|
|
if (newBrowser.isRemoteBrowser) {
|
|
newBrowser._outerWindowID = oldBrowser._outerWindowID;
|
|
}
|
|
|
|
window.gBrowser._outerWindowIDBrowserMap.set(
|
|
newBrowser.outerWindowID,
|
|
newBrowser
|
|
);
|
|
newBrowser.permanentKey = oldBrowser.permanentKey;
|
|
|
|
oldWin.gBrowser.getPanel(oldBrowser).remove();
|
|
return newBrowser;
|
|
},
|
|
|
|
maybeCreatePreloadedBrowser(window) {
|
|
// If we're not enabled, have already got one, or are in a popup window,
|
|
// don't bother creating a preload browser - there's no point.
|
|
if (
|
|
!this.enabled ||
|
|
window.gBrowser.preloadedBrowser ||
|
|
!window.toolbar.visible
|
|
) {
|
|
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
|
|
);
|