/* -*- 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" ); const { ImageTestUtils } = ChromeUtils.importESModule( "resource://testing-common/ImageTestUtils.sys.mjs" ); AddonTestUtils.initMochitest(this); const server = AddonTestUtils.createHttpServer(); const serverHost = server.identity.primaryHost; const serverPort = server.identity.primaryPort; // data-URL with a valid 5x5 image. const BASE64_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"; const DATA_URL = "data:image/png;base64," + BASE64_DATA; 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() { let count = 0; server.registerPathHandler("/test_http_icon.png", (request, response) => { is(++count, 1, "Got one request to test_http_icon.png"); response.setStatusLine(request.httpVersion, 200, "OK"); let body = atob(BASE64_DATA); response.bodyOutputStream.write(body, body.length); }); // 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) { const img = alertWindow.document.getElementById("alertImage"); await ImageTestUtils.assertEqualImage( alertWindow, img.src, DATA_URL, "Got image" ); info("Verifying that http:-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 = img.src; await testImg.decode(); is(testImg.naturalWidth, 5, "Test image was loaded successfully"); }, }); }); add_task(async function test_data_icon() { await testCreateNotification({ iconUrl: DATA_URL, async testOnShown(alertWindow) { const img = alertWindow.document.getElementById("alertImage"); await ImageTestUtils.assertEqualImage( alertWindow, img.src, DATA_URL, "Got image" ); info("Verifying that data:-URL can be loaded in the document."); const testImg = alertWindow.document.createElement("img"); testImg.src = img.src; 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"); await ImageTestUtils.assertEqualImage( alertWindow, img.src, DATA_URL, "Got image" ); 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"); await ImageTestUtils.assertEqualImage( alertWindow, img.src, DATA_URL, "Got image" ); 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"); }, }); }); add_task(async function test_forbidden_chrome_icon() { let loadFailedMessagePromise = new Promise(resolve => { Services.console.registerListener(function listener(msg) { if ( /Content at moz-extension:.*? may not load or link to chrome:/.test( msg.message ) ) { resolve(); Services.console.unregisterListener(listener); } }); }); await testCreateNotification({ iconUrl: "chrome://branding/content/icon64.png", async testOnShown(alertWindow) { const img = alertWindow.document.getElementById("alertImage"); ok(!img.hasAttribute("src"), "No image"); }, }); info("Waiting for console error message"); await loadFailedMessagePromise; });