diff options
Diffstat (limited to 'testing/web-platform/tests/shared-storage/resources')
40 files changed, 1025 insertions, 0 deletions
diff --git a/testing/web-platform/tests/shared-storage/resources/cors-redirect.py b/testing/web-platform/tests/shared-storage/resources/cors-redirect.py new file mode 100644 index 0000000000..a475ee4f96 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/cors-redirect.py @@ -0,0 +1,21 @@ +def main(request, response): + """Simple handler that causes redirection but also adds the + "Access-Control-Allow-Origin: *" header. + + The request should typically have two query parameters: + status - The status to use for the redirection. Defaults to 302. + location - The resource to redirect to. + """ + status = 302 + if b"status" in request.GET: + try: + status = int(request.GET.first(b"status")) + except ValueError: + pass + + response.status = status + + location = request.GET.first(b"location") + + response.headers.set(b"Location", location) + response.headers.append(b"Access-Control-Allow-Origin", b"*") diff --git a/testing/web-platform/tests/shared-storage/resources/credentials-test-helper.py b/testing/web-platform/tests/shared-storage/resources/credentials-test-helper.py new file mode 100644 index 0000000000..81a988e358 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/credentials-test-helper.py @@ -0,0 +1,22 @@ +def main(request, response): + """ + A handler that does either one of the following based on the provided + "action" parameter: + 1) action="store-cookie": Stores the provided token and the request cookie + to the stash, and returns a regular module script content. + 2) action="get-cookie": Retrieves and returns the content stored in the + stash at the provided token. + """ + token = request.GET[b"token"] + action = request.GET[b"action"] + + response.status = 200 + response.headers.append(b"Content-Type", b"text/javascript") + + if action == b"store-cookie": + cookie = request.headers.get(b"Cookie", b"NO_COOKIE_HEADER") + request.server.stash.put(token, cookie) + return b"" + else: + assert action == b"get-cookie" + return request.server.stash.take(token) diff --git a/testing/web-platform/tests/shared-storage/resources/delete-key.https.html b/testing/web-platform/tests/shared-storage/resources/delete-key.https.html new file mode 100644 index 0000000000..41595f3ee3 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/delete-key.https.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<script src=/resources/testharness.js></script> +<script src=/shared-storage/resources/util.js></script> +<script src=/fenced-frame/resources/utils.js></script> +<body> +<script> + +async function init() { + const [outerKey] = parseKeylist(); + const parsedData = parseExpectedKeyData(); + await sharedStorage.delete(parsedData['expectedKey']); + await writeValueToServer(outerKey, "delete_key_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/delete-key.https.html.headers b/testing/web-platform/tests/shared-storage/resources/delete-key.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/delete-key.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html b/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html new file mode 100644 index 0000000000..e4b59f7f7a --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<script src="/shared-storage/resources/util.js"></script> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> + +async function init() { + const [ancestorKey] = parseKeylist(); + + await addModuleOnce("/shared-storage/resources/embedder-context-module.js"); + await sharedStorage.run("report-context", + { + data: {'ancestorKey': ancestorKey} + }); + + await writeValueToServer(ancestorKey, "embedder_context_inner_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html.headers b/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-module.js b/testing/web-platform/tests/shared-storage/resources/embedder-context-module.js new file mode 100644 index 0000000000..782b97aea9 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-module.js @@ -0,0 +1,16 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class ReportContext { + async run(data) { + if (!data || !data.hasOwnProperty('ancestorKey')) { + return; + } + const ancestorKey = data['ancestorKey']; + const context = sharedStorage.context; + await sharedStorage.set(ancestorKey, context); + } +} + +register('report-context', ReportContext); diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-iframe.https.html b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-iframe.https.html new file mode 100644 index 0000000000..2ba25d1ed9 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-iframe.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<script src="/resources/testharness.js"></script> +<script src="/shared-storage/resources/util.js"></script> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> + +async function init() { + const [outerKey, innerKey] = parseKeylist(); + let url0 = generateURL("/shared-storage/resources/frame0.html", [innerKey]); + let url1 = generateURL("/shared-storage/resources/frame1.html", [innerKey]); + + await addModuleOnce("simple-module.js"); + let fencedFrameConfig = await sharedStorage.selectURL( + "verify-key-value", [{url: url0}, {url: url1}], + {data: {'expectedKey': innerKey, 'expectedValue': 'undefined'}, + resolveToConfig: true}); + assert_true(validateSelectURLResult(fencedFrameConfig, true)); + + attachFencedFrame(fencedFrameConfig, 'opaque-ads'); + const result = await nextValueFromServer(innerKey); + assert_equals(result, "frame1_loaded"); + + await writeValueToServer(outerKey, "embedder_context_nested_iframe_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html new file mode 100644 index 0000000000..c704a00b73 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<script src="/resources/testharness.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> +async function init() { + const [outerKey, innerKey0, innerKey1] = parseKeylist(); + let url0 = generateURL( + "/shared-storage/resources/embedder-context-inner.https.html", + [innerKey0]); + + attachIFrame(url0); + const result0 = await nextValueFromServer(innerKey0); + assert_equals(result0, "embedder_context_inner_loaded"); + + let url1 = generateURL( + "/shared-storage/resources/embedder-context-inner.https.html", + [innerKey1]); + url1 = getRemoteOriginURL(url1); + + attachIFrame(url1); + const result1 = await nextValueFromServer(innerKey1); + assert_equals(result1, "embedder_context_inner_loaded"); + + await writeValueToServer(outerKey, "embedder_context_nested_inner_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html.headers b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/embedder-context-nested-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/frame0.html b/testing/web-platform/tests/shared-storage/resources/frame0.html new file mode 100644 index 0000000000..c9b559fe83 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/frame0.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> +async function init() { + const [ancestor_key] = parseKeylist(); + writeValueToServer(ancestor_key, "frame0_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/frame0.html.headers b/testing/web-platform/tests/shared-storage/resources/frame0.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/frame0.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/frame1.html b/testing/web-platform/tests/shared-storage/resources/frame1.html new file mode 100644 index 0000000000..a34990dc03 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/frame1.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> +async function init() { + const [ancestor_key] = parseKeylist(); + writeValueToServer(ancestor_key, "frame1_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/frame1.html.headers b/testing/web-platform/tests/shared-storage/resources/frame1.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/frame1.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/register-service-worker-iframe.https.html b/testing/web-platform/tests/shared-storage/resources/register-service-worker-iframe.https.html new file mode 100644 index 0000000000..547ab1d93d --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/register-service-worker-iframe.https.html @@ -0,0 +1,66 @@ +<!doctype html> +<body> + <script src=/resources/testharness.js></script> + <script src=/common/utils.js></script> + <script src=/fenced-frame/resources/utils.js></script> + <script src=/shared-storage/resources/util.js></script> + <script src=/shared-storage/resources/util.sub.js></script> + <script src=/service-workers/service-worker/resources/test-helpers.sub.js></script> + <script> + const INTERMEDIATE_FRAME_SUFFIX = + 'able-fetch-request-fallback-to-network-iframe.https.html' + const ORIGIN = new URL("", location.href).origin; + + window.addEventListener('message', async function handler(event) { + const data = event.data; + if (data.script && data.scope && data.port) { + var absoluteScope = (new URL(data.scope, window.location).href); + let oldReg =await navigator.serviceWorker.getRegistration(data.scope); + if (oldReg && oldReg.scope === absoluteScope) { + await oldReg.unregister(); + } + let reg = await navigator.serviceWorker.register(data.script, + { scope: data.scope }); + let worker = reg.installing; + await new Promise(function(resolve) { + worker.addEventListener('statechange', function() { + if (worker.state == 'activated') { + resolve(); + } + }); + }); + assert_not_equals(worker, null, 'worker is installing'); + + let result = await loadNestedSharedStorageFrameInNewFrame({ + key: 'c', value: 'd', + hasSharedStorageWritableAttribute: true, + // Same-origin to this frame, cross-origin to top. + isSameOrigin: true, + }); + const urls = [ + { + "url": ORIGIN + data.scope + INTERMEDIATE_FRAME_SUFFIX, + "mode": "navigate", + "SSWHeader": "null" + }, + { + "url": ORIGIN + "/resources/testharness.js", + "mode": "no-cors", + "SSWHeader": "null" + }, + { + "url": ORIGIN + result.nestedFrameUrl, + "mode": "navigate", + "SSWHeader": "null" + }, + ]; + await checkInterceptedUrls(worker, urls); + await verifyKeyValueForOrigin('c', 'd', ORIGIN); + await deleteKeyForOrigin('c', ORIGIN); + data.port.postMessage({msg: 'test completed'}); + reg.unregister() + window.removeEventListener('message', handler); + } + }); + </script> +</body> diff --git a/testing/web-platform/tests/shared-storage/resources/report.py b/testing/web-platform/tests/shared-storage/resources/report.py new file mode 100644 index 0000000000..7d0fa36019 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/report.py @@ -0,0 +1,25 @@ +def main(request, response): + + # `token` should be a unique UUID request parameter for the duration of this + # request. It will get stored in the server stash and will be used later in + # a query request. + # `query` should be a request parameter indicating the request would like + # to know how many times the server has seen the request (with the + # same token). + token = request.GET.first(b"token", None) + is_query = request.GET.first(b"query", None) is not None + with request.server.stash.lock: + value = request.server.stash.take(token) + count = 0 + if value is not None: + count = int(value) + if is_query: + request.server.stash.put(token, count) + else: + count += 1 + request.server.stash.put(token, count) + + headers = [] + if is_query: + headers = [(b"Count", count)] + return (200, headers, b"") diff --git a/testing/web-platform/tests/shared-storage/resources/select-url-permissions-policy-helper.html b/testing/web-platform/tests/shared-storage/resources/select-url-permissions-policy-helper.html new file mode 100644 index 0000000000..b70d763ad4 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/select-url-permissions-policy-helper.html @@ -0,0 +1,18 @@ +<!doctype html> +<body> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/shared-storage/resources/util.js"></script> + <script> + 'use strict'; + + window.onload = async function() { + if (await IsSharedStorageSelectUrlAllowedByPermissionsPolicy()) { + parent.postMessage({ type: 'availability-result', enabled: true }, '*'); + return; + } + + parent.postMessage({ type: 'availability-result', enabled: false }, '*'); + } + </script> +</body> diff --git a/testing/web-platform/tests/shared-storage/resources/sender0.html b/testing/web-platform/tests/shared-storage/resources/sender0.html new file mode 100644 index 0000000000..e3d0475f12 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/sender0.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<script src="/fenced-frame/resources/utils.js"></script> +<body> +<script> +async function init() { + const [ancestor_key] = parseKeylist(); + + window.fence.reportEvent({ + eventType: 'click', + eventData: "user clicked", + destination: ['shared-storage-select-url'] + }); + + writeValueToServer(ancestor_key, "sender0_reported"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/sender0.html.headers b/testing/web-platform/tests/shared-storage/resources/sender0.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/sender0.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/set-key-value.https.html b/testing/web-platform/tests/shared-storage/resources/set-key-value.https.html new file mode 100644 index 0000000000..ba0a24ceb2 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/set-key-value.https.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<script src=/resources/testharness.js></script> +<script src=/shared-storage/resources/util.js></script> +<script src=/fenced-frame/resources/utils.js></script> +<body> +<script> + +async function init() { + const [outerKey] = parseKeylist(); + const parsedData = parseExpectedKeyAndValueData(); + await sharedStorage.set(parsedData['expectedKey'], + parsedData['expectedValue']); + await writeValueToServer(outerKey, "set_key_value_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-permissions-policy-helper.html b/testing/web-platform/tests/shared-storage/resources/shared-storage-permissions-policy-helper.html new file mode 100644 index 0000000000..d87092aad1 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-permissions-policy-helper.html @@ -0,0 +1,18 @@ +<!doctype html> +<body> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/shared-storage/resources/util.js"></script> + <script> + 'use strict'; + + window.onload = async function() { + if (await AreSharedStorageMethodsAllowedByPermissionsPolicy()) { + parent.postMessage({ type: 'availability-result', enabled: true }, '*'); + return; + } + + parent.postMessage({ type: 'availability-result', enabled: false }, '*'); + } + </script> +</body> diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-iframe.https.html b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-iframe.https.html new file mode 100644 index 0000000000..3451d91477 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-iframe.https.html @@ -0,0 +1,56 @@ +<!doctype html> +<body> + <script src=/resources/testharness.js></script> + <script> + function loadImage(url, hasSharedStorageWritableAttribute) { + return new Promise(function(resolve, reject) { + var img = document.createElement('img'); + document.body.appendChild(img); + img.onload = function() { + resolve(img); + }; + img.onerror = function() { + reject(new Error('Image load failed')); + }; + if (hasSharedStorageWritableAttribute) { + img.sharedStorageWritable = true; + } + img.src = url; + }); + } + + function loadFrame(url, hasSharedStorageWritableAttribute) { + return new Promise(function(resolve, reject) { + var frame = document.createElement('iframe'); + document.body.appendChild(frame); + frame.onload = function() { + window.parent.postMessage({msg: 'iframe loaded'}, "*"); + resolve(frame); + }; + frame.onerror = function() { + reject(new Error('Nested iframe load failed')); + }; + if (hasSharedStorageWritableAttribute) { + frame.sharedStorageWritable = true; + } + frame.src = url; + }); + } + + function fetchUrl(url, hasSharedStorageWritableAttribute) { + return new Promise(function(resolve, reject) { + fetch(url, {sharedStorageWritable: + hasSharedStorageWritableAttribute}) + .then(response => { + if (!response.ok) { + throw new Error('Failed to fetch ' + url + '; ' + + String(response.status) + ' ' + response.statusText); + } + resolve(response); + }).catch(error => { + reject(error); + }); + }); + } + </script> +</body> diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-worker.js b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-worker.js new file mode 100644 index 0000000000..d304669ea1 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-fetch-request-fallback-to-network-worker.js @@ -0,0 +1,15 @@ +var requests = []; + +self.addEventListener('message', function(event) { + event.data.port.postMessage({requests: requests}); + requests = []; +}); + +self.addEventListener('fetch', function(event) { + let maybeHeader = event.request.headers.get('Sec-Shared-Storage-Writable'); + requests.push({ + url: event.request.url, + mode: event.request.mode, + SSWHeader: String(maybeHeader), + }); +}); diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html new file mode 100644 index 0000000000..87dbe81a2b --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html @@ -0,0 +1,29 @@ +<!doctype html> +<body> + <script src=/resources/testharness.js></script> + <script src=/common/utils.js></script> + <script src=/fenced-frame/resources/utils.js></script> + <script src=/shared-storage/resources/util.js></script> + <script src=/shared-storage/resources/util.sub.js></script> + <script> + +async function init() { + const sameOrigin = new URL("", location.href).origin; + const [outerKey] = parseKeylist(); + let {expectedKey, expectedValue} = parseExpectedKeyAndValueData(); + const promise = navigateSharedStorageIframe({ + hasSharedStorageWritableAttribute: true, + rawWriteHeader: `set;key=${expectedKey};value=${expectedValue}`, + isSameOrigin: true, + expectSharedStorageWritableHeader: true, + }); + promise.then(() => { + verifyKeyValueForOrigin(expectedKey, expectedValue, sameOrigin); + deleteKeyForOrigin(expectedKey, sameOrigin); + writeValueToServer(outerKey, "writable_iframe_in_fenced_inner_loaded"); + }); +} + +init(); + </script> +</body> diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html.headers b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-iframe-in-fenced-inner.https.sub.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png Binary files differnew file mode 100644 index 0000000000..818c71d03f --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png.sub.headers b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png.sub.headers new file mode 100644 index 0000000000..9f2cb718aa --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel-write.png.sub.headers @@ -0,0 +1,3 @@ +Timing-Allow-Origin: * +Access-Control-Allow-Origin: * +Shared-Storage-Write: {{GET[write]}} diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png Binary files differnew file mode 100644 index 0000000000..818c71d03f --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png.sub.headers b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png.sub.headers new file mode 100644 index 0000000000..8b541e7b13 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-writable-pixel.png.sub.headers @@ -0,0 +1,3 @@ +Timing-Allow-Origin: * +Access-Control-Allow-Origin: * +Shared-Storage-Write: set;key={{GET[key]}};value={{GET[value]}} diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-write-notify-parent.py b/testing/web-platform/tests/shared-storage/resources/shared-storage-write-notify-parent.py new file mode 100644 index 0000000000..63041218c8 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-write-notify-parent.py @@ -0,0 +1,23 @@ +from wptserve.utils import isomorphic_decode +from wptserve.utils import isomorphic_encode +from urllib.parse import unquote + +def unescape_query_value(query_value_bytes): + return isomorphic_encode(unquote(isomorphic_decode(query_value_bytes))) + +def main(request, response): + writable_header = request.headers.get( + b"Sec-Shared-Storage-Writable", + b"NO_SHARED_STORAGE_WRITABLE_HEADER") + response.headers.append(b"Content-Type", b"text/html") + response.headers.append(b"Access-Control-Allow-Origin", b"*") + response.headers.append(b"Supports-Loading-Mode", b"fenced-frame") + if writable_header == b"?1" and b'write' in request.GET: + write_header = unescape_query_value(request.GET[b'write']) + response.headers.append(b"Shared-Storage-Write", write_header) + response.content = b''' +<script> + let parentOrOpener = window.opener || window.parent; + parentOrOpener.postMessage({ sharedStorageWritableHeader: '%s'}, "*"); +</script> +''' % (writable_header) diff --git a/testing/web-platform/tests/shared-storage/resources/shared-storage-write.py b/testing/web-platform/tests/shared-storage/resources/shared-storage-write.py new file mode 100644 index 0000000000..fabe1eba26 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/shared-storage-write.py @@ -0,0 +1,16 @@ +from wptserve.utils import isomorphic_decode +from wptserve.utils import isomorphic_encode +from urllib.parse import unquote + +def unescape_query_value(query_value_bytes): + return isomorphic_encode(unquote(isomorphic_decode(query_value_bytes))) + +def main(request, response): + writable_header = request.headers.get( + b"Sec-Shared-Storage-Writable", + b"NO_SHARED_STORAGE_WRITABLE_HEADER") + response.headers.append(b"Access-Control-Allow-Origin", b"*") + if writable_header == b"?1" and b'write' in request.GET: + write_header = unescape_query_value(request.GET[b'write']) + response.headers.append(b"Shared-Storage-Write", write_header) + response.content = writable_header diff --git a/testing/web-platform/tests/shared-storage/resources/simple-module.js b/testing/web-platform/tests/shared-storage/resources/simple-module.js new file mode 100644 index 0000000000..620a3592f2 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/simple-module.js @@ -0,0 +1,53 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var globalVar = 0; + +class TestURLSelectionOperation { + async run(urls, data) { + if (data && data.hasOwnProperty('mockResult')) { + return data['mockResult']; + } + + return -1; + } +} + +class IncrementGlobalVariableAndReturnOriginalValueOperation { + async run(urls, data) { + return globalVar++; + } +} + +class VerifyKeyValue { + async run(urls, data) { + if (data && data.hasOwnProperty('expectedKey') && + data.hasOwnProperty('expectedValue')) { + const expectedValue = data['expectedValue']; + const value = await sharedStorage.get(data['expectedKey']); + if (value === expectedValue) { + return 1; + } + } + return -1; + } +} + +class VerifyKeyNotFound { + async run(urls, data) { + if (data && data.hasOwnProperty('expectedKey')) { + const value = await sharedStorage.get(data['expectedKey']); + if (typeof value === 'undefined') { + return 1; + } + } + return -1; + } +} + +register('test-url-selection-operation', TestURLSelectionOperation); +register('increment-global-variable-and-return-original-value-operation', + IncrementGlobalVariableAndReturnOriginalValueOperation); +register('verify-key-value', VerifyKeyValue); +register('verify-key-not-found', VerifyKeyNotFound); diff --git a/testing/web-platform/tests/shared-storage/resources/util.js b/testing/web-platform/tests/shared-storage/resources/util.js new file mode 100644 index 0000000000..4a7fcc4590 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/util.js @@ -0,0 +1,217 @@ +// META: script=/common/utils.js +// META: script=/fenced-frame/resources/utils.js +'use strict'; + +async function IsSharedStorageSelectUrlAllowedByPermissionsPolicy() { + const errorMessage = 'The \"shared-storage-select-url\" Permissions Policy denied the usage of window.sharedStorage.selectURL().'; + let allowedByPermissionsPolicy = true; + try { + // Run selectURL() with without addModule() and this should always fail. + // Check the error message to distinguish between the permissions policy + // error and the missing addModule() error. + await sharedStorage.selectURL("operation", [{url: "1.html"}]); + assert_unreached("did not fail"); + } catch (e) { + if (e.message === errorMessage) { + allowedByPermissionsPolicy = false; + } + } + + return allowedByPermissionsPolicy; +} + +// Execute all shared storage methods and capture their errors. Return true if +// the permissions policy allows all of them; return false if the permissions +// policy disallows all of them. Precondition: only these two outcomes are +// possible. +async function AreSharedStorageMethodsAllowedByPermissionsPolicy() { + let permissionsPolicyDeniedCount = 0; + const errorMessage = 'The \"shared-storage\" Permissions Policy denied the method on window.sharedStorage.'; + + try { + await window.sharedStorage.worklet.addModule('/shared-storage/resources/simple-module.js'); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + try { + await window.sharedStorage.run('operation'); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + try { + // Run selectURL() with without addModule() and this should always fail. + // Check the error message to distinguish between the permissions policy + // error and the missing addModule() error. + await sharedStorage.selectURL("operation", [{url: "1.html"}]); + assert_unreached("did not fail"); + } catch (e) { + if (e.message === errorMessage) { + ++permissionsPolicyDeniedCount; + } + } + + try { + await window.sharedStorage.set('a', 'b'); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + try { + await window.sharedStorage.append('a', 'b'); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + try { + await window.sharedStorage.clear(); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + try { + await window.sharedStorage.delete('a'); + } catch (e) { + assert_equals(e.message, errorMessage); + ++permissionsPolicyDeniedCount; + } + + if (permissionsPolicyDeniedCount === 0) + return true; + + return false; +} + +// Run sharedStorage.worklet.addModule once. +// @param {string} module - The URL to the module. +async function addModuleOnce(module) { + try { + await sharedStorage.worklet.addModule(module); + } catch (e) { + // Shared Storage needs to have a module added before we can operate on it. + // It is generated on the fly with this call, and since there's no way to + // tell through the API if a module already exists, wrap the addModule call + // in a try/catch so that if it runs a second time in a test, it will + // gracefully fail rather than bring the whole test down. + } +} + +// Validate the type of the result of sharedStorage.worklet.selectURL. +// @param result - The result of sharedStorage.worklet.selectURL. +// @param {boolean} - Whether sharedStorage.worklet.selectURL is resolved to +// a fenced frame config (true) or an urn:uuid (false). +// @return {boolean} Whether sharedStorage.worklet.selectURL returns an expected +// result type or not. +function validateSelectURLResult(result, resolve_to_config) { + if (resolve_to_config) { + return result instanceof FencedFrameConfig; + } + + return result.startsWith('urn:uuid:'); +} + +function updateUrlToUseNewOrigin(url, newOriginString) { + const origin = url.origin; + return new URL(url.toString().replace(origin, newOriginString)); +} + +function appendExpectedKeyAndValue(url, expectedKey, expectedValue) { + url.searchParams.append('expectedKey', expectedKey); + url.searchParams.append('expectedValue', expectedValue); + return url; +} + +function parseExpectedKeyAndValueData() { + const url = new URL(location.href); + const key = url.searchParams.get('expectedKey'); + const value = url.searchParams.get('expectedValue'); + return {'expectedKey': key, 'expectedValue': value}; +} + +function appendExpectedKey(url, expectedKey) { + url.searchParams.append('expectedKey', expectedKey); + return url; +} + +function parseExpectedKeyData() { + const url = new URL(location.href); + const key = url.searchParams.get('expectedKey'); + return {'expectedKey': key}; +} + +async function verifyKeyValueForOrigin(key, value, origin) { + const outerKey = token(); + const innerKey = token(); + let iframeUrl = generateURL( + '/shared-storage/resources/verify-key-value.https.html', + [outerKey, innerKey]); + iframeUrl = updateUrlToUseNewOrigin(iframeUrl, origin); + iframeUrl = appendExpectedKeyAndValue(iframeUrl, key, value); + + attachIFrame(iframeUrl); + const result = await nextValueFromServer(outerKey); + assert_equals(result, 'verify_key_value_loaded'); +} + +async function verifyKeyNotFoundForOrigin(key, origin) { + const outerKey = token(); + const innerKey = token(); + let iframeUrl = generateURL( + '/shared-storage/resources/verify-key-not-found.https.html', + [outerKey, innerKey]); + iframeUrl = updateUrlToUseNewOrigin(iframeUrl, origin); + iframeUrl = appendExpectedKey(iframeUrl, key); + + attachIFrame(iframeUrl); + const result = await nextValueFromServer(outerKey); + assert_equals(result, 'verify_key_not_found_loaded'); +} + +async function setKeyValueForOrigin(key, value, origin) { + const outerKey = token(); + let setIframeUrl = generateURL( + '/shared-storage/resources/set-key-value.https.html', [outerKey]); + setIframeUrl = updateUrlToUseNewOrigin(setIframeUrl, origin); + setIframeUrl = appendExpectedKeyAndValue(setIframeUrl, key, value); + + attachIFrame(setIframeUrl); + const result = await nextValueFromServer(outerKey); + assert_equals(result, 'set_key_value_loaded'); +} + +async function deleteKeyForOrigin(key, origin) { + const outerKey = token(); + let deleteIframeUrl = generateURL( + '/shared-storage/resources/delete-key.https.html', [outerKey]); + deleteIframeUrl = updateUrlToUseNewOrigin(deleteIframeUrl, origin); + deleteIframeUrl = appendExpectedKey(deleteIframeUrl, key); + + attachIFrame(deleteIframeUrl); + const result = await nextValueFromServer(outerKey); + assert_equals(result, 'delete_key_loaded'); +} + +function getFetchedUrls(worker) { + return new Promise(function(resolve) { + var channel = new MessageChannel(); + channel.port1.onmessage = function(msg) { + resolve(msg); + }; + worker.postMessage({port: channel.port2}, [channel.port2]); + }); +} + +function checkInterceptedUrls(worker, expectedRequests) { + return getFetchedUrls(worker).then(function(msg) { + let actualRequests = msg.data.requests; + assert_equals(actualRequests.length, expectedRequests.length); + assert_equals( + JSON.stringify(actualRequests), JSON.stringify(expectedRequests)); + }); +} diff --git a/testing/web-platform/tests/shared-storage/resources/util.sub.js b/testing/web-platform/tests/shared-storage/resources/util.sub.js new file mode 100644 index 0000000000..f147209d60 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/util.sub.js @@ -0,0 +1,117 @@ +function loadSharedStorageImage(data) { + let {key, value, hasSharedStorageWritableAttribute, isSameOrigin} = data; + const sameOriginSrc = `/shared-storage/resources/` + + `shared-storage-writable-pixel.png?key=${key}&value=${value}`; + const crossOriginSrc = + 'https://{{domains[www]}}:{{ports[https][0]}}' + sameOriginSrc; + + let image = document.createElement('img'); + image.src = isSameOrigin ? sameOriginSrc : crossOriginSrc; + if (hasSharedStorageWritableAttribute) { + image.sharedStorageWritable = true; + } + + const promise = new Promise((resolve, reject) => { + image.addEventListener('load', () => { + resolve(image); + }); + image.addEventListener('error', () => { + reject(new Error('Image load failed')); + }); + }); + + document.body.appendChild(image); + return promise; +} + +function navigateSharedStorageIframe(data) { + let { + hasSharedStorageWritableAttribute, + rawWriteHeader, + isSameOrigin, + expectSharedStorageWritableHeader + } = data; + const writeHeader = encodeURIComponent(rawWriteHeader); + const sameOriginSrc = + `/shared-storage/resources/shared-storage-write-notify-parent.py` + + `?write=${writeHeader}`; + const crossOriginSrc = + 'https://{{domains[www]}}:{{ports[https][0]}}' + sameOriginSrc; + + let frame = document.createElement('iframe'); + frame.src = isSameOrigin ? sameOriginSrc : crossOriginSrc; + if (hasSharedStorageWritableAttribute) { + frame.sharedStorageWritable = true; + } + + const expectedResult = expectSharedStorageWritableHeader ? + '?1' : + 'NO_SHARED_STORAGE_WRITABLE_HEADER'; + + function checkExpectedResult(data) { + assert_equals(data.sharedStorageWritableHeader, expectedResult); + } + + const promise = new Promise((resolve, reject) => { + window.addEventListener('message', async function handler(evt) { + if (evt.source === frame.contentWindow) { + checkExpectedResult(evt.data); + document.body.removeChild(frame); + window.removeEventListener('message', handler); + resolve(); + } + }); + window.addEventListener('error', () => { + reject(new Error('Navigation error')); + }); + }); + + document.body.appendChild(frame); + return promise; +} + +async function loadNestedSharedStorageFrameInNewFrame(data) { + const SCOPE = '/shared-storage/resources/shared-storage-writ'; + const INTERMEDIATE_FRAME_SUFFIX = + 'able-fetch-request-fallback-to-network-iframe.https.html' + const CROSS_ORIGIN = 'https://{{domains[www]}}:{{ports[https][0]}}'; + + let {key, value, hasSharedStorageWritableAttribute, isSameOrigin} = data; + + const windowPromise = new Promise((resolve, reject) => { + window.addEventListener('message', async function handler(evt) { + if (evt.data.msg && evt.data.msg === 'iframe loaded') { + window.removeEventListener('message', handler); + resolve(); + } + }); + window.addEventListener('error', () => { + reject(new Error('Navigation error')); + }); + }); + + const framePromise = new Promise((resolve, reject) => { + let frame = document.createElement('iframe'); + frame.src = SCOPE + INTERMEDIATE_FRAME_SUFFIX; + frame.onload = function() { + resolve(frame); + }; + frame.onerror = function() { + reject(new Error('Iframe load failed')); + }; + document.body.appendChild(frame); + }); + let frame = await framePromise; + + let rawWriteHeader = `set;key=${key};value=${value}`; + let writeHeader = encodeURIComponent(rawWriteHeader); + const sameOriginNestedSrc = `/shared-storage/resources/` + + `shared-storage-write.py?write=${writeHeader}`; + const nestedSrc = + isSameOrigin ? sameOriginNestedSrc : CROSS_ORIGIN + sameOriginNestedSrc; + + let nestedFrame = frame.contentWindow.loadFrame( + nestedSrc, hasSharedStorageWritableAttribute); + await windowPromise; + return {frame: frame, nestedFrame: nestedFrame, nestedFrameUrl: nestedSrc}; +} diff --git a/testing/web-platform/tests/shared-storage/resources/verify-get-undefined-module.js b/testing/web-platform/tests/shared-storage/resources/verify-get-undefined-module.js new file mode 100644 index 0000000000..56bc2f51e2 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-get-undefined-module.js @@ -0,0 +1,17 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class VerifyGetUndefinedURLSelectionOperation { + async run(urls, data) { + if (await sharedStorage.get('key-not-previously-set') === undefined) { + return 1; + } + + return -1; + } +} + +register( + 'verify-get-undefined-url-selection-operation', + VerifyGetUndefinedURLSelectionOperation); diff --git a/testing/web-platform/tests/shared-storage/resources/verify-key-not-found.https.html b/testing/web-platform/tests/shared-storage/resources/verify-key-not-found.https.html new file mode 100644 index 0000000000..f1c7919c19 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-key-not-found.https.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<script src=/resources/testharness.js></script> +<script src=/shared-storage/resources/util.js></script> +<script src=/fenced-frame/resources/utils.js></script> +<body> +<script> + +async function init() { + const [outerKey, innerKey] = parseKeylist(); + const parsedData = parseExpectedKeyData(); + let url0 = generateURL("/shared-storage/resources/frame0.html", [innerKey]); + let url1 = generateURL("/shared-storage/resources/frame1.html", [innerKey]); + + await addModuleOnce("/shared-storage/resources/simple-module.js"); + let fencedFrameConfig = await sharedStorage.selectURL( + "verify-key-not-found", [{url: url0}, {url: url1}], + {data: parsedData, resolveToConfig: true}); + assert_true(validateSelectURLResult(fencedFrameConfig, true)); + + attachFencedFrame(fencedFrameConfig, 'opaque-ads'); + const result = await nextValueFromServer(innerKey); + const dataStr = JSON.stringify(parsedData); + assert_equals(result, "frame1_loaded", + `verifying key ${dataStr} not found for ${location.origin};`); + + await writeValueToServer(outerKey, + "verify_key_not_found_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html b/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html new file mode 100644 index 0000000000..20c4694104 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<script src=/resources/testharness.js></script> +<script src=/shared-storage/resources/util.js></script> +<script src=/fenced-frame/resources/utils.js></script> +<body> +<script> + +async function init() { + const [outerKey, innerKey] = parseKeylist(); + const parsedData = parseExpectedKeyAndValueData(); + let url0 = generateURL("/shared-storage/resources/frame0.html", [innerKey]); + let url1 = generateURL("/shared-storage/resources/frame1.html", [innerKey]); + + await addModuleOnce("/shared-storage/resources/simple-module.js"); + let fencedFrameConfig = await sharedStorage.selectURL( + "verify-key-value", [{url: url0}, {url: url1}], + {data: parsedData, resolveToConfig: true}); + assert_true(validateSelectURLResult(fencedFrameConfig, true)); + + attachFencedFrame(fencedFrameConfig, 'opaque-ads'); + const result = await nextValueFromServer(innerKey); + const dataStr = JSON.stringify(parsedData); + assert_equals(result, "frame1_loaded", + `verifying key, value ${dataStr} found for ${location.origin};`); + + await writeValueToServer(outerKey, + "verify_key_value_loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html.headers b/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-key-value.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/shared-storage/resources/verify-shared-storage.https.html b/testing/web-platform/tests/shared-storage/resources/verify-shared-storage.https.html new file mode 100644 index 0000000000..52c79e7e85 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-shared-storage.https.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<body> +<script> + try { + window.sharedStorage; + window.parent.postMessage({ accessSharedStorageResult: 'success'}, "*"); + } catch (error) { + window.parent.postMessage({ accessSharedStorageResult: 'failure'}, "*"); + } +</script> +</body> +</html> diff --git a/testing/web-platform/tests/shared-storage/resources/verify-storage-entries-module.js b/testing/web-platform/tests/shared-storage/resources/verify-storage-entries-module.js new file mode 100644 index 0000000000..ee0c636cd1 --- /dev/null +++ b/testing/web-platform/tests/shared-storage/resources/verify-storage-entries-module.js @@ -0,0 +1,26 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class SetKey0Operation { + async run(data) { + sharedStorage.set('key0-set-from-worklet', 'value0'); + } +} + +class VerifyStorageEntriesURLSelectionOperation { + async run(urls, data) { + if (await sharedStorage.get('key0-set-from-worklet') === 'value0' && + await sharedStorage.get('key0-set-from-document') === 'value0') { + return 1; + } + + return -1; + } +} + +register('set-key0-operation', SetKey0Operation); + +register( + 'verify-storage-entries-url-selection-operation', + VerifyStorageEntriesURLSelectionOperation); |