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"); + }, + }); +});