/* -*- 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;
});