summaryrefslogtreecommitdiffstats
path: root/dom/notification/test/mochitest
diff options
context:
space:
mode:
Diffstat (limited to 'dom/notification/test/mochitest')
-rw-r--r--dom/notification/test/mochitest/MockServices.js176
-rw-r--r--dom/notification/test/mochitest/NotificationTest.js102
-rw-r--r--dom/notification/test/mochitest/blank.html4
-rw-r--r--dom/notification/test/mochitest/create_notification.html16
-rw-r--r--dom/notification/test/mochitest/mochitest.toml30
-rw-r--r--dom/notification/test/mochitest/test_notification_basics.html125
-rw-r--r--dom/notification/test/mochitest/test_notification_crossorigin_iframe.html67
-rw-r--r--dom/notification/test/mochitest/test_notification_insecure_context.html42
-rw-r--r--dom/notification/test/mochitest/test_notification_permissions.html68
-rw-r--r--dom/notification/test/mochitest/test_notification_tag.html169
10 files changed, 799 insertions, 0 deletions
diff --git a/dom/notification/test/mochitest/MockServices.js b/dom/notification/test/mochitest/MockServices.js
new file mode 100644
index 0000000000..4b2526c26a
--- /dev/null
+++ b/dom/notification/test/mochitest/MockServices.js
@@ -0,0 +1,176 @@
+var MockServices = (function () {
+ "use strict";
+
+ const MOCK_ALERTS_CID = SpecialPowers.wrap(SpecialPowers.Components).ID(
+ "{48068bc2-40ab-4904-8afd-4cdfb3a385f3}"
+ );
+ const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
+
+ const MOCK_SYSTEM_ALERTS_CID = SpecialPowers.wrap(
+ SpecialPowers.Components
+ ).ID("{e86d888c-e41b-4b78-9104-2f2742a532de}");
+ const SYSTEM_ALERTS_SERVICE_CONTRACT_ID =
+ "@mozilla.org/system-alerts-service;1";
+
+ var registrar = SpecialPowers.wrap(
+ SpecialPowers.Components
+ ).manager.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar);
+
+ var activeAlertNotifications = Object.create(null);
+
+ var activeAppNotifications = Object.create(null);
+
+ window.addEventListener("mock-notification-close-event", function (e) {
+ for (var alertName in activeAlertNotifications) {
+ var notif = activeAlertNotifications[alertName];
+ if (notif.title === e.detail.title) {
+ notif.listener.observe(null, "alertfinished", null);
+ delete activeAlertNotifications[alertName];
+ delete activeAppNotifications[alertName];
+ return;
+ }
+ }
+ });
+
+ var mockAlertsService = {
+ showPersistentNotification(persistentData, alert, alertListener) {
+ this.showAlert(alert, alertListener);
+ },
+
+ showAlert(alert, alertListener) {
+ var listener = SpecialPowers.wrap(alertListener);
+ activeAlertNotifications[alert.name] = {
+ listener,
+ cookie: alert.cookie,
+ title: alert.title,
+ };
+
+ // fake async alert show event
+ if (listener) {
+ setTimeout(function () {
+ listener.observe(null, "alertshow", alert.cookie);
+ }, 100);
+ setTimeout(function () {
+ listener.observe(null, "alertclickcallback", alert.cookie);
+ }, 100);
+ }
+ },
+
+ showAlertNotification(
+ imageUrl,
+ title,
+ text,
+ textClickable,
+ cookie,
+ alertListener,
+ name
+ ) {
+ this.showAlert(
+ {
+ name,
+ cookie,
+ title,
+ },
+ alertListener
+ );
+ },
+
+ closeAlert(name) {
+ var alertNotification = activeAlertNotifications[name];
+ if (alertNotification) {
+ if (alertNotification.listener) {
+ alertNotification.listener.observe(
+ null,
+ "alertfinished",
+ alertNotification.cookie
+ );
+ }
+ delete activeAlertNotifications[name];
+ }
+
+ var appNotification = activeAppNotifications[name];
+ if (appNotification) {
+ delete activeAppNotifications[name];
+ }
+ },
+
+ QueryInterface(aIID) {
+ if (
+ SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
+ SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)
+ ) {
+ return this;
+ }
+ throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ createInstance(aIID) {
+ return this.QueryInterface(aIID);
+ },
+ };
+ mockAlertsService = SpecialPowers.wrapCallbackObject(mockAlertsService);
+
+ // MockServices API
+ return {
+ register() {
+ try {
+ this.originalAlertsCID = registrar.contractIDToCID(
+ ALERTS_SERVICE_CONTRACT_ID
+ );
+ } catch (ex) {
+ this.originalAlertsCID = null;
+ }
+ try {
+ this.originalSystemAlertsCID = registrar.contractIDToCID(
+ SYSTEM_ALERTS_SERVICE_CONTRACT_ID
+ );
+ } catch (ex) {
+ this.originalSystemAlertsCID = null;
+ }
+
+ registrar.registerFactory(
+ MOCK_ALERTS_CID,
+ "alerts service",
+ ALERTS_SERVICE_CONTRACT_ID,
+ mockAlertsService
+ );
+
+ registrar.registerFactory(
+ MOCK_SYSTEM_ALERTS_CID,
+ "system alerts service",
+ SYSTEM_ALERTS_SERVICE_CONTRACT_ID,
+ mockAlertsService
+ );
+ },
+
+ unregister() {
+ registrar.unregisterFactory(MOCK_ALERTS_CID, mockAlertsService);
+ registrar.unregisterFactory(MOCK_SYSTEM_ALERTS_CID, mockAlertsService);
+
+ // Passing `null` for the factory re-maps the contract ID to the
+ // entry for its original CID.
+
+ if (this.originalAlertsCID) {
+ registrar.registerFactory(
+ this.originalAlertsCID,
+ "alerts service",
+ ALERTS_SERVICE_CONTRACT_ID,
+ null
+ );
+ }
+
+ if (this.originalSystemAlertsCID) {
+ registrar.registerFactory(
+ this.originalSystemAlertsCID,
+ "system alerts service",
+ SYSTEM_ALERTS_SERVICE_CONTRACT_ID,
+ null
+ );
+ }
+ },
+
+ activeAlertNotifications,
+
+ activeAppNotifications,
+ };
+})();
diff --git a/dom/notification/test/mochitest/NotificationTest.js b/dom/notification/test/mochitest/NotificationTest.js
new file mode 100644
index 0000000000..400ff56253
--- /dev/null
+++ b/dom/notification/test/mochitest/NotificationTest.js
@@ -0,0 +1,102 @@
+var NotificationTest = (function () {
+ "use strict";
+
+ function info(msg, name) {
+ SimpleTest.info("::Notification Tests::" + (name || ""), msg);
+ }
+
+ function setup_testing_env() {
+ SimpleTest.waitForExplicitFinish();
+ // turn on testing pref (used by notification.cpp, and mock the alerts
+ return SpecialPowers.setBoolPref("notification.prompt.testing", true);
+ }
+
+ async function teardown_testing_env() {
+ await SpecialPowers.clearUserPref("notification.prompt.testing");
+ await SpecialPowers.clearUserPref("notification.prompt.testing.allow");
+
+ SimpleTest.finish();
+ }
+
+ function executeTests(tests, callback) {
+ // context is `this` object in test functions
+ // it can be used to track data between tests
+ var context = {};
+
+ (function executeRemainingTests(remainingTests) {
+ if (!remainingTests.length) {
+ callback();
+ return;
+ }
+
+ var nextTest = remainingTests.shift();
+ var finishTest = executeRemainingTests.bind(null, remainingTests);
+ var startTest = nextTest.call.bind(nextTest, context, finishTest);
+
+ try {
+ startTest();
+ // if no callback was defined for test function,
+ // we must manually invoke finish to continue
+ if (nextTest.length === 0) {
+ finishTest();
+ }
+ } catch (e) {
+ ok(false, "Test threw exception!");
+ finishTest();
+ }
+ })(tests);
+ }
+
+ // NotificationTest API
+ return {
+ run(tests, callback) {
+ let ready = setup_testing_env();
+
+ addLoadEvent(async function () {
+ await ready;
+ executeTests(tests, function () {
+ teardown_testing_env();
+ callback && callback();
+ });
+ });
+ },
+
+ allowNotifications() {
+ return SpecialPowers.setBoolPref(
+ "notification.prompt.testing.allow",
+ true
+ );
+ },
+
+ denyNotifications() {
+ return SpecialPowers.setBoolPref(
+ "notification.prompt.testing.allow",
+ false
+ );
+ },
+
+ clickNotification(notification) {
+ // TODO: how??
+ },
+
+ fireCloseEvent(title) {
+ window.dispatchEvent(
+ new CustomEvent("mock-notification-close-event", {
+ detail: {
+ title,
+ },
+ })
+ );
+ },
+
+ info,
+
+ payload: {
+ body: "Body",
+ tag: "fakeTag",
+ icon: "icon.jpg",
+ lang: "en-US",
+ dir: "ltr",
+ },
+ };
+})();
diff --git a/dom/notification/test/mochitest/blank.html b/dom/notification/test/mochitest/blank.html
new file mode 100644
index 0000000000..1f9324523a
--- /dev/null
+++ b/dom/notification/test/mochitest/blank.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+ <body></body>
+</html>
diff --git a/dom/notification/test/mochitest/create_notification.html b/dom/notification/test/mochitest/create_notification.html
new file mode 100644
index 0000000000..b0387e4ffb
--- /dev/null
+++ b/dom/notification/test/mochitest/create_notification.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset=utf-8>
+ <title>Create a notification</title>
+</head>
+<body>
+<script>
+
+var notification = new Notification("This is a title", {
+ body: "This is a notification body",
+ tag: "sometag",
+ });
+
+</script>
+</body>
+</html>
diff --git a/dom/notification/test/mochitest/mochitest.toml b/dom/notification/test/mochitest/mochitest.toml
new file mode 100644
index 0000000000..19320326fc
--- /dev/null
+++ b/dom/notification/test/mochitest/mochitest.toml
@@ -0,0 +1,30 @@
+[DEFAULT]
+
+support-files = [
+ "blank.html",
+ "create_notification.html",
+ "MockServices.js",
+ "NotificationTest.js",
+]
+
+["test_notification_basics.html"]
+skip-if = ["xorigin"] # Bug 1792790
+
+["test_notification_crossorigin_iframe.html"]
+scheme = "https"
+# This test needs to be run on HTTP (not HTTPS).
+
+["test_notification_insecure_context.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_notification_permissions.html"]
+scheme = "https"
+
+["test_notification_tag.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
diff --git a/dom/notification/test/mochitest/test_notification_basics.html b/dom/notification/test/mochitest/test_notification_basics.html
new file mode 100644
index 0000000000..3dde839a96
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_basics.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Notification Basics</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="MockServices.js"></script>
+ <script type="text/javascript" src="NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script type="text/javascript">
+
+ var info = NotificationTest.info;
+ var options;
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ var steps = [
+ function() {
+ info("Test notification spec");
+ ok(Notification, "Notification constructor exists");
+ ok(Notification.permission, "Notification.permission exists");
+ ok(Notification.requestPermission, "Notification.requestPermission exists");
+ },
+
+ function() {
+ info("Test requestPermission without callback");
+ Notification.requestPermission();
+ },
+
+ async function(done) {
+ info("Test requestPermission deny");
+ function assertPermissionDenied(perm) {
+ is(perm, "denied", "Permission should be denied.");
+ is(Notification.permission, "denied", "Permission should be denied.");
+ }
+ await NotificationTest.denyNotifications();
+ Notification.requestPermission()
+ .then(assertPermissionDenied)
+ .then(_ => Notification.requestPermission(assertPermissionDenied))
+ .catch(err => {
+ ok(!err, "requestPermission should not reject promise");
+ })
+ .then(done);
+ },
+
+ async function(done) {
+ info("Test requestPermission grant");
+ function assertPermissionGranted(perm) {
+ is(perm, "granted", "Permission should be granted.");
+ is(Notification.permission, "granted", "Permission should be granted");
+ }
+ await NotificationTest.allowNotifications();
+ Notification.requestPermission()
+ .then(assertPermissionGranted)
+ .then(_ => Notification.requestPermission(assertPermissionGranted))
+ .catch(err => {
+ ok(!err, "requestPermission should not reject promise");
+ })
+ .then(done);
+ },
+
+ function(done) {
+ info("Test invalid requestPermission");
+ Notification.requestPermission({})
+ .then(_ => {
+ ok(false, "Non callable arg to requestPermission should reject promise");
+ }, err => {
+ ok(true, "Non callable arg to requestPermission should reject promise");
+ })
+ .then(done);
+ },
+
+ function(done) {
+ info("Test create notification");
+
+ options = NotificationTest.payload;
+
+ var notification = new Notification("This is a title", options);
+
+ ok(notification, "Notification exists");
+ is(notification.onclick, null, "onclick() should be null");
+ is(notification.onshow, null, "onshow() should be null");
+ is(notification.onerror, null, "onerror() should be null");
+ is(notification.onclose, null, "onclose() should be null");
+ is(typeof notification.close, "function", "close() should exist");
+
+ is(notification.dir, options.dir, "auto should get set");
+ is(notification.lang, options.lang, "lang should get set");
+ is(notification.body, options.body, "body should get set");
+ is(notification.tag, options.tag, "tag should get set");
+ is(notification.icon, options.icon, "icon should get set");
+
+ // store notification in test context
+ this.notification = notification;
+
+ notification.onshow = function() {
+ ok(true, "onshow handler should be called");
+ done();
+ };
+ },
+
+ function(done) {
+ info("Test closing a notification");
+ var notification = this.notification;
+
+ notification.onclose = function() {
+ ok(true, "onclose handler should be called");
+ done();
+ };
+
+ notification.close();
+ },
+ ];
+
+ MockServices.register();
+ NotificationTest.run(steps, function() {
+ MockServices.unregister();
+ });
+</script>
+</body>
+</html>
diff --git a/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html b/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html
new file mode 100644
index 0000000000..0abfce3722
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests that Notification permissions are denied in cross-origin iframes.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
+-->
+<head>
+ <title>Notification permission in cross-origin iframes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ const kBlankURL = "https://example.org/tests/dom/notification/test/mochitest/blank.html";
+
+ (async function runTest() {
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["notification.prompt.testing", true],
+ ["notification.prompt.testing.allow", true],
+ ]});
+
+ let iframe = document.createElement("iframe");
+ iframe.src = kBlankURL;
+ document.body.appendChild(iframe);
+ await new Promise(resolve => {
+ iframe.onload = resolve;
+ });
+
+ let checkRequest = async (expectedResponse, msg) => {
+ let response = await this.content.Notification.requestPermission();
+ Assert.equal(response, expectedResponse, msg);
+ };
+
+ await SpecialPowers.spawn(iframe,
+ ["denied", "Denied permission in cross-origin iframe"],
+ checkRequest);
+
+ let checkPermission = async (expectedPermission, msg) => {
+ let permission = this.content.Notification.permission;
+ Assert.equal(permission, expectedPermission, msg);
+ };
+
+ await SpecialPowers.spawn(iframe,
+ ["denied", "Permission is denied in cross-origin iframe"],
+ checkPermission);
+
+ await SpecialPowers.pushPrefEnv({"set": [["dom.webnotifications.allowcrossoriginiframe", true]]});
+
+ await SpecialPowers.spawn(iframe,
+ ["granted", "Granted permission in cross-origin iframe with pref set"],
+ checkRequest);
+ await SpecialPowers.spawn(iframe,
+ ["granted", "Permission is granted in cross-origin iframe with pref set"],
+ checkPermission);
+
+ SimpleTest.finish();
+ })();
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/notification/test/mochitest/test_notification_insecure_context.html b/dom/notification/test/mochitest/test_notification_insecure_context.html
new file mode 100644
index 0000000000..bb64db64a9
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_insecure_context.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests that Notification permissions are denied in insecure context.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1429432
+-->
+<head>
+ <title>Notification permission in insecure context</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ // Add an allow permission for the mochitest origin to test this.
+ let script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin("http://mochi.test:8888");
+ Services.perms.addFromPrincipal(principal, "desktop-notification", Services.perms.ALLOW_ACTION);
+ addMessageListener("destroy", function() {
+ Services.perms.removeFromPrincipal(principal, "desktop-notification");
+ });
+ });
+
+ (async function runTest() {
+ let response = await Notification.requestPermission();
+ is(response, "denied", "Denied permission in insecure context");
+
+ script.sendAsyncMessage("destroy");
+ script.destroy();
+
+ SimpleTest.finish();
+ })();
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/notification/test/mochitest/test_notification_permissions.html b/dom/notification/test/mochitest/test_notification_permissions.html
new file mode 100644
index 0000000000..0290fb9c87
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_permissions.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests that the Notification.requestPermission and navigator.permissions.query
+return values are consistent with the stored permission.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1589754
+-->
+<head>
+ <title>Notification permissions and permissions API</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+<script class="testbody">
+
+add_task(async function test_notifications_permission() {
+ await SpecialPowers.clearUserPref("notification.prompt.testing");
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // Automatically dismiss the permission request when it appears.
+ ["dom.webnotifications.requireuserinteraction", true],
+ ],
+ });
+
+ async function testPermissionInWindow(win) {
+ async function checkPermission(perm, expectedResult, expectedPermission) {
+ await SpecialPowers.pushPermissions([
+ {
+ type: "desktop-notification",
+ allow: SpecialPowers.Ci.nsIPermissionManager[perm],
+ context: document,
+ },
+ ]);
+ is(
+ await win.Notification.requestPermission(),
+ expectedResult,
+ `expected requestPermission() result for permission ${perm}`
+ );
+
+ let result =
+ await win.navigator.permissions.query({ name: "notifications" });
+ is(
+ result.state,
+ expectedPermission,
+ `expected permissions API result for permission ${perm}`
+ );
+ }
+
+ await checkPermission("UNKNOWN_ACTION", "default", "prompt");
+ await checkPermission("ALLOW_ACTION", "granted", "granted");
+ await checkPermission("DENY_ACTION", "denied", "denied");
+ await checkPermission("PROMPT_ACTION", "default", "prompt");
+ }
+
+ var win = window.open("blank.html");
+ await new Promise(resolve => { win.onload = resolve; });
+ await testPermissionInWindow(win);
+ win.close();
+});
+
+</script>
+ </pre>
+</body>
+</html>
diff --git a/dom/notification/test/mochitest/test_notification_tag.html b/dom/notification/test/mochitest/test_notification_tag.html
new file mode 100644
index 0000000000..f4fc72bbe3
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_tag.html
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=782211
+-->
+<head>
+ <title>Bug 782211</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782211">Bug 782211</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ /* eslint-disable mozilla/use-chromeutils-generateqi */
+
+ // The mock is not a general purpose mock, but is specific for this test.
+ // It is always registered in the parent process using LoadChromeScript by
+ // the MockAlertsService below, to allow this to work regardless of whether
+ // the frames from different origins live in the same process or in different
+ // processes (with Fission), since the default content-process alerts service
+ // relays messages to the parent process.
+ function mockServicesChromeScript() {
+ /* eslint-env mozilla/chrome-script */
+ const MOCK_CID = Components.ID("{dbe37e64-d9a3-402c-8d8a-0826c619f7ad}");
+ const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
+
+ var notificationsCreated = [];
+
+ const mockAlertsService = {
+ showAlert(alert, alertListener) {
+ notificationsCreated.push(alert.name);
+ if (notificationsCreated.length == 3) {
+ // notifications created by the test1 origin
+ var test1notifications = [];
+ // notifications created by the test2 origin
+ var test2notifications = [];
+ for (var i = 0; i < notificationsCreated.length; i++) {
+ var notificationName = notificationsCreated[i];
+ if (notificationName.includes("test1")) {
+ test1notifications.push(notificationsCreated[i]);
+ } else if (notificationName.includes("test2")) {
+ test2notifications.push(notificationsCreated[i]);
+ }
+ }
+
+ is(
+ test1notifications.length,
+ 2,
+ "2 notifications should be created by test1.example.org:80 origin."
+ );
+ is(
+ test1notifications[0],
+ test1notifications[1],
+ "notification names should be identical."
+ );
+ is(
+ test2notifications.length,
+ 1,
+ "1 notification should be created by test2.example.org:80 origin."
+ );
+
+ // Register original alerts service.
+ registrar.unregisterFactory(MOCK_CID, this);
+
+ sendAsyncMessage("mock-alert-service:unregistered");
+ }
+ },
+
+ showAlertNotification(
+ imageUrl,
+ title,
+ text,
+ textClickable,
+ cookie,
+ alertListener,
+ name,
+ dir,
+ lang,
+ data
+ ) {
+ this.showAlert({ name });
+ },
+
+ QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsISupports) || aIID.equals(Ci.nsIAlertsService)) {
+ return this;
+ }
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+
+ createInstance(aIID) {
+ return this.QueryInterface(aIID);
+ },
+ };
+
+ const registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+ registrar.registerFactory(
+ MOCK_CID,
+ "alerts service",
+ ALERTS_SERVICE_CONTRACT_ID,
+ mockAlertsService
+ );
+
+ const { sendAsyncMessage } = this;
+
+ sendAsyncMessage("mock-alert-service:registered");
+ }
+
+ const MockAlertsService = {
+ async register() {
+ if (this._chromeScript) {
+ throw new Error("MockAlertsService already registered");
+ }
+ this._chromeScript = SpecialPowers.loadChromeScript(
+ mockServicesChromeScript
+ );
+ await this._chromeScript.promiseOneMessage("mock-alert-service:registered");
+ },
+ async unregistered() {
+ await this._chromeScript.promiseOneMessage(
+ "mock-alert-service:unregistered"
+ );
+ },
+ };
+
+ if (window.Notification) {
+ SimpleTest.waitForExplicitFinish();
+
+ async function showNotifications() {
+ await MockAlertsService.register();
+
+ // Load two frames with the same origin that create notification with the same tag.
+ // Both pages should generate notifications with the same name, and thus the second
+ // notification should replace the first.
+ let sameDomain = window.open("http://test1.example.org:80/tests/dom/notification/test/mochitest/create_notification.html");
+ let anotherSameDomain = window.open("http://test1.example.org:80/tests/dom/notification/test/mochitest/create_notification.html");
+ // Load a frame with a different origin that creates a notification with the same tag.
+ // The notification name should be different and thus no notifications should be replaced.
+ let crossDomain = window.open("http://test2.example.org:80/tests/dom/notification/test/mochitest/create_notification.html");
+
+ await MockAlertsService.unregistered();
+
+ sameDomain.close();
+ anotherSameDomain.close();
+ crossDomain.close();
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["notification.prompt.testing", true],
+ ["notification.prompt.testing.allow", true],
+ ],
+ },
+ showNotifications
+ );
+ } else {
+ ok(true, "Notifications are not enabled on the platform.");
+ }
+</script>
+</body>
+</html>