From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/serviceworkers/test/fetch/fetch_tests.js | 716 +++++++++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 dom/serviceworkers/test/fetch/fetch_tests.js (limited to 'dom/serviceworkers/test/fetch/fetch_tests.js') diff --git a/dom/serviceworkers/test/fetch/fetch_tests.js b/dom/serviceworkers/test/fetch/fetch_tests.js new file mode 100644 index 0000000000..69b8a89679 --- /dev/null +++ b/dom/serviceworkers/test/fetch/fetch_tests.js @@ -0,0 +1,716 @@ +var origin = "http://mochi.test:8888"; + +function fetchXHRWithMethod(name, method, onload, onerror, headers) { + expectAsyncResult(); + + onload = + onload || + function () { + my_ok(false, "XHR load should not complete successfully"); + finish(); + }; + onerror = + onerror || + function () { + my_ok( + false, + "XHR load for " + name + " should be intercepted successfully" + ); + finish(); + }; + + var x = new XMLHttpRequest(); + x.open(method, name, true); + x.onload = function () { + onload(x); + }; + x.onerror = function () { + onerror(x); + }; + headers = headers || []; + headers.forEach(function (header) { + x.setRequestHeader(header[0], header[1]); + }); + x.send(); +} + +var corsServerPath = + "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs"; +var corsServerURL = "http://example.com" + corsServerPath; + +function redirectURL(hops) { + return ( + hops[0].server + + corsServerPath + + "?hop=1&hops=" + + encodeURIComponent(JSON.stringify(hops)) + ); +} + +function fetchXHR(name, onload, onerror, headers) { + return fetchXHRWithMethod(name, "GET", onload, onerror, headers); +} + +fetchXHR("bare-synthesized.txt", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "synthesized response body", + "load should have synthesized response" + ); + finish(); +}); + +fetchXHR("test-respondwith-response.txt", function (xhr) { + my_ok( + xhr.status == 200, + "test-respondwith-response load should be successful" + ); + my_ok( + xhr.responseText == "test-respondwith-response response body", + "load should have response" + ); + finish(); +}); + +fetchXHR("synthesized-404.txt", function (xhr) { + my_ok(xhr.status == 404, "load should 404"); + my_ok( + xhr.responseText == "synthesized response body", + "404 load should have synthesized response" + ); + finish(); +}); + +fetchXHR("synthesized-headers.txt", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.getResponseHeader("X-Custom-Greeting") === "Hello", + "custom header should be set" + ); + my_ok( + xhr.responseText == "synthesized response body", + "custom header load should have synthesized response" + ); + finish(); +}); + +fetchXHR("synthesized-redirect-real-file.txt", function (xhr) { + dump("Got status AARRGH " + xhr.status + " " + xhr.responseText + "\n"); + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "This is a real file.\n", + "Redirect to real file should complete." + ); + finish(); +}); + +fetchXHR("synthesized-redirect-twice-real-file.txt", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "This is a real file.\n", + "Redirect to real file (twice) should complete." + ); + finish(); +}); + +fetchXHR("synthesized-redirect-synthesized.txt", function (xhr) { + my_ok(xhr.status == 200, "synth+redirect+synth load should be successful"); + my_ok( + xhr.responseText == "synthesized response body", + "load should have redirected+synthesized response" + ); + finish(); +}); + +fetchXHR("synthesized-redirect-twice-synthesized.txt", function (xhr) { + my_ok( + xhr.status == 200, + "synth+redirect+synth (twice) load should be successful" + ); + my_ok( + xhr.responseText == "synthesized response body", + "load should have redirected+synthesized (twice) response" + ); + finish(); +}); + +fetchXHR("redirect.sjs", function (xhr) { + my_ok(xhr.status == 404, "redirected load should be uninterrupted"); + finish(); +}); + +fetchXHR("ignored.txt", function (xhr) { + my_ok(xhr.status == 404, "load should be uninterrupted"); + finish(); +}); + +fetchXHR("rejected.txt", null, function (xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR("nonresponse.txt", null, function (xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR("nonresponse2.txt", null, function (xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR("nonpromise.txt", null, function (xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR( + "headers.txt", + function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "1", "request header checks should have passed"); + finish(); + }, + null, + [ + ["X-Test1", "header1"], + ["X-Test2", "header2"], + ] +); + +fetchXHR("http://user:pass@mochi.test:8888/user-pass", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "http://user:pass@mochi.test:8888/user-pass", + "The username and password should be preserved" + ); + finish(); +}); + +fetchXHR("readable-stream.txt", function (xhr) { + my_ok(xhr.status == 200, "loading completed"); + my_ok(xhr.responseText == "Hello!", "The message is correct!"); + finish(); +}); + +fetchXHR( + "readable-stream-locked.txt", + function (xhr) { + my_ok(false, "This should not be called!"); + finish(); + }, + function () { + my_ok(true, "The exception has been correctly handled!"); + finish(); + } +); + +fetchXHR( + "readable-stream-with-exception.txt", + function (xhr) { + my_ok(false, "This should not be called!"); + finish(); + }, + function () { + my_ok(true, "The exception has been correctly handled!"); + finish(); + } +); + +fetchXHR( + "readable-stream-with-exception2.txt", + function (xhr) { + my_ok(false, "This should not be called!"); + finish(); + }, + function () { + my_ok(true, "The exception has been correctly handled!"); + finish(); + } +); + +fetchXHR( + "readable-stream-already-consumed.txt", + function (xhr) { + my_ok(false, "This should not be called!"); + finish(); + }, + function () { + my_ok(true, "The exception has been correctly handled!"); + finish(); + } +); + +var expectedUncompressedResponse = ""; +for (let i = 0; i < 10; ++i) { + expectedUncompressedResponse += "hello"; +} +expectedUncompressedResponse += "\n"; + +// ServiceWorker does not intercept, at which point the network request should +// be correctly decoded. +fetchXHR("deliver-gzip.sjs", function (xhr) { + my_ok(xhr.status == 200, "network gzip load should be successful"); + my_ok( + xhr.responseText == expectedUncompressedResponse, + "network gzip load should have synthesized response." + ); + my_ok( + xhr.getResponseHeader("Content-Encoding") == "gzip", + "network Content-Encoding should be gzip." + ); + my_ok( + xhr.getResponseHeader("Content-Length") == "35", + "network Content-Length should be of original gzipped file." + ); + finish(); +}); + +fetchXHR("hello.gz", function (xhr) { + my_ok(xhr.status == 200, "gzip load should be successful"); + my_ok( + xhr.responseText == expectedUncompressedResponse, + "gzip load should have synthesized response." + ); + my_ok( + xhr.getResponseHeader("Content-Encoding") == "gzip", + "Content-Encoding should be gzip." + ); + my_ok( + xhr.getResponseHeader("Content-Length") == "35", + "Content-Length should be of original gzipped file." + ); + finish(); +}); + +fetchXHR("hello-after-extracting.gz", function (xhr) { + my_ok(xhr.status == 200, "gzip load after extracting should be successful"); + my_ok( + xhr.responseText == expectedUncompressedResponse, + "gzip load after extracting should have synthesized response." + ); + my_ok( + xhr.getResponseHeader("Content-Encoding") == "gzip", + "Content-Encoding after extracting should be gzip." + ); + my_ok( + xhr.getResponseHeader("Content-Length") == "35", + "Content-Length after extracting should be of original gzipped file." + ); + finish(); +}); + +fetchXHR(corsServerURL + "?status=200&allowOrigin=*", function (xhr) { + my_ok( + xhr.status == 200, + "cross origin load with correct headers should be successful" + ); + my_ok( + xhr.getResponseHeader("access-control-allow-origin") == null, + "cors headers should be filtered out" + ); + finish(); +}); + +// Verify origin header is sent properly even when we have a no-intercept SW. +var uriOrigin = encodeURIComponent(origin); +fetchXHR( + "http://example.org" + + corsServerPath + + "?ignore&status=200&origin=" + + uriOrigin + + "&allowOrigin=" + + uriOrigin, + function (xhr) { + my_ok( + xhr.status == 200, + "cross origin load with correct headers should be successful" + ); + my_ok( + xhr.getResponseHeader("access-control-allow-origin") == null, + "cors headers should be filtered out" + ); + finish(); + } +); + +// Verify that XHR is considered CORS tainted even when original URL is same-origin +// redirected to cross-origin. +fetchXHR( + redirectURL([ + { server: origin }, + { server: "http://example.org", allowOrigin: origin }, + ]), + function (xhr) { + my_ok( + xhr.status == 200, + "cross origin load with correct headers should be successful" + ); + my_ok( + xhr.getResponseHeader("access-control-allow-origin") == null, + "cors headers should be filtered out" + ); + finish(); + } +); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses not to intercept. This requires a +// preflight request, which the SW must not be allowed to intercept. +fetchXHR( + corsServerURL + "?status=200&allowOrigin=*", + null, + function (xhr) { + my_ok( + xhr.status == 0, + "cross origin load with incorrect headers should be a failure" + ); + finish(); + }, + [["X-Unsafe", "unsafe"]] +); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses to intercept and respond with a +// cross-origin fetch. This requires a preflight request, which the SW must not +// be allowed to intercept. +fetchXHR( + "http://example.org" + corsServerPath + "?status=200&allowOrigin=*", + null, + function (xhr) { + my_ok( + xhr.status == 0, + "cross origin load with incorrect headers should be a failure" + ); + finish(); + }, + [["X-Unsafe", "unsafe"]] +); + +// Test that when the page fetches a url the controlling SW forces a redirect to +// another location. This other location fetch should also be intercepted by +// the SW. +fetchXHR("something.txt", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "something else response body", + "load should have something else" + ); + finish(); +}); + +// Test fetch will internally get it's SkipServiceWorker flag set. The request is +// made from the SW through fetch(). fetch() fetches a server-side JavaScript +// file that force a redirect. The redirect location fetch does not go through +// the SW. +fetchXHR("redirect_serviceworker.sjs", function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "// empty worker, always succeed!\n", + "load should have redirection content" + ); + finish(); +}); + +fetchXHR( + "empty-header", + function (xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok( + xhr.responseText == "emptyheader", + "load should have the expected content" + ); + finish(); + }, + null, + [["emptyheader", ""]] +); + +expectAsyncResult(); +fetch( + "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*" +).then( + function (res) { + my_ok(res.ok, "Valid CORS request should receive valid response"); + my_ok(res.type == "cors", "Response type should be CORS"); + res.text().then(function (body) { + my_ok( + body === "hello pass\n", + "cors response body should match" + ); + finish(); + }); + }, + function (e) { + my_ok(false, "CORS Fetch failed"); + finish(); + } +); + +expectAsyncResult(); +fetch( + "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200", + { mode: "no-cors" } +).then( + function (res) { + my_ok(res.type == "opaque", "Response type should be opaque"); + my_ok(res.status == 0, "Status should be 0"); + res.text().then(function (body) { + my_ok(body === "", "opaque response body should be empty"); + finish(); + }); + }, + function (e) { + my_ok(false, "no-cors Fetch failed"); + finish(); + } +); + +expectAsyncResult(); +fetch("opaque-on-same-origin").then( + function (res) { + my_ok( + false, + "intercepted opaque response for non no-cors request should fail." + ); + finish(); + }, + function (e) { + my_ok( + true, + "intercepted opaque response for non no-cors request should fail." + ); + finish(); + } +); + +expectAsyncResult(); +fetch("http://example.com/opaque-no-cors", { mode: "no-cors" }).then( + function (res) { + my_ok( + res.type == "opaque", + "intercepted opaque response for no-cors request should have type opaque." + ); + finish(); + }, + function (e) { + my_ok( + false, + "intercepted opaque response for no-cors request should pass." + ); + finish(); + } +); + +expectAsyncResult(); +fetch("http://example.com/cors-for-no-cors", { mode: "no-cors" }).then( + function (res) { + my_ok( + res.type == "cors", + "synthesize CORS response should result in outer CORS response" + ); + finish(); + }, + function (e) { + my_ok(false, "cors-for-no-cors request should not reject"); + finish(); + } +); + +function arrayBufferFromString(str) { + var arr = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) { + arr[i] = str.charCodeAt(i); + } + return arr; +} + +expectAsyncResult(); +fetch(new Request("body-simple", { method: "POST", body: "my body" })) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body == "my bodymy body", + "the body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +fetch( + new Request("body-arraybufferview", { + method: "POST", + body: arrayBufferFromString("my body"), + }) +) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body == "my bodymy body", + "the ArrayBufferView body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +fetch( + new Request("body-arraybuffer", { + method: "POST", + body: arrayBufferFromString("my body").buffer, + }) +) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body == "my bodymy body", + "the ArrayBuffer body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +var usp = new URLSearchParams(); +usp.set("foo", "bar"); +usp.set("baz", "qux"); +fetch(new Request("body-urlsearchparams", { method: "POST", body: usp })) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body == "foo=bar&baz=quxfoo=bar&baz=qux", + "the URLSearchParams body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +var fd = new FormData(); +fd.set("foo", "bar"); +fd.set("baz", "qux"); +fetch(new Request("body-formdata", { method: "POST", body: fd })) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body.indexOf('Content-Disposition: form-data; name="foo"\r\n\r\nbar') < + body.indexOf('Content-Disposition: form-data; name="baz"\r\n\r\nqux'), + "the FormData body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +fetch( + new Request("body-blob", { + method: "POST", + body: new Blob(new String("my body")), + }) +) + .then(function (res) { + return res.text(); + }) + .then(function (body) { + my_ok( + body == "my bodymy body", + "the Blob body of the intercepted fetch should be visible in the SW" + ); + finish(); + }); + +expectAsyncResult(); +fetch("interrupt.sjs").then( + function (res) { + my_ok(true, "interrupted fetch succeeded"); + res.text().then( + function (body) { + my_ok(false, "interrupted fetch shouldn't have complete body"); + finish(); + }, + function () { + my_ok(true, "interrupted fetch shouldn't have complete body"); + finish(); + } + ); + }, + function (e) { + my_ok(false, "interrupted fetch failed"); + finish(); + } +); + +["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"].forEach(function (method) { + fetchXHRWithMethod("xhr-method-test.txt", method, function (xhr) { + my_ok(xhr.status == 200, method + " load should be successful"); + if (method === "HEAD") { + my_ok( + xhr.responseText == "", + method + "load should not have synthesized response" + ); + } else { + my_ok( + xhr.responseText == "intercepted " + method, + method + " load should have synthesized response" + ); + } + finish(); + }); +}); + +expectAsyncResult(); +fetch(new Request("empty-header", { headers: { emptyheader: "" } })) + .then(function (res) { + return res.text(); + }) + .then( + function (body) { + my_ok( + body == "emptyheader", + "The empty header was observed in the fetch event" + ); + finish(); + }, + function (err) { + my_ok(false, "A promise was rejected with " + err); + finish(); + } + ); + +expectAsyncResult(); +fetch("fetchevent-extendable") + .then(function (res) { + return res.text(); + }) + .then( + function (body) { + my_ok(body == "extendable", "FetchEvent inherits from ExtendableEvent"); + finish(); + }, + function (err) { + my_ok(false, "A promise was rejected with " + err); + finish(); + } + ); + +expectAsyncResult(); +fetch("fetchevent-request") + .then(function (res) { + return res.text(); + }) + .then( + function (body) { + my_ok(body == "non-nullable", "FetchEvent.request must be non-nullable"); + finish(); + }, + function (err) { + my_ok(false, "A promise was rejected with " + err); + finish(); + } + ); -- cgit v1.2.3