summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/storage-access-api/helpers.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/storage-access-api/helpers.js
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/storage-access-api/helpers.js')
-rw-r--r--testing/web-platform/tests/storage-access-api/helpers.js306
1 files changed, 306 insertions, 0 deletions
diff --git a/testing/web-platform/tests/storage-access-api/helpers.js b/testing/web-platform/tests/storage-access-api/helpers.js
new file mode 100644
index 0000000000..0fd5d814db
--- /dev/null
+++ b/testing/web-platform/tests/storage-access-api/helpers.js
@@ -0,0 +1,306 @@
+'use strict';
+
+function processQueryParams() {
+ const url = new URL(window.location);
+ const queryParams = url.searchParams;
+ return {
+ topLevelDocument: window === window.top,
+ testPrefix: queryParams.get("testCase") || "top-level-context",
+ };
+}
+
+// Create an iframe element, set it up using `setUpFrame`, and optionally fetch
+// tests in it. Returns the created frame, after it has loaded.
+async function CreateFrameHelper(setUpFrame, fetchTests) {
+ const frame = document.createElement('iframe');
+ const promise = new Promise((resolve, reject) => {
+ frame.onload = () => resolve(frame);
+ frame.onerror = reject;
+ });
+
+ setUpFrame(frame);
+
+ if (fetchTests) {
+ await fetch_tests_from_window(frame.contentWindow);
+ }
+ return promise;
+}
+
+// Create an iframe element with content loaded from `sourceURL`, append it to
+// the document, and optionally fetch tests. Returns the loaded frame, once
+// ready.
+function CreateFrame(sourceURL, fetchTests = false) {
+ return CreateFrameHelper((frame) => {
+ frame.src = sourceURL;
+ document.body.appendChild(frame);
+ }, fetchTests);
+}
+
+// Create a new iframe with content loaded from `sourceURL`, and fetches tests.
+// Returns the loaded frame, once ready.
+function RunTestsInIFrame(sourceURL) {
+ return CreateFrame(sourceURL, true);
+}
+
+function RunTestsInNestedIFrame(sourceURL) {
+ return CreateFrameHelper((frame) => {
+ document.body.appendChild(frame);
+ frame.contentDocument.write(`
+ <script src="/resources/testharness.js"></script>
+ <script src="helpers.js"></script>
+ <body>
+ <script>
+ RunTestsInIFrame("${sourceURL}");
+ </script>
+ `);
+ frame.contentDocument.close();
+ }, true);
+}
+
+function CreateDetachedFrame() {
+ const frame = document.createElement('iframe');
+ document.body.append(frame);
+ const inner_doc = frame.contentDocument;
+ frame.remove();
+ return inner_doc;
+}
+
+function CreateDocumentViaDOMParser() {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString('<html></html>', 'text/html');
+ return doc;
+}
+
+function RunCallbackWithGesture(callback) {
+ return test_driver.bless('run callback with user gesture', callback);
+}
+
+// Sends a message to the given target window and returns a promise that
+// resolves when a reply was sent.
+function PostMessageAndAwaitReply(message, targetWindow) {
+ const timestamp = window.performance.now();
+ const reply = ReplyPromise(timestamp);
+ targetWindow.postMessage({timestamp, ...message}, "*");
+ return reply;
+}
+
+// Returns a promise that resolves when the next "reply" is received via
+// postMessage. Takes a "timestamp" argument to validate that the received
+// message belongs to its original counterpart.
+function ReplyPromise(timestamp) {
+ return new Promise((resolve) => {
+ const listener = (event) => {
+ if (event.data.timestamp == timestamp) {
+ window.removeEventListener("message", listener);
+ resolve(event.data.data);
+ }
+ };
+ window.addEventListener("message", listener);
+ });
+}
+
+// Returns a promise that resolves when the given frame fires its load event.
+function LoadPromise(frame) {
+ return new Promise((resolve) => {
+ frame.addEventListener("load", (event) => {
+ resolve();
+ }, { once: true });
+ });
+}
+
+// Writes cookies via document.cookie in the given frame.
+function SetDocumentCookieFromFrame(frame, cookie) {
+ return PostMessageAndAwaitReply(
+ { command: "write document.cookie", cookie }, frame.contentWindow);
+}
+
+// Reads cookies via document.cookie in the given frame.
+function GetJSCookiesFromFrame(frame) {
+ return PostMessageAndAwaitReply(
+ { command: "document.cookie" }, frame.contentWindow);
+}
+
+async function DeleteCookieInFrame(frame, name, params) {
+ await SetDocumentCookieFromFrame(frame, `${name}=0; expires=${new Date(0).toUTCString()}; ${params};`);
+ assert_false(cookieStringHasCookie(name, '0', await GetJSCookiesFromFrame(frame)), `Verify that cookie '${name}' has been deleted.`);
+}
+
+// Tests whether the frame can write cookies via document.cookie. Note that this
+// overwrites, then optionally deletes, cookies named "cookie" and "foo".
+//
+// This function requires the caller to have included
+// /cookies/resources/cookie-helper.sub.js.
+async function CanFrameWriteCookies(frame, keep_after_writing = false) {
+ const cookie_suffix = "Secure;SameSite=None;Path=/";
+ await DeleteCookieInFrame(frame, "cookie", cookie_suffix);
+ await DeleteCookieInFrame(frame, "foo", cookie_suffix);
+
+ await SetDocumentCookieFromFrame(frame, `cookie=monster;${cookie_suffix}`);
+ await SetDocumentCookieFromFrame(frame, `foo=bar;${cookie_suffix}`);
+
+ const cookies = await GetJSCookiesFromFrame(frame);
+ const can_write = cookieStringHasCookie("cookie", "monster", cookies) &&
+ cookieStringHasCookie("foo", "bar", cookies);
+
+ if (!keep_after_writing) {
+ await DeleteCookieInFrame(frame, "cookie", cookie_suffix);
+ await DeleteCookieInFrame(frame, "foo", cookie_suffix);
+ }
+
+ return can_write;
+}
+
+// Sets a cookie in an unpartitioned context by creating a new frame
+// and requesting storage access in the frame.
+async function SetFirstPartyCookieAndUnsetStorageAccessPermission(origin) {
+ let frame = await CreateFrame(`${origin}/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js`);
+ await SetPermissionInFrame(frame, [{ name: 'storage-access' }, 'granted']);
+ await RequestStorageAccessInFrame(frame);
+ await SetDocumentCookieFromFrame(frame, `cookie=unpartitioned;Secure;SameSite=None;Path=/`);
+ await SetPermissionInFrame(frame, [{ name: 'storage-access' }, 'prompt']);
+}
+
+// Tests for the presence of the unpartitioned cookie set by SetFirstPartyCookieAndUnsetStorageAccessPermission
+// in both the `document.cookie` variable and same-origin subresource \
+// Request Headers in the given frame
+async function HasUnpartitionedCookie(frame) {
+ let frameDocumentCookie = await GetJSCookiesFromFrame(frame);
+ let jsAccess = cookieStringHasCookie("cookie", "unpartitioned", frameDocumentCookie);
+ const httpCookie = await FetchSubresourceCookiesFromFrame(frame, "");
+ let httpAccess = cookieStringHasCookie("cookie", "unpartitioned", httpCookie);
+ assert_equals(jsAccess, httpAccess, "HTTP and Javascript cookies must be in sync");
+ return jsAccess && httpAccess;
+}
+
+// Tests whether the current frame can read and write cookies via HTTP headers.
+// This deletes, writes, reads, then deletes a cookie named "cookie".
+async function CanAccessCookiesViaHTTP() {
+ // We avoid reusing SetFirstPartyCookieAndUnsetStorageAccessPermission here, since that bypasses the
+ // cookie-accessibility settings that we want to check here.
+ await fetch(`${window.location.origin}/storage-access-api/resources/set-cookie-header.py?cookie=1;path=/;SameSite=None;Secure`);
+ const http_cookies = await fetch(`${window.location.origin}/storage-access-api/resources/echo-cookie-header.py`)
+ .then((resp) => resp.text());
+ const can_access = cookieStringHasCookie("cookie", "1", http_cookies);
+
+ erase_cookie_from_js("cookie", "SameSite=None;Secure;Path=/");
+
+ return can_access;
+}
+
+// Tests whether the current frame can read and write cookies via
+// document.cookie. This deletes, writes, reads, then deletes a cookie named
+// "cookie".
+function CanAccessCookiesViaJS() {
+ erase_cookie_from_js("cookie", "SameSite=None;Secure;Path=/");
+ assert_false(cookieStringHasCookie("cookie", "1", document.cookie));
+
+ document.cookie = "cookie=1;SameSite=None;Secure;Path=/";
+ const can_access = cookieStringHasCookie("cookie", "1", document.cookie);
+
+ erase_cookie_from_js("cookie", "SameSite=None;Secure;Path=/");
+ assert_false(cookieStringHasCookie("cookie", "1", document.cookie));
+
+ return can_access;
+}
+
+// Reads cookies via the `httpCookies` variable in the given frame.
+function GetHTTPCookiesFromFrame(frame) {
+ return PostMessageAndAwaitReply(
+ { command: "httpCookies" }, frame.contentWindow);
+}
+
+// Executes document.hasStorageAccess in the given frame.
+function FrameHasStorageAccess(frame) {
+ return PostMessageAndAwaitReply(
+ { command: "hasStorageAccess" }, frame.contentWindow);
+}
+
+// Executes document.requestStorageAccess in the given frame.
+function RequestStorageAccessInFrame(frame) {
+ return PostMessageAndAwaitReply(
+ { command: "requestStorageAccess" }, frame.contentWindow);
+}
+
+// Executes test_driver.set_permission in the given frame, with the provided
+// arguments.
+function SetPermissionInFrame(frame, args = []) {
+ return PostMessageAndAwaitReply(
+ { command: "set_permission", args }, frame.contentWindow);
+}
+
+// Waits for a storage-access permission change and resolves with the current
+// state.
+function ObservePermissionChange(frame, args = []) {
+ return PostMessageAndAwaitReply(
+ { command: "observe_permission_change", args }, frame.contentWindow);
+}
+
+// Executes `location.reload()` in the given frame. The returned promise
+// resolves when the frame has finished reloading.
+function FrameInitiatedReload(frame) {
+ const reload = LoadPromise(frame);
+ frame.contentWindow.postMessage({ command: "reload" }, "*");
+ return reload;
+}
+
+// Executes `location.href = url` in the given frame. The returned promise
+// resolves when the frame has finished navigating.
+function FrameInitiatedNavigation(frame, url) {
+ const load = LoadPromise(frame);
+ frame.contentWindow.postMessage({ command: "navigate", url }, "*");
+ return load;
+}
+
+// Makes a subresource request to the provided host in the given frame, and
+// returns the cookies that were included in the request.
+function FetchSubresourceCookiesFromFrame(frame, host) {
+ return FetchFromFrame(frame, `${host}/storage-access-api/resources/echo-cookie-header.py`);
+}
+
+// Makes a subresource request to the provided host in the given frame, and
+// returns the response.
+function FetchFromFrame(frame, url) {
+ return PostMessageAndAwaitReply(
+ { command: "cors fetch", url }, frame.contentWindow);
+}
+
+// Makes a subresource request to the provided host in the given frame with
+// the mode set to 'no-cors'
+function NoCorsSubresourceCookiesFromFrame(frame, host) {
+ const url = `${host}/storage-access-api/resources/echo-cookie-header.py`;
+ return PostMessageAndAwaitReply(
+ { command: "no-cors fetch", url }, frame.contentWindow);
+}
+
+// Tries to set storage access policy, ignoring any errors.
+//
+// Note: to discourage the writing of tests that assume unpartitioned cookie
+// access by default, any test that calls this with `value` == "blocked" should
+// do so as the first step in the test.
+async function MaybeSetStorageAccess(origin, embedding_origin, value) {
+ try {
+ await test_driver.set_storage_access(origin, embedding_origin, value);
+ } catch (e) {
+ // Ignore, can be unimplemented if the platform blocks cross-site cookies
+ // by default. If this failed without default blocking we'll notice it later
+ // in the test.
+ }
+}
+
+// Starts a dedicated worker in the given frame.
+function StartDedicatedWorker(frame) {
+ return PostMessageAndAwaitReply(
+ { command: "start_dedicated_worker" }, frame.contentWindow);
+}
+
+// Sends a message to the dedicated worker in the given frame.
+function MessageWorker(frame, message = {}) {
+ return PostMessageAndAwaitReply(
+ { command: "message_worker", message }, frame.contentWindow);
+}
+// Opens a WebSocket connection to origin from within frame, and
+// returns the cookie header that was sent during the handshake.
+function ReadCookiesFromWebSocketConnection(frame, origin) {
+ return PostMessageAndAwaitReply(
+ { command: "get_cookie_via_websocket", origin}, frame.contentWindow);
+}