summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js223
1 files changed, 223 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js b/toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js
new file mode 100644
index 0000000000..983fe1c542
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_xhr_cors.js
@@ -0,0 +1,223 @@
+"use strict";
+
+// The purpose of this test is to show that the XMLHttpRequest API behaves
+// similarly in MV2 and MV3, except for intentional differences related to
+// permission handling.
+
+Services.prefs.setBoolPref("extensions.manifestV3.enabled", true);
+
+const server = createHttpServer({
+ hosts: ["example.com", "example.net", "example.org"],
+});
+server.registerPathHandler("/dummy", (req, res) => {
+ res.setStatusLine(req.httpVersion, 200, "OK");
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
+
+ // A very strict CSP.
+ res.setHeader(
+ "Content-Security-Policy",
+ "default-src; script-src 'nonce-kindasecret'; connect-src http:"
+ );
+
+ res.write(
+ `<script id="id_of_some_element" nonce="kindasecret">
+ // Clobber XMLHttpRequest API to allow us to verify that the page's value
+ // for it does not affect the XMLHttpRequest API in the content script.
+ window.XMLHttpRequest = "This is not XMLHttpRequest";
+ </script>
+ `
+ );
+});
+server.registerPathHandler("/dummy.json", (req, res) => {
+ res.write(`{"mykey": "kvalue"}`);
+});
+server.registerPathHandler("/nocors", (req, res) => {
+ res.write("no cors");
+});
+server.registerPathHandler("/cors-enabled", (req, res) => {
+ res.setHeader("Access-Control-Allow-Origin", "http://example.com");
+ res.write("cors_response");
+});
+server.registerPathHandler("/return-origin", (req, res) => {
+ res.setHeader("Content-Type", "text/plain");
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "*");
+ res.write(req.hasHeader("Origin") ? req.getHeader("Origin") : "undefined");
+});
+
+// We just need to test XHR; fetch is already covered by test_ext_secfetch.js.
+async function test_xhr({ manifest_version }) {
+ async function contentScript(manifest_version) {
+ function runXHR(url, extraXHRProps, method = "GET") {
+ return new Promise(resolve => {
+ let x = new XMLHttpRequest();
+ x.open(method, url);
+ Object.assign(x, extraXHRProps);
+ x.onloadend = () => resolve(x);
+ x.send();
+ });
+ }
+ async function checkXHR({
+ description,
+ url,
+ extraXHRProps,
+ method,
+ expected,
+ }) {
+ let { status, response } = expected;
+ let x = await runXHR(url, extraXHRProps, method);
+ browser.test.assertEq(status, x.status, `${description} - status`);
+ browser.test.assertEq(response, x.response, `${description} - body`);
+ }
+
+ await checkXHR({
+ description: "Same-origin",
+ url: "http://example.com/nocors",
+ expected: { status: 200, response: "no cors" },
+ });
+
+ await checkXHR({
+ description: "Cross-origin without CORS",
+ url: "http://example.org/nocors",
+ expected: { status: 0, response: "" },
+ });
+
+ await checkXHR({
+ description: "Cross-origin with CORS",
+ url: "http://example.org/cors-enabled",
+ expected:
+ manifest_version === 2
+ ? // Bug 1605197: MV2 cannot fall back to CORS.
+ { status: 0, response: "" }
+ : { status: 200, response: "cors_response" },
+ });
+
+ // MV2 allowed cross-origin requests in content scripts with host
+ // permissions, but MV3 does not.
+ await checkXHR({
+ description: "Cross-origin without CORS, with permission",
+ url: "http://example.net/nocors",
+ expected:
+ manifest_version === 2
+ ? { status: 200, response: "no cors" }
+ : { status: 0, response: "" },
+ });
+
+ await checkXHR({
+ description: "Cross-origin with CORS (and permission)",
+ url: "http://example.net/cors-enabled",
+ expected: { status: 200, response: "cors_response" },
+ });
+
+ // MV2 has a XMLHttpRequest instance specific to the sandbox.
+ // MV3 uses the page's XMLHttpRequest and currently enforces the page's CSP.
+ // TODO bug 1766813: Enforce content script CSP instead.
+ await checkXHR({
+ description: "data:-URL while page blocks data: via CSP",
+ url: "data:,data-url",
+ expected:
+ // Should be "data-url" in MV3 too.
+ manifest_version === 2
+ ? { status: 200, response: "data-url" }
+ : { status: 0, response: "" },
+ });
+
+ {
+ let x = await runXHR("http://example.com/dummy.json", {
+ responseType: "json",
+ });
+ browser.test.assertTrue(x.response instanceof Object, "is JSON object");
+ browser.test.assertEq(x.response.mykey, "kvalue", "can read parsed JSON");
+ }
+
+ {
+ let x = await runXHR("http://example.com/dummy", {
+ responseType: "document",
+ });
+ browser.test.assertTrue(HTMLDocument.isInstance(x.response), "is doc");
+ browser.test.assertTrue(
+ x.response.querySelector("#id_of_some_element"),
+ "got parsed document"
+ );
+ }
+
+ await checkXHR({
+ description: "Same-origin Origin header",
+ url: "http://example.com/return-origin",
+ expected: { status: 200, response: "undefined" },
+ });
+
+ await checkXHR({
+ description: "Same-origin POST Origin header",
+ url: "http://example.com/return-origin",
+ method: "POST",
+ expected:
+ manifest_version === 2
+ ? { status: 200, response: "undefined" }
+ : { status: 200, response: "http://example.com" },
+ });
+
+ await checkXHR({
+ description: "Cross-origin (CORS) Origin header",
+ url: "http://example.org/return-origin",
+ expected:
+ manifest_version === 2
+ ? // Bug 1605197: MV2 cannot fall back to CORS.
+ { status: 0, response: "" }
+ : { status: 200, response: "http://example.com" },
+ });
+
+ await checkXHR({
+ description: "Cross-origin (CORS) POST Origin header",
+ url: "http://example.org/return-origin",
+ method: "POST",
+ expected:
+ manifest_version === 2
+ ? // Bug 1605197: MV2 cannot fall back to CORS.
+ { status: 0, response: "" }
+ : { status: 200, response: "http://example.com" },
+ });
+
+ browser.test.sendMessage("done");
+ }
+ let extension = ExtensionTestUtils.loadExtension({
+ temporarilyInstalled: true, // Needed for granted_host_permissions
+ manifest: {
+ manifest_version,
+ granted_host_permissions: true, // Test-only: grant permissions in MV3.
+ host_permissions: [
+ "http://example.net/",
+ // Work-around for bug 1766752.
+ "http://example.com/",
+ // "http://example.org/" is intentionally missing.
+ ],
+ content_scripts: [
+ {
+ matches: ["http://example.com/dummy"],
+ run_at: "document_end",
+ js: ["contentscript.js"],
+ },
+ ],
+ },
+ files: {
+ "contentscript.js": `(${contentScript})(${manifest_version})`,
+ },
+ });
+ await extension.startup();
+
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ "http://example.com/dummy"
+ );
+ await extension.awaitMessage("done");
+ await contentPage.close();
+
+ await extension.unload();
+}
+
+add_task(async function test_XHR_MV2() {
+ await test_xhr({ manifest_version: 2 });
+});
+
+add_task(async function test_XHR_MV3() {
+ await test_xhr({ manifest_version: 3 });
+});