Bug 1414018 - Go to new New Tab page after disabling add-on in New Tab doorhanger r?aswan
MozReview-Commit-ID: 7ZXehNEC6ml
--- a/browser/components/extensions/ext-url-overrides.js
+++ b/browser/components/extensions/ext-url-overrides.js
@@ -20,16 +20,34 @@ const STORE_TYPE = "url_overrides";
const NEW_TAB_SETTING_NAME = "newTabURL";
const NEW_TAB_CONFIRMED_TYPE = "newTabNotification";
function userWasNotified(extensionId) {
let setting = ExtensionSettingsStore.getSetting(NEW_TAB_CONFIRMED_TYPE, extensionId);
return setting && setting.value;
}
+function replaceUrlInTab(gBrowser, tab, url) {
+ let loaded = new Promise(resolve => {
+ windowTracker.addListener("progress", {
+ onLocationChange(browser, webProgress, request, locationURI, flags) {
+ if (webProgress.isTopLevel
+ && browser.ownerGlobal.gBrowser.getTabForBrowser(browser) == tab
+ && locationURI.spec == url) {
+ windowTracker.removeListener(this);
+ resolve();
+ }
+ },
+ });
+ });
+ gBrowser.loadURIWithFlags(
+ url, {flags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY});
+ return loaded;
+}
+
async function handleNewTabOpened() {
// We don't need to open the doorhanger again until the controlling add-on changes.
// eslint-disable-next-line no-use-before-define
removeNewTabObserver();
let item = ExtensionSettingsStore.getSetting(STORE_TYPE, NEW_TAB_SETTING_NAME);
if (!item || !item.id || userWasNotified(item.id)) {
@@ -43,19 +61,34 @@ async function handleNewTabOpened() {
// Setup the command handler.
let handleCommand = async (event) => {
if (event.originalTarget.getAttribute("anonid") == "button") {
// Main action is to keep changes.
await ExtensionSettingsStore.addSetting(
item.id, NEW_TAB_CONFIRMED_TYPE, item.id, true, () => false);
} else {
- // Secondary action is to restore settings.
+ // Secondary action is to restore settings. Disabling an add-on should remove
+ // the tabs that it has open, but we want to open the new New Tab in this tab.
+ // 1. Replace the tab's URL with about:blank, wait for it to change
+ // 2. Now that this tab isn't associated with the add-on, disable the add-on
+ // 3. Replace the tab's URL with the new New Tab URL
ExtensionSettingsStore.removeSetting(NEW_TAB_CONFIRMED_TYPE, item.id);
let addon = await AddonManager.getAddonByID(item.id);
+ let gBrowser = win.gBrowser;
+ let tab = gBrowser.selectedTab;
+ await replaceUrlInTab(gBrowser, tab, "about:blank");
+ Services.obs.addObserver({
+ async observe() {
+ await replaceUrlInTab(gBrowser, tab, aboutNewTabService.newTabURL);
+ handleNewTabOpened();
+ Services.obs.removeObserver(this, "newtab-url-changed");
+ },
+ }, "newtab-url-changed");
+
addon.userDisabled = true;
}
panel.hidePopup();
win.gURLBar.focus();
};
panel.addEventListener("command", handleCommand);
panel.addEventListener("popuphidden", () => {
panel.removeEventListener("command", handleCommand);
@@ -95,22 +128,22 @@ function removeNewTabObserver() {
function addNewTabObserver(extensionId) {
if (!aboutNewTabService.willNotifyUser && extensionId && !userWasNotified(extensionId)) {
Services.obs.addObserver(newTabOpenedListener, "browser-open-newtab-start");
aboutNewTabService.willNotifyUser = true;
}
}
function setNewTabURL(extensionId, url) {
- aboutNewTabService.newTabURL = url;
- if (aboutNewTabService.overridden) {
+ if (extensionId) {
addNewTabObserver(extensionId);
} else {
removeNewTabObserver();
}
+ aboutNewTabService.newTabURL = url;
}
this.urlOverrides = class extends ExtensionAPI {
processNewTabSetting(action) {
let {extension} = this;
let item = ExtensionSettingsStore[action](extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
if (item) {
setNewTabURL(item.id, item.value || item.initialValue);
@@ -159,13 +192,13 @@ this.urlOverrides = class extends Extens
// This is required because addSetting above is used for both add and update.
if (["ADDON_ENABLE", "ADDON_UPGRADE", "ADDON_DOWNGRADE"]
.includes(extension.startupReason)) {
item = ExtensionSettingsStore.enable(extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
}
// Set the newTabURL to the current value of the setting.
if (item) {
- setNewTabURL(extension.id, item.value || item.initialValue);
+ setNewTabURL(item.id, item.value || item.initialValue);
}
}
}
};
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -34,16 +34,44 @@ function waitForNewTab() {
function observer() {
Services.obs.removeObserver(observer, eventName);
resolve();
}
Services.obs.addObserver(observer, eventName);
});
}
+function waitForAddonDisabled(addon) {
+ return new Promise(resolve => {
+ let listener = {
+ onDisabled(disabledAddon) {
+ if (disabledAddon.id == addon.id) {
+ resolve();
+ AddonManager.removeAddonListener(listener);
+ }
+ },
+ };
+ AddonManager.addAddonListener(listener);
+ });
+}
+
+function waitForAddonEnabled(addon) {
+ return new Promise(resolve => {
+ let listener = {
+ onEnabled(enabledAddon) {
+ if (enabledAddon.id == addon.id) {
+ AddonManager.removeAddonListener(listener);
+ resolve();
+ }
+ },
+ };
+ AddonManager.addAddonListener(listener);
+ });
+}
+
add_task(async function test_new_tab_opens() {
let panel = getNewTabDoorhanger().closest("panel");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"chrome_url_overrides": {
newtab: NEWTAB_URI_1,
},
},
@@ -223,18 +251,18 @@ add_task(async function test_new_tab_kee
// Ensure panel is closed and setting is still set.
ok(panel.getAttribute("panelopen") != "true",
"The notification panel is closed after click");
is(getNotificationSetting(extensionId).value, true,
"The New Tab notification is set after keeping the changes");
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ await upgradedExtension.unload();
await extension.unload();
- await upgradedExtension.unload();
});
add_task(async function test_new_tab_restore_settings() {
await ExtensionSettingsStore.initialize();
let notification = getNewTabDoorhanger();
let panel = notification.closest("panel");
let extensionId = "newtabrestore@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
@@ -262,38 +290,31 @@ add_task(async function test_new_tab_res
let addon = await AddonManager.getAddonByID(extensionId);
is(addon.userDisabled, false, "The add-on is enabled at first");
is(panel.getAttribute("panelopen"), "true",
"The notification panel is open after opening New Tab");
is(getNotificationSetting(extensionId), null,
"The New Tab notification is not set for this extension");
// Click the Restore Changes button.
- let addonDisabled = new Promise(resolve => {
- let listener = {
- onDisabled(disabledAddon) {
- if (disabledAddon.id == addon.id) {
- resolve();
- AddonManager.removeAddonListener(listener);
- }
- },
- };
- AddonManager.addAddonListener(listener);
- });
+ let addonDisabled = waitForAddonDisabled(addon);
let popupHidden = promisePopupHidden(panel);
clickRestoreSettings(notification);
await popupHidden;
await addonDisabled;
+ await BrowserTestUtils.waitForLocationChange(gBrowser, "about:newtab");
// Ensure panel is closed, settings haven't changed and add-on is disabled.
ok(panel.getAttribute("panelopen") != "true",
"The notification panel is closed after click");
is(getNotificationSetting(extensionId), null,
- "The New Tab notification is not set after resorting the settings");
+ "The New Tab notification is not set after restoring the settings");
is(addon.userDisabled, true, "The extension is now disabled");
+ is(gBrowser.currentURI.spec, "about:newtab",
+ "The user has been redirected to about:newtab");
// Reopen a browser tab and verify that there's no doorhanger.
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
let newTabOpened = waitForNewTab();
BrowserOpenTab();
await newTabOpened;
ok(panel.getAttribute("panelopen") != "true",
@@ -313,16 +334,130 @@ add_task(async function test_new_tab_res
AddonManager.addAddonListener(listener);
});
addon.userDisabled = false;
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await addonEnabled;
await extension.unload();
});
+add_task(async function test_new_tab_restore_settings_multiple() {
+ await ExtensionSettingsStore.initialize();
+ let notification = getNewTabDoorhanger();
+ let panel = notification.closest("panel");
+ let extensionOneId = "newtabrestoreone@mochi.test";
+ let extensionOne = ExtensionTestUtils.loadExtension({
+ manifest: {
+ applications: {gecko: {id: extensionOneId}},
+ chrome_url_overrides: {newtab: "restore-one.html"},
+ },
+ files: {
+ "restore-one.html": `
+ <h1 id="extension-new-tab">New Tab!</h1>
+ <script src="newtab.js"></script>
+ `,
+ "newtab.js": function() {
+ browser.test.sendMessage("from-newtab-page", window.location.href);
+ },
+ },
+ useAddonManager: "temporary",
+ });
+ let extensionTwoId = "newtabrestoretwo@mochi.test";
+ let extensionTwo = ExtensionTestUtils.loadExtension({
+ manifest: {
+ applications: {gecko: {id: extensionTwoId}},
+ chrome_url_overrides: {newtab: "restore-two.html"},
+ },
+ files: {"restore-two.html": '<h1 id="extension-new-tab">New Tab!</h1>'},
+ useAddonManager: "temporary",
+ });
+
+ ok(panel.getAttribute("panelopen") != "true",
+ "The notification panel is initially closed");
+ is(getNotificationSetting(extensionOneId), null,
+ "The New Tab notification is not initially set for this extension");
+ is(getNotificationSetting(extensionTwoId), null,
+ "The New Tab notification is not initially set for this extension");
+
+ await extensionOne.startup();
+ await extensionTwo.startup();
+
+ // Simulate opening the newtab open as a user would.
+ let popupShown = promisePopupShown(panel);
+ BrowserOpenTab();
+ await popupShown;
+
+ // Verify that the panel is open and add-on is enabled.
+ let addonTwo = await AddonManager.getAddonByID(extensionTwoId);
+ is(addonTwo.userDisabled, false, "The add-on is enabled at first");
+ is(panel.getAttribute("panelopen"), "true",
+ "The notification panel is open after opening New Tab");
+ is(getNotificationSetting(extensionTwoId), null,
+ "The New Tab notification is not set for this extension");
+
+ // Click the Restore Changes button.
+ let addonDisabled = waitForAddonDisabled(addonTwo);
+ let popupHidden = promisePopupHidden(panel);
+ let newTabUrlPromise = extensionOne.awaitMessage("from-newtab-page");
+ clickRestoreSettings(notification);
+ await popupHidden;
+ await addonDisabled;
+ await promisePopupShown(panel);
+ let newTabUrl = await newTabUrlPromise;
+
+ // Ensure the panel opens again for the next add-on.
+ is(getNotificationSetting(extensionTwoId), null,
+ "The New Tab notification is not set after restoring the settings");
+ is(addonTwo.userDisabled, true, "The extension is now disabled");
+ let addonOne = await AddonManager.getAddonByID(extensionOneId);
+ is(addonOne.userDisabled, false, "The extension is enabled before making a choice");
+ is(getNotificationSetting(extensionOneId), null,
+ "The New Tab notification is not set before making a choice");
+ is(panel.getAttribute("panelopen"), "true",
+ "The notification panel is open after opening New Tab");
+ is(gBrowser.currentURI.spec, newTabUrl,
+ "The user is now on the next extension's New Tab page");
+
+ addonDisabled = waitForAddonDisabled(addonOne);
+ popupHidden = promisePopupHidden(panel);
+ clickRestoreSettings(notification);
+ await popupHidden;
+ await addonDisabled;
+ await BrowserTestUtils.waitForLocationChange(gBrowser, "about:newtab");
+
+ ok(panel.getAttribute("panelopen") != "true",
+ "The notification panel is closed after restoring the second time");
+ is(getNotificationSetting(extensionOneId), null,
+ "The New Tab notification is not set after restoring the settings");
+ is(addonOne.userDisabled, true, "The extension is now disabled");
+ is(gBrowser.currentURI.spec, "about:newtab",
+ "The user is now on the original New Tab URL since all extensions are disabled");
+
+ // Reopen a browser tab and verify that there's no doorhanger.
+ await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ let newTabOpened = waitForNewTab();
+ BrowserOpenTab();
+ await newTabOpened;
+
+ ok(panel.getAttribute("panelopen") != "true",
+ "The notification panel is not opened after keeping the changes");
+
+ await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+ // FIXME: We need to enable the add-on so it gets cleared from the
+ // ExtensionSettingsStore for now. See bug 1408226.
+ let addonsEnabled = Promise.all([
+ waitForAddonEnabled(addonOne), waitForAddonEnabled(addonTwo)]);
+ addonOne.userDisabled = false;
+ addonTwo.userDisabled = false;
+ await addonsEnabled;
+ await extensionOne.unload();
+ await extensionTwo.unload();
+});
+
/**
* Ensure we don't show the extension URL in the URL bar temporarily in new tabs
* while we're switching remoteness (when the URL we're loading and the
* default content principal are different).
*/
add_task(async function dontTemporarilyShowAboutExtensionPath() {
await ExtensionSettingsStore.initialize();
let extension = ExtensionTestUtils.loadExtension({