diff --git a/dom/security/nsContentSecurityUtils.cpp b/dom/security/nsContentSecurityUtils.cpp
index 6d1fa12ddcc1..ffe2c2d1b2d7 100644
--- a/dom/security/nsContentSecurityUtils.cpp
+++ b/dom/security/nsContentSecurityUtils.cpp
@@ -1356,6 +1356,7 @@ static nsLiteralCString sImgSrcDataBlobAllowList[] = {
"chrome://devtools/content/responsive/toolbar.xhtml"_ns,
"chrome://devtools/content/shared/sourceeditor/codemirror/cmiframe.html"_ns,
"chrome://devtools/content/webconsole/index.html"_ns,
+ "chrome://global/content/alerts/alert.xhtml"_ns,
"chrome://global/content/print.html"_ns,
};
// img-src https:
@@ -1382,6 +1383,7 @@ static nsLiteralCString sImgSrcHttpAllowList[] = {
"chrome://devtools/content/framework/browser-toolbox/window.html"_ns,
"chrome://devtools/content/framework/toolbox-window.xhtml"_ns,
"chrome://browser/content/preferences/dialogs/applicationManager.xhtml"_ns,
+ "chrome://global/content/alerts/alert.xhtml"_ns,
"chrome://mozapps/content/handling/appChooser.xhtml"_ns,
// STOP! Do not add anything to this list.
};
diff --git a/toolkit/components/alerts/alert.xhtml b/toolkit/components/alerts/alert.xhtml
index e510560873a0..0d84b137a639 100644
--- a/toolkit/components/alerts/alert.xhtml
+++ b/toolkit/components/alerts/alert.xhtml
@@ -3,7 +3,8 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
+
diff --git a/toolkit/components/extensions/test/browser/browser.toml b/toolkit/components/extensions/test/browser/browser.toml
index 3943c55eb4cb..c83835c61c25 100644
--- a/toolkit/components/extensions/test/browser/browser.toml
+++ b/toolkit/components/extensions/test/browser/browser.toml
@@ -17,6 +17,8 @@ support-files = [
["browser_ext_management_themes.js"]
skip-if = ["verify"]
+["browser_ext_notifications_useSystemBackend_false.js"]
+
["browser_ext_process_crash_handling.js"]
skip-if = ["!crashreporter"]
diff --git a/toolkit/components/extensions/test/browser/browser_ext_notifications_useSystemBackend_false.js b/toolkit/components/extensions/test/browser/browser_ext_notifications_useSystemBackend_false.js
new file mode 100644
index 000000000000..e02d5f8d4af2
--- /dev/null
+++ b/toolkit/components/extensions/test/browser/browser_ext_notifications_useSystemBackend_false.js
@@ -0,0 +1,184 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// Regression test for: https://bugzilla.mozilla.org/show_bug.cgi?id=1970075
+//
+// This test verifies that iconUrl as passed to browser.notifications.create()
+// can be loaded. By default, the system backend is enabled, for which we can
+// do little more than verifying that the options are set, but in case the
+// system backend is disabled, we can verify that the image is actually loaded,
+// because in this case Firefox is responsible for rendering the notification.
+
+const { AddonTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/AddonTestUtils.sys.mjs"
+);
+AddonTestUtils.initMochitest(this);
+const server = AddonTestUtils.createHttpServer();
+const serverHost = server.identity.primaryHost;
+const serverPort = server.identity.primaryPort;
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["alerts.useSystemBackend", false]],
+ });
+});
+
+async function testCreateNotification({ iconUrl, testOnShown }) {
+ function background() {
+ function createBlobUrlForTest() {
+ const imgData = Uint8Array.fromBase64(
+ // PNG image of size 5x5. test_blob_icon will verify the width.
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"
+ );
+ const blob = new Blob([imgData], { type: "image/png" });
+ return URL.createObjectURL(blob);
+ }
+ browser.test.onMessage.addListener(async (msg, iconUrl) => {
+ browser.test.assertEq("iconUrl", msg, "Expected message");
+
+ if (iconUrl == "blob:REPLACE_WITH_REAL_URL_IN_TEST") {
+ iconUrl = createBlobUrlForTest();
+ }
+ if (iconUrl === "moz-extension:REPLACE_WITH_REAL_URL_IN_TEST") {
+ iconUrl = browser.runtime.getURL("5x5.png");
+ }
+
+ let shownPromise = new Promise(resolve => {
+ browser.notifications.onShown.addListener(resolve);
+ });
+ let closedPromise = new Promise(resolve => {
+ browser.notifications.onClosed.addListener(resolve);
+ });
+ let createdId = await browser.notifications.create("notifid", {
+ iconUrl,
+ type: "basic",
+ title: "title",
+ message: "msg",
+ });
+ let shownId = await shownPromise;
+ browser.test.assertEq(createdId, shownId, "ID of shown notification");
+ browser.test.sendMessage("notification_shown");
+ let closedId = await closedPromise;
+ browser.test.assertEq(createdId, closedId, "ID of closed notification");
+ browser.test.assertEq(
+ "{}",
+ JSON.stringify(await browser.notifications.getAll()),
+ "no notifications left"
+ );
+ browser.test.sendMessage("notification_closed");
+ });
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["notifications"],
+ },
+ background,
+ files: {
+ "5x5.png": imageBufferFromDataURI(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"
+ ),
+ },
+ });
+ await extension.startup();
+ extension.sendMessage("iconUrl", iconUrl);
+ await extension.awaitMessage("notification_shown");
+ let alertWindow = Services.wm.getMostRecentWindow("alert:alert");
+ ok(alertWindow, "Found alert.xhtml window");
+ await testOnShown(alertWindow);
+ info("Closing alert.xhtml window");
+ alertWindow.document.querySelector(".close-icon").click();
+ await extension.awaitMessage("notification_closed");
+ await extension.unload();
+}
+
+// Ideally we'd also repeat the following test for https, but the test server
+// does not support https (bug 1742061).
+add_task(async function test_http_icon() {
+ const requestPromise = new Promise(resolve => {
+ let count = 0;
+ server.registerPathHandler("/test_http_icon.png", () => {
+ // We only care about the request happening, we don't care about the
+ // actual response.
+ is(++count, 1, "Got one request to test_http_icon.png");
+ resolve();
+ });
+ });
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ const httpUrl = `http://${serverHost}:${serverPort}/test_http_icon.png`;
+
+ await testCreateNotification({
+ iconUrl: httpUrl,
+ async testOnShown(alertWindow) {
+ info("Waiting for test_http_icon.png request to be detected.");
+ const img = alertWindow.document.getElementById("alertImage");
+ is(img.src, httpUrl, "Got http:-URL");
+ await requestPromise;
+ },
+ });
+});
+
+add_task(async function test_data_icon() {
+ // data-URL with a valid 5x5 image.
+ const dataUrl =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC";
+
+ await testCreateNotification({
+ iconUrl: dataUrl,
+ async testOnShown(alertWindow) {
+ const img = alertWindow.document.getElementById("alertImage");
+ is(img.src, dataUrl, "Got data:-URL");
+
+ info("Verifying that data:-URL can be loaded in the document.");
+ // img is not an
but an element, so we cannot read its
+ // intrinsic size directly to guess whether it was loaded.
+ // To see whether it is NOT blocked by CSP, create a new image and see if
+ // it can be loaded.
+
+ const testImg = alertWindow.document.createElement("img");
+ testImg.src = dataUrl;
+ await testImg.decode();
+ is(testImg.naturalWidth, 5, "Test image was loaded successfully");
+ },
+ });
+});
+
+add_task(async function test_blob_icon() {
+ await testCreateNotification({
+ iconUrl: "blob:REPLACE_WITH_REAL_URL_IN_TEST",
+ async testOnShown(alertWindow) {
+ const img = alertWindow.document.getElementById("alertImage");
+ ok(img.src.startsWith("blob:moz-extension"), `Got blob:-URL: ${img.src}`);
+
+ info("Verifying that blob:-URL can be loaded in the document.");
+
+ const testImg = alertWindow.document.createElement("img");
+ testImg.src = img.src;
+ await testImg.decode();
+ // The 5 here is the size of the test image, see createBlobUrlForTest.
+ is(testImg.naturalWidth, 5, "Test image was loaded successfully");
+ },
+ });
+});
+
+add_task(async function test_moz_extension_icon() {
+ await testCreateNotification({
+ iconUrl: "moz-extension:REPLACE_WITH_REAL_URL_IN_TEST",
+ async testOnShown(alertWindow) {
+ const img = alertWindow.document.getElementById("alertImage");
+ ok(
+ img.src.startsWith("moz-extension:/") && img.src.endsWith("/5x5.png"),
+ `Got moz-extension:-URL: ${img.src}`
+ );
+
+ info("Verifying that moz-extension:-URL can be loaded in the document.");
+
+ const testImg = alertWindow.document.createElement("img");
+ testImg.src = img.src;
+ await testImg.decode();
+ // The 5 here is the size of the test image (5x5.png).
+ is(testImg.naturalWidth, 5, "Test image was loaded successfully");
+ },
+ });
+});