summaryrefslogtreecommitdiffstats
path: root/toolkit/components/alerts/test
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/alerts/test')
-rw-r--r--toolkit/components/alerts/test/browser.toml4
-rw-r--r--toolkit/components/alerts/test/browser_bug1682866.js56
-rw-r--r--toolkit/components/alerts/test/file_bug1682866.html9
-rw-r--r--toolkit/components/alerts/test/image.gifbin0 -> 60901 bytes
-rw-r--r--toolkit/components/alerts/test/image.pngbin0 -> 2531 bytes
-rw-r--r--toolkit/components/alerts/test/image_server.sjs92
-rw-r--r--toolkit/components/alerts/test/mochitest.toml28
-rw-r--r--toolkit/components/alerts/test/test_alerts.html88
-rw-r--r--toolkit/components/alerts/test/test_alerts_noobserve.html93
-rw-r--r--toolkit/components/alerts/test/test_alerts_requireinteraction.html164
-rw-r--r--toolkit/components/alerts/test/test_image.html117
-rw-r--r--toolkit/components/alerts/test/test_invalid_utf16.html160
-rw-r--r--toolkit/components/alerts/test/test_multiple_alerts.html99
-rw-r--r--toolkit/components/alerts/test/test_principal.html123
14 files changed, 1033 insertions, 0 deletions
diff --git a/toolkit/components/alerts/test/browser.toml b/toolkit/components/alerts/test/browser.toml
new file mode 100644
index 0000000000..61724f2c12
--- /dev/null
+++ b/toolkit/components/alerts/test/browser.toml
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+["browser_bug1682866.js"]
+support-files = ["file_bug1682866.html"]
diff --git a/toolkit/components/alerts/test/browser_bug1682866.js b/toolkit/components/alerts/test/browser_bug1682866.js
new file mode 100644
index 0000000000..24b8b6f7b8
--- /dev/null
+++ b/toolkit/components/alerts/test/browser_bug1682866.js
@@ -0,0 +1,56 @@
+const baseURL = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+const alertURL = `${baseURL}file_bug1682866.html`;
+
+add_task(async function testAlertForceClosed() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ alertURL,
+ true /* waitForLoad */
+ );
+
+ // Open a second which is in the same process as tab
+ let secondTabIsLoaded = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ alertURL,
+ true,
+ false
+ );
+
+ let isSuspendedAfterAlert = await SpecialPowers.spawn(
+ tab.linkedBrowser.browsingContext,
+ [alertURL],
+ url => {
+ content.open(url);
+ var utils = SpecialPowers.getDOMWindowUtils(content);
+ return utils.isInputTaskManagerSuspended;
+ }
+ );
+
+ await secondTabIsLoaded;
+
+ let secondTab = gBrowser.tabs[2];
+
+ is(
+ isSuspendedAfterAlert,
+ Services.prefs.getBoolPref("dom.input_events.canSuspendInBCG.enabled"),
+ "InputTaskManager should be suspended because alert is opened"
+ );
+
+ let alertClosed = BrowserTestUtils.waitForEvent(
+ tab.linkedBrowser,
+ "DOMModalDialogClosed"
+ );
+
+ BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, "about:newtab");
+
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ await alertClosed;
+
+ gBrowser.removeTab(tab);
+ gBrowser.removeTab(secondTab);
+});
diff --git a/toolkit/components/alerts/test/file_bug1682866.html b/toolkit/components/alerts/test/file_bug1682866.html
new file mode 100644
index 0000000000..328edcbac5
--- /dev/null
+++ b/toolkit/components/alerts/test/file_bug1682866.html
@@ -0,0 +1,9 @@
+<html>
+ <body>
+ <script>
+ window.onload = function() {
+ alert("This is an alert");
+ }
+ </script>
+ </body>
+</html>
diff --git a/toolkit/components/alerts/test/image.gif b/toolkit/components/alerts/test/image.gif
new file mode 100644
index 0000000000..053b4d9261
--- /dev/null
+++ b/toolkit/components/alerts/test/image.gif
Binary files differ
diff --git a/toolkit/components/alerts/test/image.png b/toolkit/components/alerts/test/image.png
new file mode 100644
index 0000000000..430c3c5e65
--- /dev/null
+++ b/toolkit/components/alerts/test/image.png
Binary files differ
diff --git a/toolkit/components/alerts/test/image_server.sjs b/toolkit/components/alerts/test/image_server.sjs
new file mode 100644
index 0000000000..4caa21ce27
--- /dev/null
+++ b/toolkit/components/alerts/test/image_server.sjs
@@ -0,0 +1,92 @@
+const CC = Components.Constructor;
+
+let { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const LocalFile = CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
+
+const FileInputStream = CC(
+ "@mozilla.org/network/file-input-stream;1",
+ "nsIFileInputStream",
+ "init"
+);
+
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ let params = parseQueryString(request.queryString);
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ // Compare and increment a cookie for this request. This is used to test
+ // private browsing mode; the cookie should not be set if the image is
+ // loaded anonymously.
+ if (params.has("c")) {
+ let expectedValue = parseInt(params.get("c"), 10);
+ let actualValue = !request.hasHeader("Cookie")
+ ? 0
+ : parseInt(
+ request.getHeader("Cookie").replace(/^counter=(\d+)/, "$1"),
+ 10
+ );
+ if (actualValue != expectedValue) {
+ response.setStatusLine(request.httpVersion, 400, "Wrong counter value");
+ return;
+ }
+ response.setHeader("Set-Cookie", `counter=${expectedValue + 1}`, false);
+ }
+
+ // Wait to send the image if a timeout is given.
+ let timeout = parseInt(params.get("t"), 10);
+ if (timeout > 0) {
+ response.processAsync();
+ setTimeout(() => {
+ respond(params, request, response);
+ response.finish();
+ }, timeout * 1000);
+ return;
+ }
+
+ respond(params, request, response);
+}
+
+function parseQueryString(queryString) {
+ return queryString.split("&").reduce((params, param) => {
+ let [key, value] = param.split("=", 2);
+ params.set(key, value);
+ return params;
+ }, new Map());
+}
+
+function respond(params, request, response) {
+ if (params.has("s")) {
+ let statusCode = parseInt(params.get("s"), 10);
+ response.setStatusLine(request.httpVersion, statusCode, "Custom status");
+ return;
+ }
+ var filename = params.get("f");
+ writeFile(filename, response);
+}
+
+function writeFile(name, response) {
+ var file = new LocalFile(getState("__LOCATION__")).parent;
+ file.append(name);
+
+ let mimeType = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromFile(file);
+
+ let fileStream = new FileInputStream(file, 1, 0, false);
+ let binaryStream = new BinaryInputStream(fileStream);
+
+ response.setHeader("Content-Type", mimeType, false);
+ response.bodyOutputStream.writeFrom(binaryStream, binaryStream.available());
+
+ binaryStream.close();
+ fileStream.close();
+}
diff --git a/toolkit/components/alerts/test/mochitest.toml b/toolkit/components/alerts/test/mochitest.toml
new file mode 100644
index 0000000000..bb6da55f33
--- /dev/null
+++ b/toolkit/components/alerts/test/mochitest.toml
@@ -0,0 +1,28 @@
+[DEFAULT]
+skip-if = ["os == 'android'"] # We don't use XUL alerts on Android
+support-files = [
+ "image.gif",
+ "image.png",
+ "image_server.sjs",
+]
+
+# Synchronous tests like test_alerts.html must come before
+# asynchronous tests like test_alerts_noobserve.html!
+
+["test_alerts.html"]
+
+["test_alerts_noobserve.html"]
+
+["test_alerts_requireinteraction.html"]
+skip-if = ["verify && os == 'linux'"]
+
+["test_image.html"]
+skip-if = ["verify"]
+
+["test_invalid_utf16.html"]
+run-if = ["os == 'win'"] # Bug 1836526
+
+["test_multiple_alerts.html"]
+
+["test_principal.html"]
+skip-if = ["verify"] # Bug 1810860
diff --git a/toolkit/components/alerts/test/test_alerts.html b/toolkit/components/alerts/test/test_alerts.html
new file mode 100644
index 0000000000..9c9371fc70
--- /dev/null
+++ b/toolkit/components/alerts/test/test_alerts.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test for Alerts Service</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>
+
+<br>Alerts service, with observer "synchronous" case.
+<br>
+<br>Did a notification appear anywhere?
+<br>If so, the test will finish once the notification disappears.
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var notifier;
+var alertName = "fiorello";
+
+var observer = {
+ alertShow: false,
+ observe(aSubject, aTopic, aData) {
+ is(aData, "foobarcookie", "Checking whether the alert cookie was passed correctly");
+ if (aTopic == "alertclickcallback") {
+ todo(false, "Did someone click the notification while running mochitests? (Please don't.)");
+ } else if (aTopic == "alertshow") {
+ ok(!this.alertShow, "Alert should not be shown more than once");
+ this.alertShow = true;
+
+ // Notifications are native on OS X 10.8 and later, GNOME Shell with
+ // libnotify (bug 1236036) and Windows >= 8. These notifications persist
+ // in the Notification Center, and only fire the `alertfinished` event
+ // when closed. For platforms where native notifications may be used, we
+ // need to close explicitly to avoid a hang. This also works for XUL
+ // notifications when running this test on OS X < 10.8, or a window
+ // manager like Ubuntu Unity with incomplete libnotify support.
+ notifier.closeAlert(alertName);
+ } else {
+ is(aTopic, "alertfinished", "Checking the topic for a finished notification");
+ SimpleTest.finish();
+ }
+ },
+};
+
+function runTest() {
+ const Cc = SpecialPowers.Cc;
+ const Ci = SpecialPowers.Ci;
+
+ if (!("@mozilla.org/alerts-service;1" in Cc)) {
+ todo(false, "Alerts service does not exist in this application");
+ return;
+ }
+
+ ok(true, "Alerts service exists in this application");
+
+ try {
+ notifier = Cc["@mozilla.org/alerts-service;1"].
+ getService(Ci.nsIAlertsService);
+ ok(true, "Alerts service is available");
+ } catch (ex) {
+ todo(false,
+ "Alerts service is not available.", ex);
+ return;
+ }
+
+ try {
+ SimpleTest.waitForExplicitFinish();
+ notifier.showAlertNotification(null, "Notification test",
+ "Surprise! I'm here to test notifications!",
+ false, "foobarcookie", observer, alertName);
+ ok(true, "showAlertNotification() succeeded. Waiting for notification...");
+ } catch (ex) {
+ todo(false, "showAlertNotification() failed.", ex);
+ SimpleTest.finish();
+ }
+}
+
+runTest();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/alerts/test/test_alerts_noobserve.html b/toolkit/components/alerts/test/test_alerts_noobserve.html
new file mode 100644
index 0000000000..b6a7658e17
--- /dev/null
+++ b/toolkit/components/alerts/test/test_alerts_noobserve.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test for Alerts Service</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>
+
+<br>Alerts service, without observer "asynchronous" case.
+<br>
+<br>A notification should soon appear somewhere.
+<br>If there has been no crash when the notification (later) disappears, assume all is good.
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+const chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {setTimeout} = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+ );
+
+ function anyXULAlertsVisible() {
+ var windows = Services.wm.getEnumerator("alert:alert");
+ return windows.hasMoreElements();
+ }
+
+ addMessageListener("anyXULAlertsVisible", anyXULAlertsVisible);
+
+ addMessageListener("waitForAlerts", function waitForAlerts() {
+ if (anyXULAlertsVisible()) {
+ setTimeout(waitForAlerts, 1000);
+ } else {
+ sendAsyncMessage("waitedForAlerts");
+ }
+ });
+});
+
+function waitForAlertsThenFinish() {
+ chromeScript.addMessageListener("waitedForAlerts", function waitedForAlerts() {
+ chromeScript.removeMessageListener("waitedForAlerts", waitedForAlerts);
+ ok(true, "Alert disappeared.");
+ SimpleTest.finish();
+ });
+ chromeScript.sendAsyncMessage("waitForAlerts");
+}
+
+async function runTest() {
+ var alertsVisible = await chromeScript.sendQuery("anyXULAlertsVisible");
+ ok(!alertsVisible, "Alerts should not be present at the start of the test.");
+
+ if (!("@mozilla.org/alerts-service;1" in Cc)) {
+ todo(false, "Alerts service does not exist in this application");
+ } else {
+ ok(true, "Alerts service exists in this application");
+
+ var notifier;
+ try {
+ notifier = Cc["@mozilla.org/alerts-service;1"].
+ getService(Ci.nsIAlertsService);
+ ok(true, "Alerts service is available");
+ } catch (ex) {
+ todo(false, "Alerts service is not available.", ex);
+ }
+
+ if (notifier) {
+ try {
+ notifier.showAlertNotification(null, "Notification test",
+ "This notification has no observer");
+ ok(true, "showAlertNotification() succeeded");
+ } catch (ex) {
+ todo(false, "showAlertNotification() failed.", ex);
+ }
+ }
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+runTest();
+setTimeout(waitForAlertsThenFinish, 1000);
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/alerts/test/test_alerts_requireinteraction.html b/toolkit/components/alerts/test/test_alerts_requireinteraction.html
new file mode 100644
index 0000000000..ca8fa1c9e3
--- /dev/null
+++ b/toolkit/components/alerts/test/test_alerts_requireinteraction.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for alerts with requireInteraction</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+const chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {clearTimeout, setTimeout} = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+ );
+
+ addMessageListener("waitForXULAlert", function() {
+ var timer = setTimeout(function() {
+ Services.ww.unregisterNotification(windowObserver);
+ sendAsyncMessage("waitForXULAlert", false);
+ }, 2000);
+
+ var windowObserver = function(win, aTopic, aData) {
+ if (aTopic != "domwindowopened") {
+ return;
+ }
+
+ win.addEventListener("load", function() {
+ let windowType = win.document.documentElement.getAttribute("windowtype");
+ if (windowType == "alert:alert") {
+ clearTimeout(timer);
+ Services.ww.unregisterNotification(windowObserver);
+
+ sendAsyncMessage("waitForXULAlert", true);
+ }
+ }, {once: true});
+ };
+
+ Services.ww.registerNotification(windowObserver);
+ });
+});
+
+var cookie = 0;
+function promiseCreateXULAlert(alertService, listener, name) {
+ return new Promise(resolve => {
+ chromeScript.addMessageListener("waitForXULAlert", function waitedForAlert(result) {
+ chromeScript.removeMessageListener("waitForXULAlert", waitedForAlert);
+ resolve(result);
+ });
+
+ chromeScript.sendAsyncMessage("waitForXULAlert");
+ alertService.showAlertNotification(null, "title", "body",
+ true, cookie++, listener, name, null, null, null,
+ null, false, true);
+ });
+}
+
+add_task(async function test_require_interaction() {
+ if (!("@mozilla.org/alerts-service;1" in Cc)) {
+ todo(false, "Alerts service does not exist in this application.");
+ return;
+ }
+
+ ok(true, "Alerts service exists in this application.");
+
+ var alertService;
+ try {
+ alertService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
+ ok(true, "Alerts service is available.");
+ } catch (ex) {
+ todo(false, "Alerts service is not available.");
+ return;
+ }
+
+ await SpecialPowers.pushPrefEnv({"set": [
+ [ "dom.webnotifications.requireinteraction.enabled", true ],
+ [ "dom.webnotifications.requireinteraction.count", 2 ],
+ ]});
+
+ var expectedSequence = [
+ "first show",
+ "second show",
+ "second finished",
+ "second replacement show",
+ "third finished",
+ "first finished",
+ "third replacement show",
+ "second replacement finished",
+ "third replacement finished",
+ ];
+
+ var actualSequence = [];
+
+ function createAlertListener(name, showCallback, finishCallback) {
+ return (subject, topic, data) => {
+ if (topic == "alertshow") {
+ actualSequence.push(name + " show");
+ if (showCallback) {
+ showCallback();
+ }
+ } else if (topic == "alertfinished") {
+ actualSequence.push(name + " finished");
+ if (finishCallback) {
+ finishCallback();
+ }
+ }
+ };
+ }
+
+ var xulAlertCreated = await promiseCreateXULAlert(alertService,
+ createAlertListener("first"), "first");
+ if (!xulAlertCreated) {
+ ok(true, "Platform does not use XUL alerts.");
+ alertService.closeAlert("first");
+ return;
+ }
+
+ xulAlertCreated = await promiseCreateXULAlert(alertService,
+ createAlertListener("second"), "second");
+ ok(xulAlertCreated, "Create XUL alert");
+
+ // Replace second alert
+ xulAlertCreated = await promiseCreateXULAlert(alertService,
+ createAlertListener("second replacement"), "second");
+ ok(xulAlertCreated, "Create XUL alert");
+
+ var testFinishResolve;
+ var testFinishPromise = new Promise((resolve) => { testFinishResolve = resolve; });
+
+ xulAlertCreated = await promiseCreateXULAlert(alertService,
+ createAlertListener("third"), "third");
+ ok(!xulAlertCreated, "XUL alert should not be visible");
+
+ // Replace the not-yet-visible third alert.
+ xulAlertCreated = await promiseCreateXULAlert(alertService,
+ createAlertListener("third replacement",
+ function showCallback() {
+ alertService.closeAlert("second");
+ alertService.closeAlert("third");
+ },
+ function finishCallback() {
+ // Check actual sequence of alert events compared to expected sequence.
+ for (var i = 0; i < actualSequence.length; i++) {
+ is(actualSequence[i], expectedSequence[i],
+ "Alert callback at index " + i + " should be in expected order.");
+ }
+
+ testFinishResolve();
+ }), "third");
+
+ ok(!xulAlertCreated, "XUL alert should not be visible");
+
+ alertService.closeAlert("first");
+
+ await testFinishPromise;
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/alerts/test/test_image.html b/toolkit/components/alerts/test/test_image.html
new file mode 100644
index 0000000000..3928529c13
--- /dev/null
+++ b/toolkit/components/alerts/test/test_image.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 1233086</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>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const Services = SpecialPowers.Services;
+
+const imageServerURL = "http://mochi.test:8888/tests/toolkit/components/alerts/test/image_server.sjs";
+
+function makeAlert(...params) {
+ var alert = Cc["@mozilla.org/alert-notification;1"]
+ .createInstance(Ci.nsIAlertNotification);
+ alert.init(...params);
+ return alert;
+}
+
+function promiseImage(alert, timeout = 0, userData = null) {
+ return new Promise(resolve => {
+ var isDone = false;
+ function done(value) {
+ ok(!isDone, "Should call the image listener once");
+ isDone = true;
+ resolve(value);
+ }
+ alert.loadImage(timeout, SpecialPowers.wrapCallbackObject({
+ onImageReady(aUserData, aRequest) {
+ done([true, aRequest, aUserData]);
+ },
+ onImageMissing(aUserData) {
+ done([false, aUserData]);
+ },
+ }), SpecialPowers.wrap(userData));
+ });
+}
+
+add_task(async function testContext() {
+ var inUserData = Cc["@mozilla.org/supports-PRInt64;1"]
+ .createInstance(Ci.nsISupportsPRInt64);
+ inUserData.data = 123;
+
+ var alert = makeAlert(null, imageServerURL + "?f=image.png");
+ var [ready, , userData] = await promiseImage(alert, 0, inUserData);
+ ok(ready, "Should load requested image");
+ is(userData.QueryInterface(Ci.nsISupportsPRInt64).data, 123,
+ "Should pass user data for loaded image");
+
+ alert = makeAlert(null, imageServerURL + "?s=404");
+ [ready, userData] = await promiseImage(alert, 0, inUserData);
+ ok(!ready, "Should not load missing image");
+ is(userData.QueryInterface(Ci.nsISupportsPRInt64).data, 123,
+ "Should pass user data for missing image");
+});
+
+add_task(async function testTimeout() {
+ var alert = makeAlert(null, imageServerURL + "?f=image.png&t=3");
+ var [ready] = await promiseImage(alert, 1000);
+ ok(!ready, "Should cancel request if timeout fires");
+
+ [ready] = await promiseImage(alert, 45000);
+ ok(ready, "Should load image if request finishes before timeout");
+});
+
+add_task(async function testAnimatedGIF() {
+ var alert = makeAlert(null, imageServerURL + "?f=image.gif");
+ var [ready, request] = await promiseImage(alert);
+ ok(ready, "Should load first animated GIF frame");
+ is(request.mimeType, "image/gif", "Should report correct GIF MIME type");
+ is(request.image.width, 256, "GIF width should be 256px");
+ is(request.image.height, 256, "GIF height should be 256px");
+});
+
+add_task(async function testCancel() {
+ var alert = makeAlert(null, imageServerURL + "?f=image.gif&t=180");
+ await new Promise((resolve, reject) => {
+ var request = alert.loadImage(0, SpecialPowers.wrapCallbackObject({
+ onImageReady() {
+ reject(new Error("Should not load cancelled request"));
+ },
+ onImageMissing() {
+ resolve();
+ },
+ }), null);
+ request.cancel(SpecialPowers.Cr.NS_BINDING_ABORTED);
+ });
+});
+
+add_task(async function testMixedContent() {
+ // Loading principal is HTTPS; image URL is HTTP.
+ var origin = "https://mochi.test:8888";
+ var principal = Services.scriptSecurityManager
+ .createContentPrincipalFromOrigin(origin);
+
+ var alert = makeAlert(null, imageServerURL + "?f=image.png",
+ null, null, false, null, null, null,
+ null, principal);
+ var [ready, request] = await promiseImage(alert);
+ ok(ready, "Should load cross-protocol image");
+ is(request.mimeType, "image/png", "Should report correct MIME type");
+ is(request.image.width, 32, "Width should be 32px");
+ is(request.image.height, 32, "Height should be 32px");
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/alerts/test/test_invalid_utf16.html b/toolkit/components/alerts/test/test_invalid_utf16.html
new file mode 100644
index 0000000000..a4f862238c
--- /dev/null
+++ b/toolkit/components/alerts/test/test_invalid_utf16.html
@@ -0,0 +1,160 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <title>Test for stability when providing invalid UTF-16 strings</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+ <script>
+ const Cc = SpecialPowers.Cc;
+ const Ci = SpecialPowers.Ci;
+ let notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);;
+ let notification = Cc["@mozilla.org/alert-notification;1"]
+
+ function buildObserver(alertName) {
+ let resolve;
+ let reject;
+ let promise = new Promise((res, rej) => {resolve = res; reject = rej});
+
+ let success = false;
+ function observe(aSubject, aTopic, aData) {
+ if (aTopic == "alertshow") {
+ success = true;
+ notifier.closeAlert(alertName);
+ } else if (aTopic == "alertfinished") {
+ ok(true, "alertfinished");
+ if (success) {
+ resolve();
+ } else {
+ reject();
+ }
+ }
+ }
+
+ return { promise, observe };
+ };
+
+ function buildAlert(options) {
+ let alert = notification.createInstance(
+ Ci.nsIAlertNotification
+ );
+ alert.init(
+ options.name,
+ options.imageURL,
+ options.title,
+ options.text,
+ options.textClickable,
+ options.cookie,
+ options.dir,
+ options.lang,
+ options.data,
+ options.principal,
+ options.inPrivateBrowsing,
+ options.requireInteraction,
+ options.silent,
+ options.vibrate || []
+ );
+ if (options.actions) {
+ alert.actions = options.actions;
+ }
+ return alert;
+ }
+
+ async function runTest(options) {
+ let alert = buildAlert(options)
+ const { promise, observe } = buildObserver(options.name);
+ notifier.showAlert(alert, observe);
+ await promise;
+ }
+
+ let invalidUtf16 = String.fromCharCode(0xdfff);
+
+ add_task(async function test_invalid_utf16_name() {
+ let name = invalidUtf16;
+ let alert = buildAlert({name});
+
+ // Extract the alert name to ensure it was not forced to be valid UTF-16.
+ ok(name == alert.name, "Notification name was not forced to be valid UTF-16");
+
+ const { promise, observe } = buildObserver(name);
+ notifier.showAlert(alert, observe);
+ await promise;
+
+ ok(true, "Notification shown with invalid UTF-16 name");
+ });
+
+ add_task(async function test_invalid_utf16_title() {
+ let name = "invalid title";
+ let title = invalidUtf16;
+ await runTest({name, title});
+
+ ok(true, "Notification shown with invalid UTF-16 title");
+ });
+
+ add_task(async function test_invalid_utf16_body() {
+ let name = "invalid body";
+ let text = invalidUtf16;
+ await runTest({name, text});
+
+ ok(true, "Notification shown with invalid UTF-16 body");
+ });
+
+ add_task(async function test_invalid_utf16_image_url() {
+ let name = "invalid image URL";
+ let imageURL = invalidUtf16;
+ await runTest({name, imageURL});
+
+ ok(true, "Notification shown with invalid UTF-16 image url");
+ });
+
+ add_task(async function test_invalid_utf16_data() {
+ let name = "invalid data";
+ let data = invalidUtf16;
+ await runTest({name, data});
+
+ ok(true, "Notification shown with invalid UTF-16 data");
+ });
+
+ // At time of writing, actions are a Windows only, privileged feature.
+ add_task(async function test_invalid_utf16_action_body() {
+ let name = "invalid action body";
+ let actions = [
+ { action: invalidUtf16 },
+ ];
+ await runTest({name, actions});
+
+ ok(true, "Notification shown with invalid UTF-16 action body");
+ });
+
+ // At time of writing, actions are a Windows only, privileged feature.
+ add_task(async function test_invalid_utf16_action_title() {
+ let name = "invalid action title";
+ let actions = [
+ {title:invalidUtf16},
+ ];
+ await runTest({name, actions});
+
+ ok(true, "Notification shown with invalid UTF-16 action title");
+ });
+
+ // At time of writing, actions are a Windows only, privileged feature.
+ add_task(async function test_invalid_utf16_action_image_url() {
+ let name = "invalid action image URL";
+ let actions = [
+ { iconURL: invalidUtf16 },
+ ];
+ await runTest({name, actions});
+
+ ok(true, "Notification shown with invalid UTF-16 action image URL");
+ });
+ </script>
+</head>
+
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+
+</html>
diff --git a/toolkit/components/alerts/test/test_multiple_alerts.html b/toolkit/components/alerts/test/test_multiple_alerts.html
new file mode 100644
index 0000000000..95843b2e89
--- /dev/null
+++ b/toolkit/components/alerts/test/test_multiple_alerts.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for multiple alerts</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+const chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {clearTimeout, setTimeout} = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+ );
+
+ const alertService = Cc["@mozilla.org/alerts-service;1"]
+ .getService(Ci.nsIAlertsService);
+
+ addMessageListener("waitForPosition", function() {
+ var timer = setTimeout(function() {
+ Services.ww.unregisterNotification(windowObserver);
+ sendAsyncMessage("waitedForPosition", null);
+ }, 2000);
+
+ var windowObserver = function(win, aTopic, aData) {
+ if (aTopic != "domwindowopened") {
+ return;
+ }
+
+ // Alerts are implemented using XUL.
+ clearTimeout(timer);
+
+ Services.ww.unregisterNotification(windowObserver);
+
+ win.addEventListener("pageshow", function() {
+ var x = win.screenX;
+ var y = win.screenY;
+
+ win.addEventListener("pagehide", function() {
+ sendAsyncMessage("waitedForPosition", { x, y });
+ }, {once: true});
+
+ alertService.closeAlert();
+ }, {once: true});
+ };
+
+ Services.ww.registerNotification(windowObserver);
+ });
+});
+
+function promiseAlertPosition(alertService) {
+ return new Promise(resolve => {
+ chromeScript.addMessageListener("waitedForPosition", function waitedForPosition(result) {
+ chromeScript.removeMessageListener("waitedForPosition", waitedForPosition);
+ resolve(result);
+ });
+ chromeScript.sendAsyncMessage("waitForPosition");
+
+ alertService.showAlertNotification(null, "title", "body");
+ ok(true, "Alert shown.");
+ });
+}
+
+add_task(async function test_multiple_alerts() {
+ if (!("@mozilla.org/alerts-service;1" in Cc)) {
+ todo(false, "Alerts service does not exist in this application.");
+ return;
+ }
+
+ ok(true, "Alerts service exists in this application.");
+
+ var alertService;
+ try {
+ alertService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
+ ok(true, "Alerts service is available.");
+ } catch (ex) {
+ todo(false, "Alerts service is not available.");
+ return;
+ }
+
+ var firstAlertPosition = await promiseAlertPosition(alertService);
+ if (!firstAlertPosition) {
+ ok(true, "Platform does not use XUL alerts.");
+ return;
+ }
+
+ var secondAlertPosition = await promiseAlertPosition(alertService);
+ is(secondAlertPosition.x, firstAlertPosition.x, "Second alert should be opened in the same position.");
+ is(secondAlertPosition.y, firstAlertPosition.y, "Second alert should be opened in the same position.");
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/alerts/test/test_principal.html b/toolkit/components/alerts/test/test_principal.html
new file mode 100644
index 0000000000..5464d92977
--- /dev/null
+++ b/toolkit/components/alerts/test/test_principal.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 1202933</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>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const Services = SpecialPowers.Services;
+
+const notifier = Cc["@mozilla.org/alerts-service;1"]
+ .getService(Ci.nsIAlertsService);
+
+const chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ addMessageListener("anyXULAlertsVisible", function() {
+ var windows = Services.wm.getEnumerator("alert:alert");
+ return windows.hasMoreElements();
+ });
+
+ addMessageListener("getAlertSource", function() {
+ var alertWindows = Services.wm.getEnumerator("alert:alert");
+ if (!alertWindows) {
+ return null;
+ }
+ var alertWindow = alertWindows.getNext();
+ return alertWindow.document.getElementById("alertSourceLabel").getAttribute("value");
+ });
+});
+
+function notify(alertName, principal) {
+ return new Promise((resolve, reject) => {
+ var source;
+ async function observe(subject, topic, data) {
+ if (topic == "alertclickcallback") {
+ reject(new Error("Alerts should not be clicked during test"));
+ } else if (topic == "alertshow") {
+ source = await chromeScript.sendQuery("getAlertSource");
+ notifier.closeAlert(alertName);
+ } else {
+ is(topic, "alertfinished", "Should hide alert");
+ resolve(source);
+ }
+ }
+ notifier.showAlertNotification(null, "Notification test",
+ "Surprise! I'm here to test notifications!",
+ false, alertName, observe, alertName,
+ null, null, null, principal);
+ if (SpecialPowers.Services.appinfo.OS == "Darwin") {
+ notifier.closeAlert(alertName);
+ }
+ });
+}
+
+async function testNoPrincipal() {
+ var source = await notify("noPrincipal", null);
+ ok(!source, "Should omit source without principal");
+}
+
+async function testSystemPrincipal() {
+ var principal = Services.scriptSecurityManager.getSystemPrincipal();
+ var source = await notify("systemPrincipal", principal);
+ ok(!source, "Should omit source for system principal");
+}
+
+async function testNullPrincipal() {
+ var principal = Services.scriptSecurityManager.createNullPrincipal({});
+ var source = await notify("nullPrincipal", principal);
+ ok(!source, "Should omit source for null principal");
+}
+
+async function testNodePrincipal() {
+ var principal = SpecialPowers.wrap(document).nodePrincipal;
+ var source = await notify("nodePrincipal", principal);
+
+ var stringBundle = Services.strings.createBundle(
+ "chrome://alerts/locale/alert.properties"
+ );
+ var localizedSource = stringBundle.formatStringFromName(
+ "source.label", [principal.hostPort]);
+ is(source, localizedSource, "Should include source for node principal");
+}
+
+function runTest() {
+ if (!("@mozilla.org/alerts-service;1" in Cc)) {
+ todo(false, "Alerts service does not exist in this application");
+ return;
+ }
+
+ ok(true, "Alerts service exists in this application");
+
+ add_setup(async () => {
+ // This test verifies behavior specific to XUL alerts.
+ await SpecialPowers.pushPrefEnv({
+ set: [["alerts.useSystemBackend", false]],
+ });
+ });
+
+ // sendSyncMessage returns an array of arrays. See the comments in
+ // test_alerts_noobserve.html and test_SpecialPowersLoadChromeScript.html.
+ add_task(async () => {
+ var alertsVisible = await chromeScript.sendQuery("anyXULAlertsVisible");
+ ok(!alertsVisible, "Alerts should not be present at the start of the test.");
+ });
+
+ add_task(testNoPrincipal);
+ add_task(testSystemPrincipal);
+ add_task(testNullPrincipal);
+ add_task(testNodePrincipal);
+}
+
+runTest();
+</script>
+</pre>
+</body>
+</html>