diff options
Diffstat (limited to '')
9 files changed, 757 insertions, 0 deletions
diff --git a/browser/base/content/test/alerts/browser.toml b/browser/base/content/test/alerts/browser.toml new file mode 100644 index 0000000000..d0d56f7392 --- /dev/null +++ b/browser/base/content/test/alerts/browser.toml @@ -0,0 +1,28 @@ +[DEFAULT] +support-files = [ + "head.js", + "file_dom_notifications.html", +] + +["browser_notification_close.js"] +https_first_disabled = true +skip-if = ["os == 'win'"] # Bug 1227785 + +["browser_notification_do_not_disturb.js"] +https_first_disabled = true + +["browser_notification_open_settings.js"] +https_first_disabled = true +skip-if = ["os == 'win'"] # Bug 1411118 + +["browser_notification_remove_permission.js"] +https_first_disabled = true +skip-if = ["os == 'win'"] # Bug 1411118 + +["browser_notification_replace.js"] +https_first_disabled = true +skip-if = ["os == 'win'"] # Bug 1422928 + +["browser_notification_tab_switching.js"] +https_first_disabled = true +skip-if = ["os == 'win'"] # Bug 1243263 diff --git a/browser/base/content/test/alerts/browser_notification_close.js b/browser/base/content/test/alerts/browser_notification_close.js new file mode 100644 index 0000000000..7568f1cc2d --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_close.js @@ -0,0 +1,108 @@ +"use strict"; + +const { PlacesTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PlacesTestUtils.sys.mjs" +); + +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +let notificationURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; +let oldShowFavicons; + +add_task(async function test_notificationClose() { + let notificationURI = makeURI(notificationURL); + await addNotificationPermission(notificationURL); + + oldShowFavicons = Services.prefs.getBoolPref("alerts.showFavicons"); + Services.prefs.setBoolPref("alerts.showFavicons", true); + + await PlacesTestUtils.addVisits(notificationURI); + let faviconURI = await new Promise(resolve => { + let uri = makeURI( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC" + ); + PlacesUtils.favicons.setAndFetchFaviconForPage( + notificationURI, + uri, + true, + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, + uriResult => resolve(uriResult), + Services.scriptSecurityManager.getSystemPrincipal() + ); + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: notificationURL, + }, + async function dummyTabTask(aBrowser) { + await openNotification(aBrowser, "showNotification2"); + + info("Notification alert showing"); + + let alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + if (!alertWindow) { + ok(true, "Notifications don't use XUL windows on all platforms."); + await closeNotification(aBrowser); + return; + } + + let alertTitleLabel = + alertWindow.document.getElementById("alertTitleLabel"); + is( + alertTitleLabel.value, + "Test title", + "Title text of notification should be present" + ); + let alertTextLabel = + alertWindow.document.getElementById("alertTextLabel"); + is( + alertTextLabel.textContent, + "Test body 2", + "Body text of notification should be present" + ); + let alertIcon = alertWindow.document.getElementById("alertIcon"); + is( + alertIcon.src, + faviconURI.spec, + "Icon of notification should be present" + ); + + let alertCloseButton = alertWindow.document.querySelector(".close-icon"); + is(alertCloseButton.localName, "toolbarbutton", "close button found"); + let promiseBeforeUnloadEvent = BrowserTestUtils.waitForEvent( + alertWindow, + "beforeunload" + ); + let closedTime = alertWindow.Date.now(); + alertCloseButton.click(); + info("Clicked on close button"); + await promiseBeforeUnloadEvent; + + ok(true, "Alert should close when the close button is clicked"); + let currentTime = alertWindow.Date.now(); + // The notification will self-close at 12 seconds, so this checks + // that the notification closed before the timeout. + Assert.less( + currentTime - closedTime, + 5000, + "Close requested at " + + closedTime + + ", actually closed at " + + currentTime + ); + } + ); +}); + +add_task(async function cleanup() { + PermissionTestUtils.remove(notificationURL, "desktop-notification"); + if (typeof oldShowFavicons == "boolean") { + Services.prefs.setBoolPref("alerts.showFavicons", oldShowFavicons); + } +}); diff --git a/browser/base/content/test/alerts/browser_notification_do_not_disturb.js b/browser/base/content/test/alerts/browser_notification_do_not_disturb.js new file mode 100644 index 0000000000..8fb5a8a52b --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_do_not_disturb.js @@ -0,0 +1,160 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests that notifications can be silenced using nsIAlertsDoNotDisturb + * on systems where that interface and its methods are implemented for + * the nsIAlertService. + */ + +const ALERT_SERVICE = Cc["@mozilla.org/alerts-service;1"] + .getService(Ci.nsIAlertsService) + .QueryInterface(Ci.nsIAlertsDoNotDisturb); + +const PAGE = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; + +// The amount of time in seconds that we will wait for a notification +// to show up before we decide that it's not coming. +const NOTIFICATION_TIMEOUT_SECS = 2000; + +add_setup(async function () { + await addNotificationPermission(PAGE); +}); + +/** + * Test that the manualDoNotDisturb attribute can prevent + * notifications from appearing. + */ +add_task(async function test_manualDoNotDisturb() { + try { + // Only run the test if the do-not-disturb + // interface has been implemented. + ALERT_SERVICE.manualDoNotDisturb; + ok(true, "Alert service implements do-not-disturb interface"); + } catch (e) { + ok( + true, + "Alert service doesn't implement do-not-disturb interface, exiting test" + ); + return; + } + + // In the event that something goes wrong during this test, make sure + // we put the attribute back to the default setting when this test file + // exits. + registerCleanupFunction(() => { + ALERT_SERVICE.manualDoNotDisturb = false; + }); + + // Make sure that do-not-disturb is not enabled before we start. + ok( + !ALERT_SERVICE.manualDoNotDisturb, + "Alert service should not be disabled when test starts" + ); + + await BrowserTestUtils.withNewTab(PAGE, async browser => { + await openNotification(browser, "showNotification2"); + + info("Notification alert showing"); + + let alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + + // For now, only the XUL alert backend implements the manualDoNotDisturb + // method for nsIAlertsDoNotDisturb, so we expect there to be a XUL alert + // window. If the method gets implemented by native backends in the future, + // we'll probably want to branch here and set the manualDoNotDisturb + // attribute manually. + ok(alertWindow, "Expected a XUL alert window."); + + // We're using the XUL notification backend. This means that there's + // a menuitem for enabling manualDoNotDisturb. We exercise that + // menuitem here. + let doNotDisturbMenuItem = alertWindow.document.getElementById( + "doNotDisturbMenuItem" + ); + is(doNotDisturbMenuItem.localName, "menuitem", "menuitem found"); + + let unloadPromise = BrowserTestUtils.waitForEvent( + alertWindow, + "beforeunload" + ); + + doNotDisturbMenuItem.click(); + info("Clicked on do-not-disturb menuitem"); + await unloadPromise; + + // At this point, we should be configured to not display notifications + // to the user. + ok( + ALERT_SERVICE.manualDoNotDisturb, + "Alert service should be disabled after clicking menuitem" + ); + + // The notification should not appear, but there is no way from the + // client-side to know that it was blocked, except for waiting some time + // and realizing that the "onshow" event never fired. + await Assert.rejects( + openNotification(browser, "showNotification2", NOTIFICATION_TIMEOUT_SECS), + /timed out/, + "The notification should never display." + ); + + ALERT_SERVICE.manualDoNotDisturb = false; + }); +}); + +/** + * Test that the suppressForScreenSharing attribute can prevent + * notifications from appearing. + */ +add_task(async function test_suppressForScreenSharing() { + try { + // Only run the test if the do-not-disturb + // interface has been implemented. + ALERT_SERVICE.suppressForScreenSharing; + ok(true, "Alert service implements do-not-disturb interface"); + } catch (e) { + ok( + true, + "Alert service doesn't implement do-not-disturb interface, exiting test" + ); + return; + } + + // In the event that something goes wrong during this test, make sure + // we put the attribute back to the default setting when this test file + // exits. + registerCleanupFunction(() => { + ALERT_SERVICE.suppressForScreenSharing = false; + }); + + // Make sure that do-not-disturb is not enabled before we start. + ok( + !ALERT_SERVICE.suppressForScreenSharing, + "Alert service should not be suppressing for screen sharing when test " + + "starts" + ); + + await BrowserTestUtils.withNewTab(PAGE, async browser => { + await openNotification(browser, "showNotification2"); + + info("Notification alert showing"); + await closeNotification(browser); + ALERT_SERVICE.suppressForScreenSharing = true; + + // The notification should not appear, but there is no way from the + // client-side to know that it was blocked, except for waiting some time + // and realizing that the "onshow" event never fired. + await Assert.rejects( + openNotification(browser, "showNotification2", NOTIFICATION_TIMEOUT_SECS), + /timed out/, + "The notification should never display." + ); + }); + + ALERT_SERVICE.suppressForScreenSharing = false; +}); diff --git a/browser/base/content/test/alerts/browser_notification_open_settings.js b/browser/base/content/test/alerts/browser_notification_open_settings.js new file mode 100644 index 0000000000..ed51cd782b --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_open_settings.js @@ -0,0 +1,80 @@ +"use strict"; + +var notificationURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; +var expectedURL = "about:preferences#privacy"; + +add_task(async function test_settingsOpen_observer() { + info( + "Opening a dummy tab so openPreferences=>switchToTabHavingURI doesn't use the blank tab." + ); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "about:robots", + }, + async function dummyTabTask(aBrowser) { + // Ensure preferences is loaded before removing the tab. + let syncPaneLoadedPromise = TestUtils.topicObserved( + "sync-pane-loaded", + () => true + ); + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL); + info("simulate a notifications-open-settings notification"); + let uri = NetUtil.newURI("https://example.com"); + let principal = Services.scriptSecurityManager.createContentPrincipal( + uri, + {} + ); + Services.obs.notifyObservers(principal, "notifications-open-settings"); + let tab = await tabPromise; + ok(tab, "The notification settings tab opened"); + await syncPaneLoadedPromise; + BrowserTestUtils.removeTab(tab); + } + ); +}); + +add_task(async function test_settingsOpen_button() { + info("Adding notification permission"); + await addNotificationPermission(notificationURL); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: notificationURL, + }, + async function tabTask(aBrowser) { + info("Waiting for notification"); + await openNotification(aBrowser, "showNotification2"); + + let alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + if (!alertWindow) { + ok(true, "Notifications don't use XUL windows on all platforms."); + await closeNotification(aBrowser); + return; + } + + // Ensure preferences is loaded before removing the tab. + let syncPaneLoadedPromise = TestUtils.topicObserved( + "sync-pane-loaded", + () => true + ); + let closePromise = promiseWindowClosed(alertWindow); + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL); + let openSettingsMenuItem = alertWindow.document.getElementById( + "openSettingsMenuItem" + ); + openSettingsMenuItem.click(); + + info("Waiting for notification settings tab"); + let tab = await tabPromise; + ok(tab, "The notification settings tab opened"); + + await syncPaneLoadedPromise; + await closePromise; + BrowserTestUtils.removeTab(tab); + } + ); +}); diff --git a/browser/base/content/test/alerts/browser_notification_remove_permission.js b/browser/base/content/test/alerts/browser_notification_remove_permission.js new file mode 100644 index 0000000000..ba198870a3 --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_remove_permission.js @@ -0,0 +1,86 @@ +"use strict"; + +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +var tab; +var notificationURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; +var alertWindowClosed = false; +var permRemoved = false; + +function test() { + waitForExplicitFinish(); + + registerCleanupFunction(function () { + gBrowser.removeTab(tab); + window.restore(); + }); + + addNotificationPermission(notificationURL).then(function openTab() { + tab = BrowserTestUtils.addTab(gBrowser, notificationURL); + gBrowser.selectedTab = tab; + BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => onLoad()); + }); +} + +function onLoad() { + openNotification(tab.linkedBrowser, "showNotification2").then(onAlertShowing); +} + +function onAlertShowing() { + info("Notification alert showing"); + + let alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + if (!alertWindow) { + ok(true, "Notifications don't use XUL windows on all platforms."); + closeNotification(tab.linkedBrowser).then(finish); + return; + } + ok( + PermissionTestUtils.testExactPermission( + notificationURL, + "desktop-notification" + ), + "Permission should exist prior to removal" + ); + let disableForOriginMenuItem = alertWindow.document.getElementById( + "disableForOriginMenuItem" + ); + is(disableForOriginMenuItem.localName, "menuitem", "menuitem found"); + Services.obs.addObserver(permObserver, "perm-changed"); + alertWindow.addEventListener("beforeunload", onAlertClosing); + disableForOriginMenuItem.click(); + info("Clicked on disable-for-origin menuitem"); +} + +function permObserver(subject, topic, data) { + if (topic != "perm-changed") { + return; + } + + let permission = subject.QueryInterface(Ci.nsIPermission); + is( + permission.type, + "desktop-notification", + "desktop-notification permission changed" + ); + is(data, "deleted", "desktop-notification permission deleted"); + + Services.obs.removeObserver(permObserver, "perm-changed"); + permRemoved = true; + if (alertWindowClosed) { + finish(); + } +} + +function onAlertClosing(event) { + event.target.removeEventListener("beforeunload", onAlertClosing); + + alertWindowClosed = true; + if (permRemoved) { + finish(); + } +} diff --git a/browser/base/content/test/alerts/browser_notification_replace.js b/browser/base/content/test/alerts/browser_notification_replace.js new file mode 100644 index 0000000000..9c72e90ab1 --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_replace.js @@ -0,0 +1,66 @@ +"use strict"; + +let notificationURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; + +add_task(async function test_notificationReplace() { + await addNotificationPermission(notificationURL); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: notificationURL, + }, + async function dummyTabTask(aBrowser) { + await SpecialPowers.spawn(aBrowser, [], async function () { + let win = content.window.wrappedJSObject; + let notification = win.showNotification1(); + let promiseCloseEvent = ContentTaskUtils.waitForEvent( + notification, + "close" + ); + + let showEvent = await ContentTaskUtils.waitForEvent( + notification, + "show" + ); + Assert.equal( + showEvent.target.body, + "Test body 1", + "Showed tagged notification" + ); + + let newNotification = win.showNotification2(); + let newShowEvent = await ContentTaskUtils.waitForEvent( + newNotification, + "show" + ); + Assert.equal( + newShowEvent.target.body, + "Test body 2", + "Showed new notification with same tag" + ); + + let closeEvent = await promiseCloseEvent; + Assert.equal( + closeEvent.target.body, + "Test body 1", + "Closed previous tagged notification" + ); + + let promiseNewCloseEvent = ContentTaskUtils.waitForEvent( + newNotification, + "close" + ); + newNotification.close(); + let newCloseEvent = await promiseNewCloseEvent; + Assert.equal( + newCloseEvent.target.body, + "Test body 2", + "Closed new notification" + ); + }); + } + ); +}); diff --git a/browser/base/content/test/alerts/browser_notification_tab_switching.js b/browser/base/content/test/alerts/browser_notification_tab_switching.js new file mode 100644 index 0000000000..ee675670cb --- /dev/null +++ b/browser/base/content/test/alerts/browser_notification_tab_switching.js @@ -0,0 +1,117 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +var tab; +var notification; +var notificationURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html"; +var newWindowOpenedFromTab; + +add_task(async function test_notificationPreventDefaultAndSwitchTabs() { + await addNotificationPermission(notificationURL); + + let originalTab = gBrowser.selectedTab; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: notificationURL, + }, + async function dummyTabTask(aBrowser) { + // Put new tab in background so it is obvious when it is re-focused. + await BrowserTestUtils.switchTab(gBrowser, originalTab); + isnot( + gBrowser.selectedBrowser, + aBrowser, + "Notification page loaded as a background tab" + ); + + // First, show a notification that will be have the tab-switching prevented. + function promiseNotificationEvent(evt) { + return SpecialPowers.spawn( + aBrowser, + [evt], + async function (contentEvt) { + return new Promise(resolve => { + let contentNotification = content.wrappedJSObject._notification; + contentNotification.addEventListener( + contentEvt, + function (event) { + resolve({ defaultPrevented: event.defaultPrevented }); + }, + { once: true } + ); + }); + } + ); + } + await openNotification(aBrowser, "showNotification1"); + info("Notification alert showing"); + let alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + if (!alertWindow) { + ok(true, "Notifications don't use XUL windows on all platforms."); + await closeNotification(aBrowser); + return; + } + info("Clicking on notification"); + let promiseClickEvent = promiseNotificationEvent("click"); + + // NB: This executeSoon is needed to allow the non-e10s runs of this test + // a chance to set the event listener on the page. Otherwise, we + // synchronously fire the click event before we listen for the event. + executeSoon(() => { + EventUtils.synthesizeMouseAtCenter( + alertWindow.document.getElementById("alertTitleLabel"), + {}, + alertWindow + ); + }); + let clickEvent = await promiseClickEvent; + ok( + clickEvent.defaultPrevented, + "The event handler for the first notification cancels the event" + ); + isnot( + gBrowser.selectedBrowser, + aBrowser, + "Notification page still a background tab" + ); + let notificationClosed = promiseNotificationEvent("close"); + await closeNotification(aBrowser); + await notificationClosed; + + // Second, show a notification that will cause the tab to get switched. + await openNotification(aBrowser, "showNotification2"); + alertWindow = Services.wm.getMostRecentWindow("alert:alert"); + let promiseTabSelect = BrowserTestUtils.waitForEvent( + gBrowser.tabContainer, + "TabSelect" + ); + EventUtils.synthesizeMouseAtCenter( + alertWindow.document.getElementById("alertTitleLabel"), + {}, + alertWindow + ); + await promiseTabSelect; + is( + gBrowser.selectedBrowser.currentURI.spec, + notificationURL, + "Clicking on the second notification should select its originating tab" + ); + notificationClosed = promiseNotificationEvent("close"); + await closeNotification(aBrowser); + await notificationClosed; + } + ); +}); + +add_task(async function cleanup() { + PermissionTestUtils.remove(notificationURL, "desktop-notification"); +}); diff --git a/browser/base/content/test/alerts/file_dom_notifications.html b/browser/base/content/test/alerts/file_dom_notifications.html new file mode 100644 index 0000000000..6deede8fcf --- /dev/null +++ b/browser/base/content/test/alerts/file_dom_notifications.html @@ -0,0 +1,39 @@ +<html> +<head> +<meta charset="utf-8"> +<script> +"use strict"; + +function showNotification1() { + var options = { + dir: undefined, + lang: undefined, + body: "Test body 1", + tag: "Test tag", + icon: undefined, + }; + var n = new Notification("Test title", options); + n.addEventListener("click", function(event) { + event.preventDefault(); + }); + return n; +} + +function showNotification2() { + var options = { + dir: undefined, + lang: undefined, + body: "Test body 2", + tag: "Test tag", + icon: undefined, + }; + return new Notification("Test title", options); +} +</script> +</head> +<body> +<form id="notificationForm" onsubmit="showNotification();"> + <input type="submit" value="Show notification" id="submit"/> +</form> +</body> +</html> diff --git a/browser/base/content/test/alerts/head.js b/browser/base/content/test/alerts/head.js new file mode 100644 index 0000000000..4be18f6c41 --- /dev/null +++ b/browser/base/content/test/alerts/head.js @@ -0,0 +1,73 @@ +// Platforms may default to reducing motion. We override this to ensure the +// alert slide animation is enabled in tests. +SpecialPowers.pushPrefEnv({ + set: [["ui.prefersReducedMotion", 0]], +}); + +async function addNotificationPermission(originString) { + return SpecialPowers.pushPermissions([ + { + type: "desktop-notification", + allow: true, + context: originString, + }, + ]); +} + +/** + * Similar to `BrowserTestUtils.closeWindow`, but + * doesn't call `window.close()`. + */ +function promiseWindowClosed(window) { + return new Promise(function (resolve) { + Services.ww.registerNotification(function observer(subject, topic, data) { + if (topic == "domwindowclosed" && subject == window) { + Services.ww.unregisterNotification(observer); + resolve(); + } + }); + }); +} + +/** + * These two functions work with file_dom_notifications.html to open the + * notification and close it. + * + * |fn| can be showNotification1 or showNotification2. + * if |timeout| is passed, then the promise returned from this function is + * rejected after the requested number of miliseconds. + */ +function openNotification(aBrowser, fn, timeout) { + info(`openNotification: ${fn}`); + return SpecialPowers.spawn( + aBrowser, + [[fn, timeout]], + async function ([contentFn, contentTimeout]) { + await new Promise((resolve, reject) => { + let win = content.wrappedJSObject; + let notification = win[contentFn](); + win._notification = notification; + + function listener() { + notification.removeEventListener("show", listener); + resolve(); + } + + notification.addEventListener("show", listener); + + if (contentTimeout) { + content.setTimeout(() => { + notification.removeEventListener("show", listener); + reject("timed out"); + }, contentTimeout); + } + }); + } + ); +} + +function closeNotification(aBrowser) { + return SpecialPowers.spawn(aBrowser, [], function () { + content.wrappedJSObject._notification.close(); + }); +} |