Bug 1353013 - move new tab preload browser tracking into a separate module, r=dthayer

Differential Revision: https://phabricator.services.mozilla.com/D21126

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gijs Kruitbosch 2019-03-19 17:29:14 +00:00
parent 7d501cb80f
commit 5e05f6c9c9
18 changed files with 142 additions and 126 deletions

View File

@ -13,6 +13,7 @@ ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
AMTelemetry: "resource://gre/modules/AddonManager.jsm",
NewTabPagePreloading: "resource:///modules/NewTabPagePreloading.jsm",
BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",

View File

@ -112,8 +112,6 @@ window._gBrowser = {
*/
_tabForBrowser: new WeakMap(),
_preloadedBrowser: null,
/**
* `_createLazyBrowser` will define properties on the unbound lazy browser
* which correspond to properties defined in MozBrowser which will be bound to
@ -162,6 +160,8 @@ window._gBrowser = {
*/
_windowIsClosing: false,
preloadedBrowser: null,
/**
* This defines a proxy which allows us to access browsers by
* index without actually creating a full array of browsers.
@ -329,7 +329,7 @@ window._gBrowser = {
sameProcessAsFrameLoader,
remoteType,
};
let browser = this._createBrowser(createOptions);
let browser = this.createBrowser(createOptions);
browser.setAttribute("primary", "true");
if (!tabArgument) {
browser.setAttribute("blank", "true");
@ -1820,87 +1820,7 @@ window._gBrowser = {
return false;
},
removePreloadedBrowser() {
if (!this._isPreloadingEnabled()) {
return;
}
let browser = this._getPreloadedBrowser();
if (browser) {
this.getPanel(browser).remove();
}
},
_getPreloadedBrowser() {
if (!this._isPreloadingEnabled()) {
return null;
}
// The preloaded browser might be null.
let browser = this._preloadedBrowser;
// Consume the browser.
this._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) {
browser.setAttribute("preloadedState", "consumed");
browser.setAttribute("autocompletepopup", "PopupAutoComplete");
}
return browser;
},
_isPreloadingEnabled() {
// Preloading for the newtab page is enabled when the prefs are true
// and the URL is "about:newtab". We do not support preloading for
// custom newtab URLs -- only for the default Firefox Home page.
return Services.prefs.getBoolPref("browser.newtab.preload") &&
Services.prefs.getBoolPref("browser.newtabpage.enabled") &&
!aboutNewTabService.overridden;
},
_createPreloadBrowser() {
// Do nothing if we have a preloaded browser already
// or preloading of newtab pages is disabled.
if (this._preloadedBrowser || !this._isPreloadingEnabled()) {
return;
}
let remoteType =
E10SUtils.getRemoteTypeForURI(BROWSER_NEW_TAB_URL,
gMultiProcessBrowser);
let browser = this._createBrowser({ isPreloadBrowser: true, remoteType });
this._preloadedBrowser = browser;
let panel = this.getPanel(browser);
this.tabpanels.appendChild(panel);
if (remoteType != E10SUtils.NOT_REMOTE) {
// For remote browsers, we need to make sure that the webProgress is
// instantiated, otherwise the parent won't get informed about the state
// of the preloaded browser until it gets attached to a tab.
browser.webProgress;
}
browser.loadURI(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(BROWSER_NEW_TAB_URL);
FullZoom.onLocationChange(tabURI, false, browser);
},
_createBrowser({
createBrowser({
isPreloadBrowser,
name,
nextTabParentId,
@ -2155,8 +2075,7 @@ window._gBrowser = {
}
}
let { uriIsAboutBlank, remoteType, usingPreloadedContent } =
aTab._browserParams;
let { uriIsAboutBlank, remoteType, usingPreloadedContent } = aTab._browserParams;
delete aTab._browserParams;
delete aTab._cachedCurrentURI;
@ -2551,7 +2470,7 @@ window._gBrowser = {
!PrivateBrowsingUtils.isWindowPrivate(window) &&
!recordExecution &&
!replayExecution) {
b = this._getPreloadedBrowser();
b = NewTabPagePreloading.getPreloadedBrowser(window);
if (b) {
usingPreloadedContent = true;
}
@ -2559,7 +2478,7 @@ window._gBrowser = {
if (!b) {
// No preloaded browser found, create one.
b = this._createBrowser({
b = this.createBrowser({
remoteType,
uriIsAboutBlank,
userContextId,
@ -4713,8 +4632,8 @@ window._gBrowser = {
// Preloaded browsers do not actually have any tabs. If one crashes,
// it should be released and removed.
if (browser === this._preloadedBrowser) {
this.removePreloadedBrowser();
if (browser === this.preloadedBrowser) {
NewTabPagePreloading.removePreloadedBrowser(window);
return;
}

View File

@ -1095,7 +1095,7 @@
// session restore), preload the next about:newtab if we don't
// already have a preloaded browser.
if (tab.linkedPanel) {
gBrowser._createPreloadBrowser();
NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
}
]]></body>
</method>

View File

@ -207,10 +207,7 @@ async function ensureNoPreloadedBrowser(win = window) {
// do this before we disable preloading or changing the new tab
// URL, otherwise _getPreloadedBrowser will return null, despite
// the preloaded browser existing.
let preloaded = win.gBrowser._getPreloadedBrowser();
if (preloaded) {
preloaded.remove();
}
NewTabPagePreloading.removePreloadedBrowser(win);
await SpecialPowers.pushPrefEnv({
set: [["browser.newtab.preload", false]],

View File

@ -7,11 +7,11 @@ const ZOOM_CHANGE_TOPIC = "browser-fullZoom:location-change";
*/
async function checkPreloadedZoom(level, message) {
// Clear up any previous preloaded to test a fresh version
gBrowser.removePreloadedBrowser();
gBrowser._createPreloadBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
// Wait for zoom handling of preloaded
const browser = gBrowser._preloadedBrowser;
const browser = gBrowser.preloadedBrowser;
await new Promise(resolve => Services.obs.addObserver(function obs(subject) {
if (subject === browser) {
Services.obs.removeObserver(obs, ZOOM_CHANGE_TOPIC);
@ -22,7 +22,7 @@ async function checkPreloadedZoom(level, message) {
is(browser.fullZoom, level, message);
// Clean up for other tests
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
}
add_task(async function test_default_zoom() {

View File

@ -30,7 +30,7 @@ async function checkActivityStreamLoads() {
// Run a first time not from a preloaded browser
add_task(async function checkActivityStreamNotPreloadedLoad() {
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
await checkActivityStreamLoads();
});

View File

@ -11,15 +11,15 @@ add_task(async function newtabPreloaded() {
const menuHome = doc.querySelector(`#newTabMode menuitem[value="0"]`);
const menuBlank = doc.querySelector(`#newTabMode menuitem[value="1"]`);
ok(menuHome.selected, "The first item, Home (default), is selected.");
ok(gBrowser._isPreloadingEnabled(), "Default Home allows preloading.");
ok(NewTabPagePreloading.enabled, "Default Home allows preloading.");
dispatchMenuItemCommand(menuBlank);
ok(menuBlank.selected, "The second item, Blank, is selected.");
ok(!gBrowser._isPreloadingEnabled(), "Non-Home prevents preloading.");
ok(!NewTabPagePreloading.enabled, "Non-Home prevents preloading.");
dispatchMenuItemCommand(menuHome);
ok(menuHome.selected, "The first item, Home, is selected again.");
ok(gBrowser._isPreloadingEnabled(), "Default Home allows preloading again.");
ok(NewTabPagePreloading.enabled, "Default Home allows preloading again.");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -228,12 +228,12 @@ add_task(async function test_preload_crash() {
}
// Release any existing preloaded browser
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
// Create a fresh preloaded browser
gBrowser._createPreloadBrowser();
NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
await BrowserTestUtils.crashBrowser(gBrowser._preloadedBrowser, false);
await BrowserTestUtils.crashBrowser(gBrowser.preloadedBrowser, false);
Assert.ok(!gBrowser._preloadedBrowser);
Assert.ok(!gBrowser.preloadedBrowser);
});

View File

@ -145,7 +145,7 @@ function constructOnePageCmdLine(aURL) {
}
add_task(async function setup() {
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
await SpecialPowers.pushPrefEnv({"set": [
["browser.startup.homepage", "about:home"],

View File

@ -93,7 +93,7 @@ add_task(async function() {
*/
add_task(async function() {
// Ensure there's no preloaded newtab browser, since that'll not fire a load event.
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
let url = `${TEST_BASE_URL}dummy_page.html#foo`;
gURLBar.value = url;

View File

@ -0,0 +1,93 @@
/* 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");
ChromeUtils.defineModuleGetter(this, "E10SUtils", "resource://gre/modules/E10SUtils.jsm");
XPCOMUtils.defineLazyServiceGetters(this, {
gAboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"],
});
let NewTabPagePreloading = {
get enabled() {
return this.prefEnabled && this.newTabEnabled &&
!gAboutNewTabService.overridden;
},
maybeCreatePreloadedBrowser(window) {
if (!this.enabled || window.gBrowser.preloadedBrowser) {
return;
}
const {gBrowser, gMultiProcessBrowser, BROWSER_NEW_TAB_URL} = window;
let remoteType =
E10SUtils.getRemoteTypeForURI(BROWSER_NEW_TAB_URL, gMultiProcessBrowser);
let browser = gBrowser.createBrowser({ isPreloadBrowser: true, remoteType });
gBrowser.preloadedBrowser = browser;
let panel = gBrowser.getPanel(browser);
gBrowser.tabpanels.appendChild(panel);
if (remoteType != E10SUtils.NOT_REMOTE) {
// For remote browsers, we need to make sure that the webProgress is
// instantiated, otherwise the parent won't get informed about the state
// of the preloaded browser until it gets attached to a tab.
browser.webProgress;
}
browser.loadURI(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(BROWSER_NEW_TAB_URL);
window.FullZoom.onLocationChange(tabURI, false, browser);
},
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) {
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);

View File

@ -46,8 +46,11 @@ with Files("AsanReporter.jsm"):
with Files('AsyncTabSwitcher.jsm'):
BUG_COMPONENT = ('Firefox', 'Tabbed Browser')
with Files('NewTabPagePreloading.jsm'):
BUG_COMPONENT = ('Firefox', 'Tabbed Browser')
with Files("BrowserWindowTracker.jsm"):
BUG_COMPONENT = ("Core", "Networking")
BUG_COMPONENT = ("Firefox", "General")
with Files("*Telemetry.jsm"):
BUG_COMPONENT = ("Toolkit", "Telemetry")
@ -139,6 +142,7 @@ EXTRA_JS_MODULES += [
'HomePage.jsm',
'LaterRun.jsm',
'LiveBookmarkMigrator.jsm',
'NewTabPagePreloading.jsm',
'OpenInTabsUtils.jsm',
'PageActions.jsm',
'PermissionUI.jsm',

View File

@ -15,10 +15,10 @@ add_task(async function(){
// Ensure that the preloaded browser exists, and it's finished loading.
async function ensurePreloaded(gBrowser) {
gBrowser._createPreloadBrowser();
NewTabPagePreloading.maybeCreatePreloadedBrowser(gBrowser.ownerGlobal);
// We cannot use the regular BrowserTestUtils helper for waiting here, since that
// would try to insert the preloaded browser, which would only break things.
await ContentTask.spawn(gBrowser._preloadedBrowser, null, async () => {
await ContentTask.spawn(gBrowser.preloadedBrowser, null, async () => {
await ContentTaskUtils.waitForCondition(() => {
return content.document && content.document.readyState == "complete";
});
@ -74,7 +74,7 @@ add_task(async function(){
BrowserTestUtils.removeTab(tab2);
// Make sure the preload browser does not keep any of the new processes alive.
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
// Since we kept alive all the processes, we can shut down the ones that do
// not host any tabs reliably.
@ -84,7 +84,7 @@ add_task(async function(){
add_task(async function preloaded_state_attribute() {
// Wait for a preloaded browser to exist, use it, and then create another one
await ensurePreloaded(gBrowser);
let preloadedTabState = gBrowser._preloadedBrowser.getAttribute("preloadedState");
let preloadedTabState = gBrowser.preloadedBrowser.getAttribute("preloadedState");
is(preloadedTabState, PRELOADED_STATE, "Sanity check that the first preloaded browser has the correct attribute");
BrowserOpenTab();
@ -94,7 +94,7 @@ add_task(async function preloaded_state_attribute() {
let consumedTabState = gBrowser.selectedBrowser.getAttribute("preloadedState");
is(consumedTabState, CONSUMED_STATE, "The opened tab consumed the preloaded browser and updated the attribute");
preloadedTabState = gBrowser._preloadedBrowser.getAttribute("preloadedState");
preloadedTabState = gBrowser.preloadedBrowser.getAttribute("preloadedState");
is(preloadedTabState, PRELOADED_STATE, "The preloaded browser has the correct attribute");
// Navigate away and check that the attribute has been removed altogether
@ -104,6 +104,6 @@ add_task(async function preloaded_state_attribute() {
// Remove tabs and preloaded browsers
BrowserTestUtils.removeTab(gBrowser.selectedTab);
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
});

View File

@ -88,22 +88,24 @@ class TestMemoryUsage(AwsyTestCase):
"""
self.logger.info("closing preloaded browser")
script = """
if (window.NewTabPagePreloading) {
return NewTabPagePreloading.removePreloadedBrowser(window);
}
if ("removePreloadedBrowser" in gBrowser) {
return gBrowser.removePreloadedBrowser();
} else {
return "gBrowser.removePreloadedBrowser not available";
}
return "gBrowser.removePreloadedBrowser not available";
"""
try:
result = self.marionette.execute_script(script,
script_timeout=180000)
except JavascriptException, e:
self.logger.error("gBrowser.removePreloadedBrowser() JavaScript error: %s" % e)
self.logger.error("removePreloadedBrowser() JavaScript error: %s" % e)
except ScriptTimeoutException:
self.logger.error("gBrowser.removePreloadedBrowser() timed out")
self.logger.error("removePreloadedBrowser() timed out")
except Exception:
self.logger.error(
"gBrowser.removePreloadedBrowser() Unexpected error: %s" % sys.exc_info()[0])
"removePreloadedBrowser() Unexpected error: %s" % sys.exc_info()[0])
else:
if result:
self.logger.info(result)

View File

@ -929,7 +929,7 @@ Tester.prototype = {
BackgroundPageThumbs._destroy();
if (window.gBrowser) {
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
}
}

View File

@ -52,8 +52,8 @@ let tabFinder = {
this._map.set(id, browser);
}
}
if (tabbrowser._preloadedBrowser) {
let browser = tabbrowser._preloadedBrowser;
if (tabbrowser.preloadedBrowser) {
let browser = tabbrowser.preloadedBrowser;
if (browser.outerWindowID)
this._map.set(browser.outerWindowID, browser);
}

View File

@ -81,7 +81,7 @@ add_task(async function test_support_ntp_colors() {
registerCleanupFunction(() => {
SpecialPowers.clearUserPref("browser.newtab.preload");
});
gBrowser.removePreloadedBrowser();
NewTabPagePreloading.removePreloadedBrowser(window);
for (let url of ["about:newtab", "about:home", "about:welcome"]) {
info("Opening url: " + url);
await BrowserTestUtils.withNewTab({gBrowser, url}, async browser => {

View File

@ -145,7 +145,7 @@ add_task(async function test_per_window_ntp_theme() {
extension.onMessage("check-window", async ({theme, isBrightText, winId}) => {
let win = Services.wm.getOuterWindowWithId(winId);
win.gBrowser.removePreloadedBrowser();
win.NewTabPagePreloading.removePreloadedBrowser(win);
for (let url of ["about:newtab", "about:home", "about:welcome"]) {
info("Opening url: " + url);
await BrowserTestUtils.withNewTab({gBrowser: win.gBrowser, url}, async browser => {