Bug 1619992 - Split AboutNewTabService into AboutNewTabParentService and AboutNewTabChildService. r=perftest-reviewers,Mardak,sparky

This patch does the following:
* Moves most logic for initiating about:home / about:newtab into AboutNewTab.jsm
* Makes AboutNewTab the API surface for overriding the default about:newtab URLs.
* Reduces the surface of nsIAboutNewTabService, and makes the properties read-only
* Splits the remaining code in the nsIAboutNewTabService into an implementation for
  the parent process, and one for content processes.

This split will hopefully help reduce confusion about which code in
AboutNewTabService is running in which process.

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

--HG--
rename : browser/components/newtab/test/xpcshell/test_AboutNewTabService.js => browser/components/newtab/test/xpcshell/test_AboutNewTab.js
extra : moz-landing-system : lando
This commit is contained in:
Mike Conley 2020-03-11 01:25:31 +00:00
Родитель 8371d74f49
Коммит 4cb59612e3
29 изменённых файлов: 489 добавлений и 532 удалений

Просмотреть файл

@ -15,7 +15,7 @@ ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
// lazy module getters
XPCOMUtils.defineLazyModuleGetters(this, {
AboutNewTabStartupRecorder: "resource:///modules/AboutNewTabService.jsm",
AboutNewTab: "resource:///modules/AboutNewTab.jsm",
AddonManager: "resource://gre/modules/AddonManager.jsm",
AMTelemetry: "resource://gre/modules/AddonManager.jsm",
NewTabPagePreloading: "resource:///modules/NewTabPagePreloading.jsm",
@ -258,10 +258,6 @@ XPCOMUtils.defineLazyServiceGetters(this, {
"nsIURIClassifier",
],
Favicons: ["@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
gAboutNewTabService: [
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService",
],
gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"],
gSerializationHelper: [
"@mozilla.org/network/serialization-helper;1",
@ -1920,11 +1916,6 @@ var gBrowserInit = {
true
);
// Get the service so that it initializes and registers listeners for new
// tab pages in order to be ready for any early-loading about:newtab pages,
// e.g., start/home page, command line / startup uris to load, sessionstore
gAboutNewTabService.QueryInterface(Ci.nsISupports);
this._handleURIToLoad();
Services.obs.addObserver(gIdentityHandler, "perm-changed");
@ -2345,7 +2336,7 @@ var gBrowserInit = {
// If the given URI is different from the homepage, we want to load it.
if (uri != defaultArgs) {
AboutNewTabStartupRecorder.noteNonDefaultStartup();
AboutNewTab.noteNonDefaultStartup();
if (uri instanceof Ci.nsIArray) {
// Transform the nsIArray of nsISupportsString's into a JS Array of

Просмотреть файл

@ -3,13 +3,13 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let gNewTabService = Cc[
"@mozilla.org/browser/aboutnewtab-service;1"
].getService(Ci.nsIAboutNewTabService);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
async function doTest(isPrivate) {
let win = await BrowserTestUtils.openNewBrowserWindow({ private: isPrivate });
let defaultURL = gNewTabService.newTabURL;
let defaultURL = AboutNewTab.newTabURL;
let newTabURL;
let mode;
let testURL = "http://example.com/";
@ -30,8 +30,8 @@ async function doTest(isPrivate) {
);
// Set the custom newtab url
gNewTabService.newTabURL = testURL;
is(gNewTabService.newTabURL, testURL, "Custom newtab url is set");
AboutNewTab.newTabURL = testURL;
is(AboutNewTab.newTabURL, testURL, "Custom newtab url is set");
// Open a newtab after setting the custom newtab url
await openNewTab(win, testURL);
@ -42,8 +42,8 @@ async function doTest(isPrivate) {
);
// Clear the custom url.
gNewTabService.resetNewTabURL();
is(gNewTabService.newTabURL, defaultURL, "No custom newtab url is set");
AboutNewTab.resetNewTabURL();
is(AboutNewTab.newTabURL, defaultURL, "No custom newtab url is set");
win.gBrowser.removeTab(win.gBrowser.selectedTab);
win.gBrowser.removeTab(win.gBrowser.selectedTab);
@ -52,7 +52,7 @@ async function doTest(isPrivate) {
add_task(async function test_newTabService() {
// check whether any custom new tab url has been configured
ok(!gNewTabService.overridden, "No custom newtab url is set");
ok(!AboutNewTab.newTabURLOverridden, "No custom newtab url is set");
// test normal mode
await doTest(false);

Просмотреть файл

@ -1,5 +1,10 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PlacesUtils",
@ -230,7 +235,7 @@ function promiseOpenAndLoadWindow(aOptions, aWaitForDelayedStartup = false) {
async function whenNewTabLoaded(aWindow, aCallback) {
aWindow.BrowserOpenTab();
let expectedURL = aboutNewTabService.newTabURL;
let expectedURL = AboutNewTab.newTabURL;
let browser = aWindow.gBrowser.selectedBrowser;
let loadPromise = BrowserTestUtils.browserLoaded(browser, false, expectedURL);
let alreadyLoaded = await SpecialPowers.spawn(browser, [expectedURL], url => {

Просмотреть файл

@ -1,6 +1,7 @@
"use strict";
XPCOMUtils.defineLazyModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.jsm",
PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
@ -246,13 +247,10 @@ async function ensureNoPreloadedBrowser(win = window) {
set: [["browser.newtab.preload", false]],
});
let aboutNewTabService = Cc[
"@mozilla.org/browser/aboutnewtab-service;1"
].getService(Ci.nsIAboutNewTabService);
aboutNewTabService.newTabURL = "about:blank";
AboutNewTab.newTabURL = "about:blank";
registerCleanupFunction(() => {
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
});
}

Просмотреть файл

@ -13,6 +13,7 @@ var { XPCOMUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.jsm",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
ContextualIdentityService:
"resource://gre/modules/ContextualIdentityService.jsm",
@ -21,13 +22,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ShellService: "resource:///modules/ShellService.jsm",
});
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
);
XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
Components.Constructor(
"@mozilla.org/referrer-info;1",
@ -42,7 +36,7 @@ Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
if (
!PrivateBrowsingUtils.permanentPrivateBrowsing &&
!aboutNewTabService.overridden
!AboutNewTab.newTabURLOverridden
) {
return "about:privatebrowsing";
}
@ -61,12 +55,12 @@ Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
if (
!privateAllowed &&
(extensionControlled ||
aboutNewTabService.newTabURL.startsWith("moz-extension://"))
AboutNewTab.newTabURL.startsWith("moz-extension://"))
) {
return "about:privatebrowsing";
}
}
return aboutNewTabService.newTabURL;
return AboutNewTab.newTabURL;
},
});
@ -655,7 +649,7 @@ function openLinkIn(url, where, params) {
focusUrlBar =
!loadInBackground &&
w.isBlankPageURL(url) &&
!aboutNewTabService.willNotifyUser;
!AboutNewTab.willNotifyUser;
let tabUsedForLoad = w.gBrowser.loadOneTab(url, {
referrerInfo: aReferrerInfo,

Просмотреть файл

@ -26,6 +26,12 @@ ChromeUtils.defineModuleGetter(
"resource:///modules/CustomizableUI.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"PushService",
@ -1209,6 +1215,7 @@ BrowserGlue.prototype = {
}
SaveToPocket.init();
Services.obs.notifyObservers(null, "browser-ui-startup-complete");
},
@ -1571,6 +1578,8 @@ BrowserGlue.prototype = {
// the first browser window has finished initializing
_onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
AboutNewTab.init();
TabCrashHandler.init();
ProcessHangMonitor.init();

Просмотреть файл

@ -18,12 +18,10 @@ ChromeUtils.defineModuleGetter(
"ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
ChromeUtils.defineModuleGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
const STORE_TYPE = "url_overrides";
@ -44,10 +42,10 @@ XPCOMUtils.defineLazyGetter(this, "newTabPopup", () => {
learnMoreMessageId: "newTabControlled.learnMore",
learnMoreLink: "extension-home",
onObserverAdded() {
aboutNewTabService.willNotifyUser = true;
AboutNewTab.willNotifyUser = true;
},
onObserverRemoved() {
aboutNewTabService.willNotifyUser = false;
AboutNewTab.willNotifyUser = false;
},
async beforeDisableAddon(popup, win) {
// ExtensionControlledPopup will disable the add-on once this function completes.
@ -62,7 +60,7 @@ XPCOMUtils.defineLazyGetter(this, "newTabPopup", () => {
Services.obs.addObserver(
{
async observe() {
await replaceUrlInTab(gBrowser, tab, aboutNewTabService.newTabURL);
await replaceUrlInTab(gBrowser, tab, AboutNewTab.newTabURL);
// Now that the New Tab is loading, try to open the popup again. This
// will only open the popup if a new extension is controlling the New Tab.
popup.open();
@ -90,7 +88,7 @@ function setNewTabURL(extensionId, url) {
Services.prefs.clearUserPref(NEW_TAB_EXTENSION_CONTROLLED);
}
if (url) {
aboutNewTabService.newTabURL = url;
AboutNewTab.newTabURL = url;
}
}
@ -168,7 +166,7 @@ this.urlOverrides = class extends ExtensionAPI {
STORE_TYPE,
NEW_TAB_SETTING_NAME,
url,
() => aboutNewTabService.newTabURL
() => AboutNewTab.newTabURL
);
// Set the newTabURL to the current value of the setting.

Просмотреть файл

@ -7,6 +7,9 @@ const { GlobalManager } = ChromeUtils.import(
const { ExtensionPermissions } = ChromeUtils.import(
"resource://gre/modules/ExtensionPermissions.jsm"
);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
const NEWTAB_PRIVATE_ALLOWED = "browser.newtab.privateAllowed";
const NEWTAB_EXTENSION_CONTROLLED = "browser.newtab.extensionControlled";
@ -35,7 +38,7 @@ function verifyPrefSettings(controlled, allowed) {
if (controlled) {
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI),
"Newtab url is overridden by the extension."
);
}

Просмотреть файл

@ -8,11 +8,10 @@ ChromeUtils.defineModuleGetter(
"ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
ChromeUtils.defineModuleGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
const NEWTAB_URI_1 = "webext-newtab-1.html";
@ -720,15 +719,15 @@ add_task(async function test_overriding_newtab_incognito_not_allowed() {
is(win.gURLBar.value, "", "newtab not used in private window");
// Verify setting the pref directly doesn't bypass permissions.
let origUrl = aboutNewTabService.newTabURL;
aboutNewTabService.newTabURL = url;
let origUrl = AboutNewTab.newTabURL;
AboutNewTab.newTabURL = url;
newTabOpened = waitForNewTab();
win.BrowserOpenTab();
await newTabOpened;
is(win.gURLBar.value, "", "directly set newtab not used in private window");
aboutNewTabService.newTabURL = origUrl;
AboutNewTab.newTabURL = origUrl;
await extension.unload();
await BrowserTestUtils.closeWindow(win);

Просмотреть файл

@ -10,6 +10,10 @@ const { AddonManager } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
// Lazy load to avoid having Services.appinfo cached first.
ChromeUtils.defineModuleGetter(
this,
@ -19,13 +23,6 @@ ChromeUtils.defineModuleGetter(
const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
);
AddonTestUtils.init(this);
// Allow for unsigned addons.
@ -52,7 +49,7 @@ add_task(async function test_settings_modules_not_loaded() {
});
add_task(async function test_settings_validated() {
let defaultNewTab = aboutNewTabService.newTabURL;
let defaultNewTab = AboutNewTab.newTabURL;
equal(defaultNewTab, "about:newtab", "Newtab url is default.");
let defaultHomepageURL = HomePage.get();
equal(defaultHomepageURL, "about:home", "Home page url is default.");
@ -80,7 +77,7 @@ add_task(async function test_settings_validated() {
"Home page url is extension controlled."
);
ok(
aboutNewTabService.newTabURL.endsWith("/newtab"),
AboutNewTab.newTabURL.endsWith("/newtab"),
"newTabURL is extension controlled."
);
@ -100,16 +97,12 @@ add_task(async function test_settings_validated() {
await prefChanged;
equal(HomePage.get(), defaultHomepageURL, "Home page url is default.");
equal(
aboutNewTabService.newTabURL,
defaultNewTab,
"newTabURL is reset to default."
);
equal(AboutNewTab.newTabURL, defaultNewTab, "newTabURL is reset to default.");
await AddonTestUtils.promiseShutdownManager();
});
add_task(async function test_settings_validated_safemode() {
let defaultNewTab = aboutNewTabService.newTabURL;
let defaultNewTab = AboutNewTab.newTabURL;
equal(defaultNewTab, "about:newtab", "Newtab url is default.");
let defaultHomepageURL = HomePage.get();
equal(defaultHomepageURL, "about:home", "Home page url is default.");
@ -121,7 +114,7 @@ add_task(async function test_settings_validated_safemode() {
`Home page url is default ${postfix}.`
);
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
defaultNewTab,
`newTabURL is default ${postfix}.`
);
@ -134,7 +127,7 @@ add_task(async function test_settings_validated_safemode() {
`Home page url is extension controlled ${postfix}.`
);
ok(
aboutNewTabService.newTabURL.endsWith("/newtab"),
AboutNewTab.newTabURL.endsWith("/newtab"),
`newTabURL is extension controlled ${postfix}.`
);
}

Просмотреть файл

@ -17,11 +17,8 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/AddonManager.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
const { AddonTestUtils } = ChromeUtils.import(
@ -46,7 +43,7 @@ function awaitEvent(eventName) {
});
}
const DEFAULT_NEW_TAB_URL = aboutNewTabService.newTabURL;
const DEFAULT_NEW_TAB_URL = AboutNewTab.newTabURL;
add_task(async function test_multiple_extensions_overriding_newtab_page() {
const NEWTAB_URI_2 = "webext-newtab-1.html";
@ -159,7 +156,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
let ext3 = ExtensionTestUtils.loadExtension(extObj);
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL is set to the default."
);
@ -168,21 +165,17 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await ext1.startup();
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL is still set to the default."
);
await checkNewTabPageOverride(
ext1,
aboutNewTabService.newTabURL,
NOT_CONTROLLABLE
);
await checkNewTabPageOverride(ext1, AboutNewTab.newTabURL, NOT_CONTROLLABLE);
verifyNewTabSettings(ext1, NOT_CONTROLLABLE);
await ext2.startup();
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_2),
"newTabURL is overridden by the second extension."
);
await checkNewTabPageOverride(ext1, NEWTAB_URI_2, CONTROLLED_BY_OTHER);
@ -205,15 +198,11 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await addon.disable();
await disabledPromise;
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL url is reset to the default after second extension is disabled."
);
await checkNewTabPageOverride(
ext1,
aboutNewTabService.newTabURL,
NOT_CONTROLLABLE
);
await checkNewTabPageOverride(ext1, AboutNewTab.newTabURL, NOT_CONTROLLABLE);
verifyNewTabSettings(ext1, NOT_CONTROLLABLE);
// Re-enable the second extension.
@ -221,7 +210,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await addon.enable();
await enabledPromise;
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_2),
"newTabURL is overridden by the second extension."
);
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
@ -229,7 +218,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await ext1.unload();
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_2),
"newTabURL is still overridden by the second extension."
);
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
@ -237,7 +226,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await ext3.startup();
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_3),
"newTabURL is overridden by the third extension."
);
await checkNewTabPageOverride(ext2, NEWTAB_URI_3, CONTROLLED_BY_OTHER);
@ -248,7 +237,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await addon.disable();
await disabledPromise;
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_3),
"newTabURL is still overridden by the third extension."
);
await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
@ -259,7 +248,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await addon.enable();
await enabledPromise;
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_3),
"newTabURL is still overridden by the third extension."
);
await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
@ -267,7 +256,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await ext3.unload();
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI_2),
"newTabURL reverts to being overridden by the second extension."
);
await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
@ -275,7 +264,7 @@ add_task(async function test_multiple_extensions_overriding_newtab_page() {
await ext2.unload();
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL url is reset to the default."
);
@ -300,7 +289,7 @@ add_task(async function test_temporary_installation() {
const PAGE2 = "page2.html";
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL is set to the default."
);
@ -321,7 +310,7 @@ add_task(async function test_temporary_installation() {
await permanent.startup();
ok(
aboutNewTabService.newTabURL.endsWith(PAGE1),
AboutNewTab.newTabURL.endsWith(PAGE1),
"newTabURL is overridden by permanent extension."
);
@ -339,7 +328,7 @@ add_task(async function test_temporary_installation() {
await temporary.startup();
ok(
aboutNewTabService.newTabURL.endsWith(PAGE2),
AboutNewTab.newTabURL.endsWith(PAGE2),
"newTabURL is overridden by temporary extension."
);
@ -347,14 +336,14 @@ add_task(async function test_temporary_installation() {
await permanent.awaitStartup();
ok(
aboutNewTabService.newTabURL.endsWith(PAGE1),
AboutNewTab.newTabURL.endsWith(PAGE1),
"newTabURL is back to the value set by permanent extension."
);
await permanent.unload();
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DEFAULT_NEW_TAB_URL,
"newTabURL is set back to the default."
);

Просмотреть файл

@ -2,11 +2,8 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
const { AddonTestUtils } = ChromeUtils.import(
@ -87,9 +84,9 @@ add_task(async function test_url_overrides_newtab_update() {
},
});
let defaultNewTabURL = aboutNewTabService.newTabURL;
let defaultNewTabURL = AboutNewTab.newTabURL;
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
defaultNewTabURL,
`Default newtab url is ${defaultNewTabURL}.`
);
@ -102,7 +99,7 @@ add_task(async function test_url_overrides_newtab_update() {
"The installed addon has the expected version."
);
ok(
aboutNewTabService.newTabURL.endsWith(NEWTAB_URI),
AboutNewTab.newTabURL.endsWith(NEWTAB_URI),
"Newtab url is overridden by the extension."
);
@ -119,7 +116,7 @@ add_task(async function test_url_overrides_newtab_update() {
"The updated addon has the expected version."
);
equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
defaultNewTabURL,
"Newtab url reverted to the default after update."
);

Просмотреть файл

@ -1,4 +1,4 @@
/*
/**
* 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/.
@ -6,11 +6,31 @@
"use strict";
const EXPORTED_SYMBOLS = ["AboutNewTabStubService"];
/**
* The nsIAboutNewTabService is accessed by the AboutRedirector anytime
* about:home, about:newtab or about:welcome are requested. The primary
* job of an nsIAboutNewTabService is to tell the AboutRedirector what
* resources to actually load for those requests.
*
* The nsIAboutNewTabService is not involved when the user has overridden
* the default about:home or about:newtab pages.
*
* There are two implementations of this service - one for the parent
* process, and one for content processes. Each one has some secondary
* responsibilties that are process-specific.
*
* The need for two implementations is an unfortunate consequence of how
* document loading and process redirection for about: pages currently
* works in Gecko. The commonalities between the two implementations has
* been put into an abstract base class.
*/
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
@ -18,124 +38,132 @@ const { E10SUtils } = ChromeUtils.import(
"resource://gre/modules/E10SUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
/**
* BEWARE: Do not add variables for holding state in the global scope.
* Any state variables should be properties of the appropriate class
* below. This is to avoid confusion where the state is set in one process,
* but not in another.
*
* Constants are fine in the global scope.
*/
const PREF_SEPARATE_ABOUT_WELCOME = "browser.aboutwelcome.enabled";
const SEPARATE_ABOUT_WELCOME_URL =
"resource://activity-stream/aboutwelcome/aboutwelcome.html";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"isSeparateAboutWelcome",
PREF_SEPARATE_ABOUT_WELCOME,
false
);
const TOPIC_APP_QUIT = "quit-application-granted";
const TOPIC_CONTENT_DOCUMENT_INTERACTIVE = "content-document-interactive";
const ABOUT_URL = "about:newtab";
const BASE_URL = "resource://activity-stream/";
const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]);
const IS_MAIN_PROCESS =
Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
const IS_PRIVILEGED_PROCESS =
Services.appinfo.remoteType === E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
const PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS =
"browser.tabs.remote.separatePrivilegedContentProcess";
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
function AboutNewTabService() {
Services.obs.addObserver(this, TOPIC_APP_QUIT);
Services.prefs.addObserver(
PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS,
this
);
if (!IS_RELEASE_OR_BETA) {
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
/**
* This is an abstract base class for the nsIAboutNewTabService
* implementations that has some common methods and properties.
*/
class BaseAboutNewTabService {
constructor() {
if (!AppConstants.RELEASE_OR_BETA) {
XPCOMUtils.defineLazyPreferenceGetter(
this,
"activityStreamDebug",
PREF_ACTIVITY_STREAM_DEBUG,
false
);
} else {
this.activityStreamDebug = false;
}
XPCOMUtils.defineLazyPreferenceGetter(
this,
"isSeparateAboutWelcome",
PREF_SEPARATE_ABOUT_WELCOME,
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"privilegedAboutProcessEnabled",
PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS,
false
);
this.classID = Components.ID("{cb36c925-3adc-49b3-b720-a5cc49d8a40e}");
this.QueryInterface = ChromeUtils.generateQI([
Ci.nsIAboutNewTabService,
Ci.nsIObserver,
]);
}
// More initialization happens here
this.toggleActivityStream(true);
this.initialized = true;
/**
* Returns the default URL.
*
* This URL depends on various activity stream prefs. Overriding
* the newtab page has no effect on the result of this function.
*/
get defaultURL() {
// Generate the desired activity stream resource depending on state, e.g.,
// "resource://activity-stream/prerendered/activity-stream.html"
// "resource://activity-stream/prerendered/activity-stream-debug.html"
// "resource://activity-stream/prerendered/activity-stream-noscripts.html"
return [
"resource://activity-stream/prerendered/",
"activity-stream",
// Debug version loads dev scripts but noscripts separately loads scripts
this.activityStreamDebug && !this.privilegedAboutProcessEnabled
? "-debug"
: "",
this.privilegedAboutProcessEnabled ? "-noscripts" : "",
".html",
].join("");
}
if (IS_MAIN_PROCESS) {
AboutNewTab.init();
} else if (IS_PRIVILEGED_PROCESS) {
Services.obs.addObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
/*
* Returns the about:welcome URL
*
* This is calculated in the same way the default URL is.
*/
get welcomeURL() {
if (this.isSeparateAboutWelcome) {
return SEPARATE_ABOUT_WELCOME_URL;
}
return this.defaultURL;
}
}
/*
* A service that allows for the overriding, at runtime, of the newtab page's url.
*
* There is tight coupling with browser/about/AboutRedirector.cpp.
*
* 1. Browser chrome access:
*
* When the user issues a command to open a new tab page, usually clicking a button
* in the browser chrome or using shortcut keys, the browser chrome code invokes the
* service to obtain the newtab URL. It then loads that URL in a new tab.
*
* When not overridden, the default URL emitted by the service is "about:newtab".
* When overridden, it returns the overriden URL.
*
* 2. Redirector Access:
*
* When the URL loaded is about:newtab, the default behavior, or when entered in the
* URL bar, the redirector is hit. The service is then called to return the
* appropriate activity stream url based on prefs.
*
* NOTE: "about:newtab" will always result in a default newtab page, and never an overridden URL.
*
* Access patterns:
*
* The behavior is different when accessing the service via browser chrome or via redirector
* largely to maintain compatibility with expectations of add-on developers.
*
* Loading a chrome resource, or an about: URL in the redirector with either the
* LOAD_NORMAL or LOAD_REPLACE flags yield unexpected behaviors, so a roundtrip
* to the redirector from browser chrome is avoided.
/**
* The child-process implementation of nsIAboutNewTabService,
* which also does the work of loading scripts from the ScriptPreloader
* cache when using the privileged about content process.
*/
AboutNewTabService.prototype = {
_newTabURL: ABOUT_URL,
_activityStreamEnabled: false,
_activityStreamDebug: false,
_privilegedAboutContentProcess: false,
_overridden: false,
willNotifyUser: false,
classID: Components.ID("{dfcd2adc-7867-4d3a-ba70-17501f208142}"),
QueryInterface: ChromeUtils.generateQI([
Ci.nsIAboutNewTabService,
Ci.nsIObserver,
]),
class AboutNewTabChildService extends BaseAboutNewTabService {
constructor() {
super();
if (this.privilegedAboutProcessEnabled) {
Services.obs.addObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
Services.obs.addObserver(this, TOPIC_APP_QUIT);
}
}
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data === PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS) {
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(
PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS
);
this.notifyChange();
} else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) {
this._activityStreamDebug = Services.prefs.getBoolPref(
PREF_ACTIVITY_STREAM_DEBUG,
false
);
this.notifyChange();
}
case TOPIC_APP_QUIT: {
Services.obs.removeObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
Services.obs.removeObserver(this, TOPIC_APP_QUIT);
break;
}
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE: {
if (!this.privilegedAboutProcessEnabled || !IS_PRIVILEGED_PROCESS) {
return;
}
const win = subject.defaultView;
// It seems like "content-document-interactive" is triggered multiple
@ -144,7 +172,7 @@ AboutNewTabService.prototype = {
// whereas the remaining ones seem to be proxied objects.
// https://searchfox.org/mozilla-central/rev/d2966246905102b36ef5221b0e3cbccf7ea15a86/devtools/server/actors/object.js#100-102
if (win === null) {
break;
return;
}
// We use win.location.pathname instead of win.location.toString()
@ -154,19 +182,19 @@ AboutNewTabService.prototype = {
// by the view-source:// scheme, so we should probably just bail out
// and do nothing.
if (!ACTIVITY_STREAM_PAGES.has(win.location.pathname)) {
break;
return;
}
// Bail out early for separate about:welcome URL
if (
isSeparateAboutWelcome &&
this.isSeparateAboutWelcome &&
win.location.pathname.includes("welcome")
) {
break;
return;
}
const onLoaded = () => {
const debugString = this._activityStreamDebug ? "-dev" : "";
const debugString = this.activityStreamDebug ? "-dev" : "";
// This list must match any similar ones in render-activity-stream-html.js.
const scripts = [
@ -197,174 +225,19 @@ AboutNewTabService.prototype = {
win.addEventListener("unload", onUnloaded, { once: true });
break;
}
case TOPIC_APP_QUIT:
this.uninit();
if (IS_MAIN_PROCESS) {
AboutNewTab.uninit();
} else if (IS_PRIVILEGED_PROCESS) {
Services.obs.removeObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
}
break;
}
},
notifyChange() {
Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
},
/**
* React to changes to the activity stream being enabled or not.
*
* This will only act if there is a change of state and if not overridden.
*
* @returns {Boolean} Returns if there has been a state change
*
* @param {Boolean} stateEnabled activity stream enabled state to set to
* @param {Boolean} forceState force state change
*/
toggleActivityStream(stateEnabled, forceState = false) {
if (
!forceState &&
(this.overridden || stateEnabled === this.activityStreamEnabled)
) {
// exit there is no change of state
return false;
}
if (stateEnabled) {
this._activityStreamEnabled = true;
} else {
this._activityStreamEnabled = false;
}
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(
PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS
);
if (!IS_RELEASE_OR_BETA) {
this._activityStreamDebug = Services.prefs.getBoolPref(
PREF_ACTIVITY_STREAM_DEBUG,
false
);
}
this._newtabURL = ABOUT_URL;
return true;
},
/*
* Returns the default URL.
*
* This URL depends on various activity stream prefs. Overriding
* the newtab page has no effect on the result of this function.
*/
get defaultURL() {
// Generate the desired activity stream resource depending on state, e.g.,
// "resource://activity-stream/prerendered/activity-stream.html"
// "resource://activity-stream/prerendered/activity-stream-debug.html"
// "resource://activity-stream/prerendered/activity-stream-noscripts.html"
return [
"resource://activity-stream/prerendered/",
"activity-stream",
// Debug version loads dev scripts but noscripts separately loads scripts
this._activityStreamDebug && !this._privilegedAboutContentProcess
? "-debug"
: "",
this._privilegedAboutContentProcess ? "-noscripts" : "",
".html",
].join("");
},
/*
* Returns the about:welcome URL
*
* This is calculated in the same way the default URL is.
*/
get welcomeURL() {
if (isSeparateAboutWelcome) {
return SEPARATE_ABOUT_WELCOME_URL;
}
return this.defaultURL;
},
get newTabURL() {
return this._newTabURL;
},
set newTabURL(aNewTabURL) {
let newTabURL = aNewTabURL.trim();
if (newTabURL === ABOUT_URL) {
// avoid infinite redirects in case one sets the URL to about:newtab
this.resetNewTabURL();
return;
} else if (newTabURL === "") {
newTabURL = "about:blank";
}
this.toggleActivityStream(false);
this._newTabURL = newTabURL;
this._overridden = true;
this.notifyChange();
},
get overridden() {
return this._overridden;
},
get activityStreamEnabled() {
return this._activityStreamEnabled;
},
get activityStreamDebug() {
return this._activityStreamDebug;
},
resetNewTabURL() {
this._overridden = false;
this._newTabURL = ABOUT_URL;
this.toggleActivityStream(true, true);
this.notifyChange();
},
uninit() {
if (!this.initialized) {
return;
}
Services.obs.removeObserver(this, TOPIC_APP_QUIT);
Services.prefs.removeObserver(
PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS,
this
);
if (!IS_RELEASE_OR_BETA) {
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
}
this.initialized = false;
},
};
}
}
/**
* We split out the definition of AboutNewTabStartupRecorder from
* AboutNewTabService to avoid initializing the AboutNewTabService
* unnecessarily early when we just want to record some startup
* data.
* The AboutNewTabStubService is a function called in both the main and
* content processes when trying to get at the nsIAboutNewTabService. This
* function does the job of choosing the appropriate implementation of
* nsIAboutNewTabService for the process type.
*/
const AboutNewTabStartupRecorder = {
_alreadyRecordedTopsitesPainted: false,
_nonDefaultStartup: false,
noteNonDefaultStartup() {
this._nonDefaultStartup = true;
},
maybeRecordTopsitesPainted(timestamp) {
if (this._alreadyRecordedTopsitesPainted || this._nonDefaultStartup) {
return;
}
const SCALAR_KEY = "timestamps.about_home_topsites_first_paint";
let startupInfo = Services.startup.getStartupInfo();
let processStartTs = startupInfo.process.getTime();
let delta = Math.round(timestamp - processStartTs);
Services.telemetry.scalarSet(SCALAR_KEY, delta);
this._alreadyRecordedTopsitesPainted = true;
},
};
const EXPORTED_SYMBOLS = ["AboutNewTabService", "AboutNewTabStartupRecorder"];
function AboutNewTabStubService() {
if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT) {
return new BaseAboutNewTabService();
}
return new AboutNewTabChildService();
}

Просмотреть файл

@ -9,6 +9,6 @@ Classes = [
'cid': '{dfcd2adc-7867-4d3a-ba70-17501f208142}',
'contract_ids': ['@mozilla.org/browser/aboutnewtab-service;1'],
'jsm': 'resource:///modules/AboutNewTabService.jsm',
'constructor': 'AboutNewTabService',
'constructor': 'AboutNewTabStubService',
},
]

Просмотреть файл

@ -4,9 +4,12 @@
"use strict";
const { AboutNewTab } = ChromeUtils.import(
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
const { RemotePages } = ChromeUtils.import(
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm"
);
@ -202,7 +205,8 @@ this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
createChannel() {
// Receive AboutNewTab's Remote Pages instance, if it exists, on override
const channel =
this.pageURL === ABOUT_NEW_TAB_URL && AboutNewTab.override(true);
this.pageURL === ABOUT_NEW_TAB_URL &&
AboutNewTab.overridePageListener(true);
this.channel =
channel || new RemotePages([ABOUT_HOME_URL, ABOUT_NEW_TAB_URL]);
this.channel.addMessageListener("RemotePage:Init", this.onNewTabInit);

Просмотреть файл

@ -31,8 +31,8 @@ ChromeUtils.defineModuleGetter(
);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTabStartupRecorder",
"resource:///modules/AboutNewTabService.jsm"
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
ChromeUtils.defineModuleGetter(
this,
@ -77,10 +77,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
XPCOMUtils.defineLazyServiceGetters(this, {
gUUIDGenerator: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
aboutNewTabService: [
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService",
],
});
const ACTIVITY_STREAM_ID = "activity-stream";
@ -831,11 +827,11 @@ this.TelemetryFeed = class TelemetryFeed {
// If so, classify them.
if (
Services.prefs.getBoolPref("browser.newtabpage.enabled") &&
aboutNewTabService.overridden &&
!aboutNewTabService.newTabURL.startsWith("moz-extension://")
AboutNewTab.newTabURLOverridden &&
!AboutNewTab.newTabURL.startsWith("moz-extension://")
) {
value.newtab_url_category = await this._classifySite(
aboutNewTabService.newTabURL
AboutNewTab.newTabURL
);
newtabAffected = true;
}
@ -1078,7 +1074,7 @@ this.TelemetryFeed = class TelemetryFeed {
!HomePage.overridden &&
Services.prefs.getIntPref("browser.startup.page") === 1
) {
AboutNewTabStartupRecorder.maybeRecordTopsitesPainted(timestamp);
AboutNewTab.maybeRecordTopsitesPainted(timestamp);
}
Object.assign(session.perf, data);

Просмотреть файл

@ -12,52 +12,13 @@
[scriptable, uuid(dfcd2adc-7867-4d3a-ba70-17501f208142)]
interface nsIAboutNewTabService : nsISupports
{
/**
* Returns the url of the resource for the newtab page if not overridden,
* otherwise a string represenation of the new URL.
*/
attribute ACString newTabURL;
/**
* Returns the default URL (local or activity stream depending on pref)
*/
attribute ACString defaultURL;
readonly attribute ACString defaultURL;
/**
* Returns the about:welcome URL.
*/
attribute ACString welcomeURL;
/**
* Returns true if opening the New Tab page will notify the user of a change.
*/
attribute bool willNotifyUser;
/**
* Returns true if the default resource got overridden.
*/
readonly attribute bool overridden;
/**
* Returns true if the default resource is activity stream and isn't
* overridden
*/
readonly attribute bool activityStreamEnabled;
/**
* Returns true if the the debug pref for activity stream is true
*/
readonly attribute bool activityStreamDebug;
/**
* Resets to the default resource and also resets the
* overridden attribute to false.
*/
void resetNewTabURL();
/**
* Records a scalar metric for how long it takes to pain Top Sites, this will
* only record the first timestamp, all the subsequent calls will be ignored.
*/
void maybeRecordTopsitesPainted(in unsigned long long timestamp);
readonly attribute ACString welcomeURL;
};

Просмотреть файл

@ -1,14 +1,11 @@
"use strict";
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
registerCleanupFunction(() => {
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
});
function nextChangeNotificationPromise(aNewURL, testMessage) {
@ -34,10 +31,10 @@ add_task(async function redirector_ignores_override() {
overrideURL,
`newtab page now points to ${overrideURL}`
);
aboutNewTabService.newTabURL = overrideURL;
AboutNewTab.newTabURL = overrideURL;
await notificationPromise;
Assert.ok(aboutNewTabService.overridden, "url has been overridden");
Assert.ok(AboutNewTab.newTabURLOverridden, "url has been overridden");
let tabOptions = {
gBrowser,
@ -84,10 +81,10 @@ add_task(async function override_loads_in_browser() {
overrideURL.trim(),
`newtab page now points to ${overrideURL}`
);
aboutNewTabService.newTabURL = overrideURL;
AboutNewTab.newTabURL = overrideURL;
await notificationPromise;
Assert.ok(aboutNewTabService.overridden, "url has been overridden");
Assert.ok(AboutNewTab.newTabURLOverridden, "url has been overridden");
// simulate a newtab open as a user would
BrowserOpenTab(); // jshint ignore:line
@ -118,10 +115,10 @@ add_task(async function override_blank_loads_in_browser() {
"about:blank",
"newtab page now points to about:blank"
);
aboutNewTabService.newTabURL = overrideURL;
AboutNewTab.newTabURL = overrideURL;
await notificationPromise;
Assert.ok(aboutNewTabService.overridden, "url has been overridden");
Assert.ok(AboutNewTab.newTabURLOverridden, "url has been overridden");
// simulate a newtab open as a user would
BrowserOpenTab(); // jshint ignore:line

Просмотреть файл

@ -29,11 +29,11 @@ describe("ActivityStreamMessageChannel", () => {
this.isFromAboutNewTab = isFromAboutNewTab;
}
globals = new GlobalOverrider();
const override = globals.sandbox.stub();
override.withArgs(true).returns(new RP("about:newtab", true));
override.withArgs(false).returns(null);
const overridePageListener = globals.sandbox.stub();
overridePageListener.withArgs(true).returns(new RP("about:newtab", true));
overridePageListener.withArgs(false).returns(null);
globals.set("AboutNewTab", {
override,
overridePageListener,
reset: globals.sandbox.spy(),
});
globals.set("RemotePages", RP);
@ -103,7 +103,7 @@ describe("ActivityStreamMessageChannel", () => {
});
it("should override AboutNewTab", () => {
mm.createChannel();
assert.calledOnce(global.AboutNewTab.override);
assert.calledOnce(global.AboutNewTab.overridePageListener);
});
it("should use the channel passed by AboutNewTab on override", () => {
mm.createChannel();
@ -112,7 +112,7 @@ describe("ActivityStreamMessageChannel", () => {
it("should not override AboutNewTab if the pageURL is not about:newtab", () => {
mm = new ActivityStreamMessageChannel({ pageURL: "foo.html" });
mm.createChannel();
assert.notCalled(global.AboutNewTab.override);
assert.notCalled(global.AboutNewTab.overridePageListener);
});
});
describe("#simulateMessagesForExistingTabs", () => {

Просмотреть файл

@ -94,8 +94,8 @@ describe("TelemetryFeed", () => {
};
sandbox.spy(global.Cu, "reportError");
globals.set("gUUIDGenerator", { generateUUID: () => FAKE_UUID });
globals.set("aboutNewTabService", {
overridden: false,
globals.set("AboutNewTab", {
newTabURLOverridden: false,
newTabURL: "",
});
globals.set("HomePage", fakeHomePage);
@ -1262,7 +1262,7 @@ describe("TelemetryFeed", () => {
const spy = sandbox.spy();
sandbox.stub(Services.prefs, "getIntPref").returns(1);
globals.set("AboutNewTabStartupRecorder", {
globals.set("AboutNewTab", {
maybeRecordTopsitesPainted: spy,
});
instance.addSession("port123", "about:home");
@ -1561,8 +1561,8 @@ describe("TelemetryFeed", () => {
assert.validate(sendEvent.firstCall.args[0], UserEventPing);
});
it("should send correct event data for about:newtab set to custom URL", async () => {
globals.set("aboutNewTabService", {
overridden: true,
globals.set("AboutNewTab", {
newTabURLOverridden: true,
newTabURL: "https://searchprovider.com",
});
instance._prefs.set(TELEMETRY_PREF, true);

Просмотреть файл

@ -4,6 +4,12 @@
"use strict";
/**
* This file tests both the AboutNewTab and nsIAboutNewTabService
* for its default URL values, as well as its behaviour when overriding
* the default URL values.
*/
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
@ -11,6 +17,10 @@ const { XPCOMUtils } = ChromeUtils.import(
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"aboutNewTabService",
@ -29,7 +39,7 @@ const ACTIVITY_STREAM_DEBUG_PREF = "browser.newtabpage.activity-stream.debug";
function cleanup() {
Services.prefs.clearUserPref(SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF);
Services.prefs.clearUserPref(ACTIVITY_STREAM_DEBUG_PREF);
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
}
registerCleanupFunction(cleanup);
@ -65,13 +75,13 @@ function nextChangeNotificationPromise(aNewURL, testMessage) {
function setPrivilegedContentProcessPref(usePrivilegedContentProcess) {
if (
usePrivilegedContentProcess ===
Services.prefs.getBoolPref(SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF)
usePrivilegedContentProcess === AboutNewTab.privilegedAboutProcessEnabled
) {
return Promise.resolve();
}
let notificationPromise = nextChangeNotificationPromise("about:newtab");
Services.prefs.setBoolPref(
SEPARATE_PRIVILEGED_CONTENT_PROCESS_PREF,
usePrivilegedContentProcess
@ -100,7 +110,7 @@ function setBoolPrefAndWaitForChange(pref, value, testMessage) {
Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
// jshint unused:false
Services.obs.removeObserver(observer, aTopic);
Assert.equal(aData, aboutNewTabService.newTabURL, testMessage);
Assert.equal(aData, AboutNewTab.newTabURL, testMessage);
resolve();
}, "newtab-url-changed");
@ -110,12 +120,12 @@ function setBoolPrefAndWaitForChange(pref, value, testMessage) {
add_task(async function test_as_initial_values() {
Assert.ok(
aboutNewTabService.activityStreamEnabled,
AboutNewTab.activityStreamEnabled,
".activityStreamEnabled should be set to the correct initial value"
);
// This pref isn't defined on release or beta, so we fall back to false
Assert.equal(
aboutNewTabService.activityStreamDebug,
AboutNewTab.activityStreamDebug,
Services.prefs.getBoolPref(ACTIVITY_STREAM_DEBUG_PREF, false),
".activityStreamDebug should be set to the correct initial value"
);
@ -130,40 +140,45 @@ add_task(async function test_override_activity_stream_disabled() {
// override with some remote URL
let url = "http://example.com/";
notificationPromise = nextChangeNotificationPromise(url);
aboutNewTabService.newTabURL = url;
AboutNewTab.newTabURL = url;
await notificationPromise;
Assert.ok(aboutNewTabService.overridden, "Newtab URL should be overridden");
Assert.ok(AboutNewTab.newTabURLOverridden, "Newtab URL should be overridden");
Assert.ok(
!aboutNewTabService.activityStreamEnabled,
!AboutNewTab.activityStreamEnabled,
"Newtab activity stream should not be enabled"
);
Assert.equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
url,
"Newtab URL should be the custom URL"
);
Assert.equal(
aboutNewTabService.defaultURL,
ACTIVITY_STREAM_URL,
"Newtab URL defaultURL still set to the default activity stream URL"
);
// test reset with activity stream disabled
notificationPromise = nextChangeNotificationPromise("about:newtab");
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
await notificationPromise;
Assert.ok(
!aboutNewTabService.overridden,
!AboutNewTab.newTabURLOverridden,
"Newtab URL should not be overridden"
);
Assert.equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
"about:newtab",
"Newtab URL should be the default"
);
// test override to a chrome URL
notificationPromise = nextChangeNotificationPromise(DOWNLOADS_URL);
aboutNewTabService.newTabURL = DOWNLOADS_URL;
AboutNewTab.newTabURL = DOWNLOADS_URL;
await notificationPromise;
Assert.ok(aboutNewTabService.overridden, "Newtab URL should be overridden");
Assert.ok(AboutNewTab.newTabURLOverridden, "Newtab URL should be overridden");
Assert.equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DOWNLOADS_URL,
"Newtab URL should be the custom URL"
);
@ -179,20 +194,20 @@ addTestsWithPrivilegedContentProcessPref(
"Newtab URL should be the default activity stream URL"
);
Assert.ok(
!aboutNewTabService.overridden,
!AboutNewTab.newTabURLOverridden,
"Newtab URL should not be overridden"
);
Assert.ok(
aboutNewTabService.activityStreamEnabled,
AboutNewTab.activityStreamEnabled,
"Activity Stream should be enabled"
);
// change to a chrome URL while activity stream is enabled
let notificationPromise = nextChangeNotificationPromise(DOWNLOADS_URL);
aboutNewTabService.newTabURL = DOWNLOADS_URL;
AboutNewTab.newTabURL = DOWNLOADS_URL;
await notificationPromise;
Assert.equal(
aboutNewTabService.newTabURL,
AboutNewTab.newTabURL,
DOWNLOADS_URL,
"Newtab URL set to chrome url"
);
@ -201,9 +216,12 @@ addTestsWithPrivilegedContentProcessPref(
ACTIVITY_STREAM_URL,
"Newtab URL defaultURL still set to the default activity stream URL"
);
Assert.ok(aboutNewTabService.overridden, "Newtab URL should be overridden");
Assert.ok(
!aboutNewTabService.activityStreamEnabled,
AboutNewTab.newTabURLOverridden,
"Newtab URL should be overridden"
);
Assert.ok(
!AboutNewTab.activityStreamEnabled,
"Activity Stream should not be enabled"
);
@ -226,7 +244,7 @@ addTestsWithPrivilegedContentProcessPref(async function test_default_url() {
"A notification occurs after changing the debug pref to true"
);
Assert.equal(
aboutNewTabService.activityStreamDebug,
AboutNewTab.activityStreamDebug,
true,
"the .activityStreamDebug property is set to true"
);
@ -244,7 +262,7 @@ addTestsWithPrivilegedContentProcessPref(async function test_default_url() {
Services.prefs.setBoolPref(ACTIVITY_STREAM_DEBUG_PREF, true);
Assert.equal(
aboutNewTabService.activityStreamDebug,
AboutNewTab.activityStreamDebug,
false,
"the .activityStreamDebug property is remains false"
);
@ -294,7 +312,7 @@ addTestsWithPrivilegedContentProcessPref(async function test_welcome_url() {
addTestsWithPrivilegedContentProcessPref(async function test_updates() {
// Simulates a "cold-boot" situation, with some pref already set before testing a series
// of changes.
aboutNewTabService.resetNewTabURL(); // need to set manually because pref notifs are off
AboutNewTab.resetNewTabURL(); // need to set manually because pref notifs are off
let notificationPromise;
// test update fires on override and reset
@ -303,7 +321,7 @@ addTestsWithPrivilegedContentProcessPref(async function test_updates() {
testURL,
"a notification occurs on override"
);
aboutNewTabService.newTabURL = testURL;
AboutNewTab.newTabURL = testURL;
await notificationPromise;
// from overridden to default
@ -311,9 +329,9 @@ addTestsWithPrivilegedContentProcessPref(async function test_updates() {
"about:newtab",
"a notification occurs on reset"
);
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
Assert.ok(
aboutNewTabService.activityStreamEnabled,
AboutNewTab.activityStreamEnabled,
"Activity Stream should be enabled"
);
Assert.equal(
@ -328,7 +346,7 @@ addTestsWithPrivilegedContentProcessPref(async function test_updates() {
"about:newtab",
"reset occurs"
);
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
await notificationPromise;
cleanup();

Просмотреть файл

@ -3,7 +3,7 @@ head =
firefox-appdir = browser
skip-if = toolkit == 'android'
[test_AboutNewTabService.js]
[test_AboutNewTab.js]
[test_ASRouterTargeting_attribution.js]
skip-if = toolkit != "cocoa" # osx specific tests
[test_AboutWelcomeTelemetry.js]

Просмотреть файл

@ -7,12 +7,13 @@ ChromeUtils.defineModuleGetter(
"ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
ChromeUtils.defineModuleGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
const { AddonTestUtils } = ChromeUtils.import(
@ -467,10 +468,7 @@ add_task(async function testExtensionControlledNewTab() {
let controlledContent = doc.getElementById("browserNewTabExtensionContent");
// The new tab is set to the default and message is hidden.
ok(
!aboutNewTabService.newTabURL.startsWith("moz-extension:"),
"new tab is not set"
);
ok(!AboutNewTab.newTabURL.startsWith("moz-extension:"), "new tab is not set");
is(controlledContent.hidden, true, "The extension controlled row is hidden");
// Install an extension that will set the new tab page.
@ -481,7 +479,7 @@ add_task(async function testExtensionControlledNewTab() {
// The new tab page has been set by the extension and the user is notified.
let controlledLabel = controlledContent.querySelector("description");
ok(
aboutNewTabService.newTabURL.startsWith("moz-extension:"),
AboutNewTab.newTabURL.startsWith("moz-extension:"),
"new tab url is set by extension"
);
Assert.deepEqual(
@ -515,7 +513,7 @@ add_task(async function testExtensionControlledNewTab() {
// Ensure the New Tab page has been reset and there is no message.
ok(
!aboutNewTabService.newTabURL.startsWith("moz-extension:"),
!AboutNewTab.newTabURL.startsWith("moz-extension:"),
"new tab page is set back to default"
);
is(controlledContent.hidden, true, "The extension controlled row is shown");

Просмотреть файл

@ -5,9 +5,10 @@
// This test makes sure that the URL bar is focused when entering the private window.
"use strict";
let aboutNewTabService = Cc[
"@mozilla.org/browser/aboutnewtab-service;1"
].getService(Ci.nsIAboutNewTabService);
const { AboutNewTab } = ChromeUtils.import(
"resource:///modules/AboutNewTab.jsm"
);
function checkUrlbarFocus(win) {
let urlbar = win.gURLBar;
@ -34,14 +35,14 @@ add_task(async function() {
});
add_task(async function() {
aboutNewTabService.newTabURL = "about:blank";
AboutNewTab.newTabURL = "about:blank";
registerCleanupFunction(() => {
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
});
let win = await openNewPrivateWindow();
checkUrlbarFocus(win);
win.close();
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
});

Просмотреть файл

@ -4,7 +4,9 @@
"use strict";
var EXPORTED_SYMBOLS = ["AboutNewTab"];
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
@ -17,21 +19,30 @@ XPCOMUtils.defineLazyModuleGetters(this, {
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm",
});
const ABOUT_URL = "about:newtab";
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
const TOPIC_APP_QUIT = "quit-application-granted";
const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
var AboutNewTab = {
const AboutNewTab = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
// AboutNewTab
initialized: false,
pageListener: null,
isPageListenerOverridden: false,
willNotifyUser: false,
isOverridden: false,
_activityStreamEnabled: false,
activityStream: null,
activityStreamDebug: false,
_newTabURL: ABOUT_URL,
_newTabURLOverridden: false,
/**
* init - Initializes an instance of Activity Stream if one doesn't exist already
@ -43,7 +54,34 @@ var AboutNewTab = {
* would like to re-use.
*/
init(pageListener) {
if (this.isOverridden) {
Services.obs.addObserver(this, TOPIC_APP_QUIT);
if (!AppConstants.RELEASE_OR_BETA) {
XPCOMUtils.defineLazyPreferenceGetter(
this,
"activityStreamDebug",
PREF_ACTIVITY_STREAM_DEBUG,
false,
() => {
this.notifyChange();
}
);
}
XPCOMUtils.defineLazyPreferenceGetter(
this,
"privilegedAboutProcessEnabled",
"browser.tabs.remote.separatePrivilegedContentProcess",
false,
() => {
this.notifyChange();
}
);
// More initialization happens here
this.toggleActivityStream(true);
this.initialized = true;
if (this.isPageListenerOverridden) {
return;
}
@ -59,6 +97,74 @@ var AboutNewTab = {
new RemotePages(["about:home", "about:newtab", "about:welcome"]);
},
/**
* React to changes to the activity stream being enabled or not.
*
* This will only act if there is a change of state and if not overridden.
*
* @returns {Boolean} Returns if there has been a state change
*
* @param {Boolean} stateEnabled activity stream enabled state to set to
* @param {Boolean} forceState force state change
*/
toggleActivityStream(stateEnabled, forceState = false) {
if (
!forceState &&
(this._newTabURLOverridden ||
stateEnabled === this._activityStreamEnabled)
) {
// exit there is no change of state
return false;
}
if (stateEnabled) {
this._activityStreamEnabled = true;
} else {
this._activityStreamEnabled = false;
}
this._newTabURL = ABOUT_URL;
return true;
},
get newTabURL() {
return this._newTabURL;
},
set newTabURL(aNewTabURL) {
let newTabURL = aNewTabURL.trim();
if (newTabURL === ABOUT_URL) {
// avoid infinite redirects in case one sets the URL to about:newtab
this.resetNewTabURL();
return;
} else if (newTabURL === "") {
newTabURL = "about:blank";
}
this.toggleActivityStream(false);
this._newTabURL = newTabURL;
this._newTabURLOverridden = true;
this.notifyChange();
},
get newTabURLOverridden() {
return this._newTabURLOverridden;
},
get activityStreamEnabled() {
return this._activityStreamEnabled;
},
resetNewTabURL() {
this._newTabURLOverridden = false;
this._newTabURL = ABOUT_URL;
this.toggleActivityStream(true, true);
this.notifyChange();
},
notifyChange() {
Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
},
/**
* onBrowserReady - Continues the initialization of Activity Stream after browser is ready.
*/
@ -89,10 +195,12 @@ var AboutNewTab = {
this.pageListener.destroy();
this.pageListener = null;
}
this.initialized = false;
},
override(shouldPassPageListener) {
this.isOverridden = true;
overridePageListener(shouldPassPageListener) {
this.isPageListenerOverridden = true;
const pageListener = this.pageListener;
if (!pageListener) {
return null;
@ -106,7 +214,7 @@ var AboutNewTab = {
},
reset(pageListener) {
this.isOverridden = false;
this.isPageListenerOverridden = false;
this.init(pageListener);
},
@ -116,15 +224,43 @@ var AboutNewTab = {
: [];
},
_alreadyRecordedTopsitesPainted: false,
_nonDefaultStartup: false,
noteNonDefaultStartup() {
this._nonDefaultStartup = true;
},
maybeRecordTopsitesPainted(timestamp) {
if (this._alreadyRecordedTopsitesPainted || this._nonDefaultStartup) {
return;
}
const SCALAR_KEY = "timestamps.about_home_topsites_first_paint";
let startupInfo = Services.startup.getStartupInfo();
let processStartTs = startupInfo.process.getTime();
let delta = Math.round(timestamp - processStartTs);
Services.telemetry.scalarSet(SCALAR_KEY, delta);
this._alreadyRecordedTopsitesPainted = true;
},
// nsIObserver implementation
observe(subject, topic, data) {
switch (topic) {
case BROWSER_READY_NOTIFICATION:
case TOPIC_APP_QUIT: {
this.uninit();
break;
}
case BROWSER_READY_NOTIFICATION: {
Services.obs.removeObserver(this, BROWSER_READY_NOTIFICATION);
// Avoid running synchronously during this event that's used for timing
Services.tm.dispatchToMainThread(() => this.onBrowserReady());
break;
}
}
},
};
var EXPORTED_SYMBOLS = ["AboutNewTab"];

Просмотреть файл

@ -15,18 +15,12 @@ const { XPCOMUtils } = ChromeUtils.import(
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",
});
XPCOMUtils.defineLazyServiceGetters(this, {
gAboutNewTabService: [
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService",
],
});
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
@ -42,7 +36,7 @@ let NewTabPagePreloading = {
get enabled() {
return (
this.prefEnabled && this.newTabEnabled && !gAboutNewTabService.overridden
this.prefEnabled && this.newTabEnabled && !AboutNewTab.newTabURLOverridden
);
},

Просмотреть файл

@ -7,6 +7,12 @@ const { RemotePages } = ChromeUtils.import(
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
let context = {};
let TalosParentProfiler;
@ -359,10 +365,7 @@ this.tabswitch = class extends ExtensionAPI {
return {
tabswitch: {
setup({ processScriptPath }) {
const AboutNewTabService = Cc[
"@mozilla.org/browser/aboutnewtab-service;1"
].getService(Ci.nsIAboutNewTabService);
AboutNewTabService.newTabURL = "about:blank";
AboutNewTab.newTabURL = "about:blank";
const processScriptURL = context.extension.baseURI.resolve(
processScriptPath
@ -378,7 +381,7 @@ this.tabswitch = class extends ExtensionAPI {
return () => {
Services.ppmm.sendAsyncMessage("Tabswitch:Teardown");
remotePage.destroy();
AboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
};
},
},

Просмотреть файл

@ -27,9 +27,11 @@
// X - tab drag
// X - tab remove from the middle
// X - Without add-tab button -> can be hidden while testing manually. in talos always with the button
let aboutNewTabService = Cc[
"@mozilla.org/browser/aboutnewtab-service;1"
].getService(Ci.nsIAboutNewTabService);
ChromeUtils.defineModuleGetter(
this,
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
/* globals res:true, sequenceArray:true */
@ -78,9 +80,9 @@ Tart.prototype = {
}, "newtab-url-changed");
});
if (url === "about:newtab") {
aboutNewTabService.resetNewTabURL();
AboutNewTab.resetNewTabURL();
} else {
aboutNewTabService.newTabURL = url;
AboutNewTab.newTabURL = url;
}
return promise;
},

Просмотреть файл

@ -16,12 +16,10 @@ ChromeUtils.defineModuleGetter(
"Services",
"resource://gre/modules/Services.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
ChromeUtils.defineModuleGetter(
this,
"aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService"
"AboutNewTab",
"resource:///modules/AboutNewTab.jsm"
);
var { ExtensionPreferencesManager } = ChromeUtils.import(
@ -333,7 +331,7 @@ this.browserSettings = class extends ExtensionAPI {
context,
name: NEW_TAB_OVERRIDE_SETTING,
callback() {
return aboutNewTabService.newTabURL;
return AboutNewTab.newTabURL;
},
storeType: URL_STORE_TYPE,
readOnly: true,
@ -344,7 +342,7 @@ this.browserSettings = class extends ExtensionAPI {
let listener = (text, id) => {
fire.async({
levelOfControl: "not_controllable",
value: aboutNewTabService.newTabURL,
value: AboutNewTab.newTabURL,
});
};
Services.obs.addObserver(listener, "newtab-url-changed");