"use strict"; const server = createHttpServer({ hosts: ["example.com", "example.org"] }); server.registerDirectory("/data/", do_get_file("data")); let image = atob( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=" ); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0) ).buffer; async function testImageLoading(src, expectedAction) { let imageLoadingPromise = new Promise((resolve, reject) => { let cleanupListeners; let testImage = document.createElement("img"); // Set the src via wrappedJSObject so the load is triggered with the // content page's principal rather than ours. testImage.wrappedJSObject.setAttribute("src", src); let loadListener = () => { cleanupListeners(); resolve(expectedAction === "loaded"); }; let errorListener = () => { cleanupListeners(); resolve(expectedAction === "blocked"); }; cleanupListeners = () => { testImage.removeEventListener("load", loadListener); testImage.removeEventListener("error", errorListener); }; testImage.addEventListener("load", loadListener); testImage.addEventListener("error", errorListener); document.body.appendChild(testImage); }); let success = await imageLoadingPromise; browser.runtime.sendMessage({ name: "image-loading", expectedAction, success, }); } add_task(async function test_web_accessible_resources_csp() { function background() { browser.runtime.onMessage.addListener((msg, sender) => { if (msg.name === "image-loading") { browser.test.assertTrue(msg.success, `Image was ${msg.expectedAction}`); browser.test.sendMessage(`image-${msg.expectedAction}`); } else { browser.test.sendMessage(msg); } }); browser.test.sendMessage("background-ready"); } function content() { window.addEventListener("message", function rcv(event) { browser.runtime.sendMessage("script-ran"); window.removeEventListener("message", rcv); }); testImageLoading(browser.runtime.getURL("image.png"), "loaded"); let testScriptElement = document.createElement("script"); // Set the src via wrappedJSObject so the load is triggered with the // content page's principal rather than ours. testScriptElement.wrappedJSObject.setAttribute( "src", browser.runtime.getURL("test_script.js") ); document.head.appendChild(testScriptElement); browser.runtime.sendMessage("script-loaded"); } function testScript() { window.postMessage("test-script-loaded", "*"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { content_scripts: [ { matches: ["http://example.com/*/file_csp.html"], run_at: "document_end", js: ["content_script_helper.js", "content_script.js"], }, ], web_accessible_resources: ["image.png", "test_script.js"], }, background, files: { "content_script_helper.js": `${testImageLoading}`, "content_script.js": content, "test_script.js": testScript, "image.png": IMAGE_ARRAYBUFFER, }, }); await Promise.all([ extension.startup(), extension.awaitMessage("background-ready"), ]); let page = await ExtensionTestUtils.loadContentPage( `http://example.com/data/file_sample.html` ); await page.legacySpawn(null, () => { this.obs = { events: [], observe(subject, topic, data) { this.events.push(subject.QueryInterface(Ci.nsIURI).spec); }, done() { Services.obs.removeObserver(this, "csp-on-violate-policy"); return this.events; }, }; Services.obs.addObserver(this.obs, "csp-on-violate-policy"); content.location.href = "http://example.com/data/file_csp.html"; }); await Promise.all([ extension.awaitMessage("image-loaded"), extension.awaitMessage("script-loaded"), extension.awaitMessage("script-ran"), ]); let events = await page.legacySpawn(null, () => this.obs.done()); equal(events.length, 2, "Two items were rejected by CSP"); for (let url of events) { ok( url.includes("file_image_bad.png") || url.includes("file_script_bad.js"), `Expected file: ${url} rejected by CSP` ); } await page.close(); await extension.unload(); });