Loading browser/base/content/test/popups/browser_popup_blocker.js +186 −3 Original line number Diff line number Diff line Loading @@ -85,7 +85,190 @@ add_task(async function test_opening_blocked_popups_privateWindow() { await BrowserTestUtils.closeWindow(win); }); async function testPopupBlockingToolbar(tab) { // This is a test for Bug 1988311. // Make sure that everything also functions correctly on special pages, // such as "about:privatebrowsing". add_task(async function test_opening_blocked_popups_about_privatebrowsing() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:privatebrowsing" ); const browser = tab.linkedBrowser; const uri = Services.io.newURI( "javascript:" + `window.open("${baseURL}" + "popup_blocker_a.html");` + `window.open("${baseURL}" + "popup_blocker_b.html");` ); const triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); browser.loadURI(uri, { triggeringPrincipal }); await testPopupBlockingToolbar(tab); }); // Bug 2006600. // When a notification has been dismissed by a user, it should not appear // again when switching to a different tab and back. add_task(async function test_dismissed_notification_switch_tabs() { // Open the test page. const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, baseURL + "popup_blocker.html" ); // Wait for the notification. let notification; await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")) ); // Click dismiss button. const mozButton = notification.shadowRoot.querySelector("moz-button.close"); mozButton.click(); // Open a new (foreground) tab and switch back. const differentTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); await BrowserTestUtils.switchTab(gBrowser, tab); // Make sure no notification appears. try { await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")), null, 50, 10 ); } catch (e) { notification = null; } ok(!notification, "Notification should not reappear"); gBrowser.removeTab(tab); gBrowser.removeTab(differentTab); }); // Bug 2025170. // "Allow" should unblock all popups across multiple browsing contexts // without erroring on out-of-bounds per-document indices. add_task(async function test_bug2025170_allow_all() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await SpecialPowers.spawn( tab.linkedBrowser, [baseURL + "popup_blocker.html"], uri => { for (let i = 0; i < 2; i++) { let iframe = content.document.createElement("iframe"); iframe.src = uri; content.document.body.appendChild(iframe); } } ); // Because popup_blocker.html is calling window.open() with target // window names, we expect only two new tabs. await testPopupBlockingToolbar( tab, /*expectedBlocked=*/ 4, /*expectedOpened=*/ 2 ); }); // Bug 2025170. // Make sure the correct popup is opened when the blocked popup list // spans multiple browsing contexts. add_task(async function test_bug2025170_unblock_popup() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await SpecialPowers.spawn( tab.linkedBrowser, [baseURL + "popup_blocker.html"], uri => { for (let i = 0; i < 2; i++) { let iframe = content.document.createElement("iframe"); iframe.src = uri; content.document.body.appendChild(iframe); } } ); // Wait for the popup-blocked notification. let notification; await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")) ); // Show the menu. const popupShown = BrowserTestUtils.waitForEvent(window, "popupshown"); const popupFilled = waitForBlockedPopups(4, { doc: document }); EventUtils.synthesizeMouseAtCenter( notification.buttonContainer.querySelector("button"), {}, window ); // Wait for the menu. const popupEvent = await popupShown; const menu = popupEvent.target; is(menu.id, "blockedPopupOptions", "Blocked popup menu shown"); await popupFilled; const popupItems = menu.querySelectorAll("[popupReportIndex]"); is(popupItems.length, 4, "Should have 4 blocked popup items"); // Track new tabs. const popupTabs = []; const onTabOpen = e => popupTabs.push(e.target); gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen); // The last item in the flattened list appears first in DOM order. // Without the fix, its popupReportIndex would be out of bounds. popupItems[0].doCommand(); await TestUtils.waitForCondition( () => popupTabs.length == 1 && popupTabs[0].linkedBrowser.currentURI.spec != "about:blank", "Waiting for popup tab to open" ); ok( popupTabs[0].linkedBrowser.currentURI.spec.endsWith("popup_blocker_b.html"), "Should have opened popup_blocker_b.html" ); gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen); // Clean up. menu.hidePopup(); BrowserTestUtils.removeTab(popupTabs[0]); BrowserTestUtils.removeTab(tab); }); async function testPopupBlockingToolbar( tab, expectedBlocked = 2, expectedOpened = 2 ) { let win = tab.ownerGlobal; // Wait for the popup-blocked notification. let notification; Loading @@ -98,7 +281,7 @@ async function testPopupBlockingToolbar(tab) { // Show the menu. let popupShown = BrowserTestUtils.waitForEvent(win, "popupshown"); let popupFilled = waitForBlockedPopups(2, { let popupFilled = waitForBlockedPopups(expectedBlocked, { doc: win.document, }); EventUtils.synthesizeMouseAtCenter( Loading @@ -124,7 +307,7 @@ async function testPopupBlockingToolbar(tab) { allow.doCommand(); await TestUtils.waitForCondition( () => popupTabs.length == 2 && popupTabs.length == expectedOpened && popupTabs.every( aTab => aTab.linkedBrowser.currentURI.spec != "about:blank" ) Loading browser/modules/PopupAndRedirectBlockerObserver.sys.mjs +6 −4 Original line number Diff line number Diff line Loading @@ -241,9 +241,11 @@ export var PopupAndRedirectBlockerObserver = { document.l10n.setAttributes(menuitem, "popup-show-popup-menuitem", { popupURI: blockedPopup.popupWindowURISpec, }); menuitem.setAttribute("popupReportIndex", i); // The report index is the index into the blocked popup list // maintained by the window where this popup was blocked. menuitem.setAttribute("popupReportIndex", blockedPopup.reportIndex); // Store the source inner window id, so we can check if the document // that triggered the redirect is still the same. // that triggered the popup is still the same. menuitem.setAttribute("popupInnerWindowId", blockedPopup.innerWindowId); // Store the browser for the current tab. The active tab may change, // so we keep a reference to it. Loading Loading @@ -321,12 +323,12 @@ export var PopupAndRedirectBlockerObserver = { showBlockedPopup(aEvent) { const { browser, browsingContext } = aEvent.target; const innerWindowId = aEvent.target.getAttribute("popupInnerWindowId"); const popupReportIndex = aEvent.target.getAttribute("popupReportIndex"); const reportIndex = aEvent.target.getAttribute("popupReportIndex"); browser.popupAndRedirectBlocker.unblockPopup( browsingContext, innerWindowId, popupReportIndex reportIndex ); }, Loading toolkit/actors/PopupAndRedirectBlockingChild.sys.mjs +5 −4 Original line number Diff line number Diff line Loading @@ -91,11 +91,12 @@ export class PopupAndRedirectBlockingChild extends JSWindowActorChild { ); const result = []; for (let i = 0; i < length; ++i) { const popup = state.popups[i]; for (let reportIndex = 0; reportIndex < length; ++reportIndex) { const popup = state.popups[reportIndex]; const { popupWindowURISpec } = popup; result.push({ popupWindowURISpec, reportIndex, }); } Loading @@ -114,8 +115,8 @@ export class PopupAndRedirectBlockingChild extends JSWindowActorChild { } #unblockPopup(aMessage) { const idx = aMessage.data.index; const popup = this.#getOrCreateDocState().popups[idx]; const reportIndex = aMessage.data.reportIndex; const popup = this.#getOrCreateDocState().popups[reportIndex]; if (popup?.requestingWindow?.document == popup.requestingDocument) { popup.requestingWindow.open( Loading toolkit/actors/PopupAndRedirectBlockingParent.sys.mjs +10 −5 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ export class PopupAndRedirectBlocker { browsingContext: currentBC, innerWindowId: currentWG.innerWindowId, popupWindowURISpec: popup.popupWindowURISpec, reportIndex: popup.reportIndex, }); } } Loading Loading @@ -121,14 +122,14 @@ export class PopupAndRedirectBlocker { }; } unblockPopup(aBrowsingContext, aInnerWindowId, aPopupIndex) { unblockPopup(aBrowsingContext, aInnerWindowId, aReportIndex) { const sourceWG = aBrowsingContext.currentWindowGlobal; if (sourceWG?.innerWindowId != aInnerWindowId) { return; } const actor = sourceWG.getActor("PopupAndRedirectBlocking"); actor.sendAsyncMessage("UnblockPopup", { index: aPopupIndex }); actor.sendAsyncMessage("UnblockPopup", { reportIndex: aReportIndex }); } unblockRedirect(aBrowsingContext, aInnerWindowId, aRedirectURISpec) { Loading @@ -145,9 +146,13 @@ export class PopupAndRedirectBlocker { async unblockAllPopups() { const popups = await this.getBlockedPopups(); for (let idx = 0; idx < popups.length; ++idx) { const popup = popups[idx]; this.unblockPopup(popup.browsingContext, popup.innerWindowId, idx); for (let i = 0; i < popups.length; ++i) { const popup = popups[i]; this.unblockPopup( popup.browsingContext, popup.innerWindowId, popup.reportIndex ); } } Loading Loading
browser/base/content/test/popups/browser_popup_blocker.js +186 −3 Original line number Diff line number Diff line Loading @@ -85,7 +85,190 @@ add_task(async function test_opening_blocked_popups_privateWindow() { await BrowserTestUtils.closeWindow(win); }); async function testPopupBlockingToolbar(tab) { // This is a test for Bug 1988311. // Make sure that everything also functions correctly on special pages, // such as "about:privatebrowsing". add_task(async function test_opening_blocked_popups_about_privatebrowsing() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:privatebrowsing" ); const browser = tab.linkedBrowser; const uri = Services.io.newURI( "javascript:" + `window.open("${baseURL}" + "popup_blocker_a.html");` + `window.open("${baseURL}" + "popup_blocker_b.html");` ); const triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); browser.loadURI(uri, { triggeringPrincipal }); await testPopupBlockingToolbar(tab); }); // Bug 2006600. // When a notification has been dismissed by a user, it should not appear // again when switching to a different tab and back. add_task(async function test_dismissed_notification_switch_tabs() { // Open the test page. const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, baseURL + "popup_blocker.html" ); // Wait for the notification. let notification; await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")) ); // Click dismiss button. const mozButton = notification.shadowRoot.querySelector("moz-button.close"); mozButton.click(); // Open a new (foreground) tab and switch back. const differentTab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "about:blank" ); await BrowserTestUtils.switchTab(gBrowser, tab); // Make sure no notification appears. try { await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")), null, 50, 10 ); } catch (e) { notification = null; } ok(!notification, "Notification should not reappear"); gBrowser.removeTab(tab); gBrowser.removeTab(differentTab); }); // Bug 2025170. // "Allow" should unblock all popups across multiple browsing contexts // without erroring on out-of-bounds per-document indices. add_task(async function test_bug2025170_allow_all() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await SpecialPowers.spawn( tab.linkedBrowser, [baseURL + "popup_blocker.html"], uri => { for (let i = 0; i < 2; i++) { let iframe = content.document.createElement("iframe"); iframe.src = uri; content.document.body.appendChild(iframe); } } ); // Because popup_blocker.html is calling window.open() with target // window names, we expect only two new tabs. await testPopupBlockingToolbar( tab, /*expectedBlocked=*/ 4, /*expectedOpened=*/ 2 ); }); // Bug 2025170. // Make sure the correct popup is opened when the blocked popup list // spans multiple browsing contexts. add_task(async function test_bug2025170_unblock_popup() { const tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await SpecialPowers.spawn( tab.linkedBrowser, [baseURL + "popup_blocker.html"], uri => { for (let i = 0; i < 2; i++) { let iframe = content.document.createElement("iframe"); iframe.src = uri; content.document.body.appendChild(iframe); } } ); // Wait for the popup-blocked notification. let notification; await TestUtils.waitForCondition( () => (notification = gBrowser .getNotificationBox() .getNotificationWithValue("popup-blocked")) ); // Show the menu. const popupShown = BrowserTestUtils.waitForEvent(window, "popupshown"); const popupFilled = waitForBlockedPopups(4, { doc: document }); EventUtils.synthesizeMouseAtCenter( notification.buttonContainer.querySelector("button"), {}, window ); // Wait for the menu. const popupEvent = await popupShown; const menu = popupEvent.target; is(menu.id, "blockedPopupOptions", "Blocked popup menu shown"); await popupFilled; const popupItems = menu.querySelectorAll("[popupReportIndex]"); is(popupItems.length, 4, "Should have 4 blocked popup items"); // Track new tabs. const popupTabs = []; const onTabOpen = e => popupTabs.push(e.target); gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen); // The last item in the flattened list appears first in DOM order. // Without the fix, its popupReportIndex would be out of bounds. popupItems[0].doCommand(); await TestUtils.waitForCondition( () => popupTabs.length == 1 && popupTabs[0].linkedBrowser.currentURI.spec != "about:blank", "Waiting for popup tab to open" ); ok( popupTabs[0].linkedBrowser.currentURI.spec.endsWith("popup_blocker_b.html"), "Should have opened popup_blocker_b.html" ); gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen); // Clean up. menu.hidePopup(); BrowserTestUtils.removeTab(popupTabs[0]); BrowserTestUtils.removeTab(tab); }); async function testPopupBlockingToolbar( tab, expectedBlocked = 2, expectedOpened = 2 ) { let win = tab.ownerGlobal; // Wait for the popup-blocked notification. let notification; Loading @@ -98,7 +281,7 @@ async function testPopupBlockingToolbar(tab) { // Show the menu. let popupShown = BrowserTestUtils.waitForEvent(win, "popupshown"); let popupFilled = waitForBlockedPopups(2, { let popupFilled = waitForBlockedPopups(expectedBlocked, { doc: win.document, }); EventUtils.synthesizeMouseAtCenter( Loading @@ -124,7 +307,7 @@ async function testPopupBlockingToolbar(tab) { allow.doCommand(); await TestUtils.waitForCondition( () => popupTabs.length == 2 && popupTabs.length == expectedOpened && popupTabs.every( aTab => aTab.linkedBrowser.currentURI.spec != "about:blank" ) Loading
browser/modules/PopupAndRedirectBlockerObserver.sys.mjs +6 −4 Original line number Diff line number Diff line Loading @@ -241,9 +241,11 @@ export var PopupAndRedirectBlockerObserver = { document.l10n.setAttributes(menuitem, "popup-show-popup-menuitem", { popupURI: blockedPopup.popupWindowURISpec, }); menuitem.setAttribute("popupReportIndex", i); // The report index is the index into the blocked popup list // maintained by the window where this popup was blocked. menuitem.setAttribute("popupReportIndex", blockedPopup.reportIndex); // Store the source inner window id, so we can check if the document // that triggered the redirect is still the same. // that triggered the popup is still the same. menuitem.setAttribute("popupInnerWindowId", blockedPopup.innerWindowId); // Store the browser for the current tab. The active tab may change, // so we keep a reference to it. Loading Loading @@ -321,12 +323,12 @@ export var PopupAndRedirectBlockerObserver = { showBlockedPopup(aEvent) { const { browser, browsingContext } = aEvent.target; const innerWindowId = aEvent.target.getAttribute("popupInnerWindowId"); const popupReportIndex = aEvent.target.getAttribute("popupReportIndex"); const reportIndex = aEvent.target.getAttribute("popupReportIndex"); browser.popupAndRedirectBlocker.unblockPopup( browsingContext, innerWindowId, popupReportIndex reportIndex ); }, Loading
toolkit/actors/PopupAndRedirectBlockingChild.sys.mjs +5 −4 Original line number Diff line number Diff line Loading @@ -91,11 +91,12 @@ export class PopupAndRedirectBlockingChild extends JSWindowActorChild { ); const result = []; for (let i = 0; i < length; ++i) { const popup = state.popups[i]; for (let reportIndex = 0; reportIndex < length; ++reportIndex) { const popup = state.popups[reportIndex]; const { popupWindowURISpec } = popup; result.push({ popupWindowURISpec, reportIndex, }); } Loading @@ -114,8 +115,8 @@ export class PopupAndRedirectBlockingChild extends JSWindowActorChild { } #unblockPopup(aMessage) { const idx = aMessage.data.index; const popup = this.#getOrCreateDocState().popups[idx]; const reportIndex = aMessage.data.reportIndex; const popup = this.#getOrCreateDocState().popups[reportIndex]; if (popup?.requestingWindow?.document == popup.requestingDocument) { popup.requestingWindow.open( Loading
toolkit/actors/PopupAndRedirectBlockingParent.sys.mjs +10 −5 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ export class PopupAndRedirectBlocker { browsingContext: currentBC, innerWindowId: currentWG.innerWindowId, popupWindowURISpec: popup.popupWindowURISpec, reportIndex: popup.reportIndex, }); } } Loading Loading @@ -121,14 +122,14 @@ export class PopupAndRedirectBlocker { }; } unblockPopup(aBrowsingContext, aInnerWindowId, aPopupIndex) { unblockPopup(aBrowsingContext, aInnerWindowId, aReportIndex) { const sourceWG = aBrowsingContext.currentWindowGlobal; if (sourceWG?.innerWindowId != aInnerWindowId) { return; } const actor = sourceWG.getActor("PopupAndRedirectBlocking"); actor.sendAsyncMessage("UnblockPopup", { index: aPopupIndex }); actor.sendAsyncMessage("UnblockPopup", { reportIndex: aReportIndex }); } unblockRedirect(aBrowsingContext, aInnerWindowId, aRedirectURISpec) { Loading @@ -145,9 +146,13 @@ export class PopupAndRedirectBlocker { async unblockAllPopups() { const popups = await this.getBlockedPopups(); for (let idx = 0; idx < popups.length; ++idx) { const popup = popups[idx]; this.unblockPopup(popup.browsingContext, popup.innerWindowId, idx); for (let i = 0; i < popups.length; ++i) { const popup = popups[i]; this.unblockPopup( popup.browsingContext, popup.innerWindowId, popup.reportIndex ); } } Loading