From 7f71421e614c3ade38f19ef4036fac563a84ca96 Mon Sep 17 00:00:00 2001 From: Uzlopak <5059100+Uzlopak@users.noreply.github.com> Date: Fri, 15 Aug 2025 00:21:44 +0000 Subject: [PATCH] chore: update WPT --- test/fixtures/wpt/common/get-host-info.sub.js | 2 + .../fixtures/wpt/eventsource/WEB_FEATURES.yml | 3 + ...versized-payload.tentative.https.window.js | 4 +- .../max-payload.tentative.https.window.js | 19 +- ...versized-payload.tentative.https.window.js | 20 +- .../fetch-later/resources/fetch-later.html | 6 - .../wpt/fetch/local-network-access/README.md | 4 +- .../iframe.tentative.https.window.js | 199 ++++++++++++++++++ .../navigate.tentative.https.window.js | 151 +++++++++++++ .../navigate.tentative.window.js | 154 ++++++++++++++ .../resources/iframer.html | 35 +++ .../resources/navigate.html | 9 + .../resources/openee.html | 7 + .../resources/support.sub.js | 65 ++++++ .../{cookie-store.idl => cookiestore.idl} | 2 +- .../wpt/interfaces/origin.tentative.idl | 16 ++ test/fixtures/wpt/resources/testharness.js | 191 ++++++++++++++++- ...ket-quota-indexeddb.tentative.https.any.js | 8 +- ...e-details-indexeddb.https.tentative.any.js | 2 +- ...ge-details-caches.tentative.https.sub.html | 74 ++++++- ...details-indexeddb.tentative.https.sub.html | 73 ++++++- ...s-service-workers.tentative.https.sub.html | 74 +++++++ ...ate-usage-details-caches-helper-frame.html | 25 ++- ...-usage-details-indexeddb-helper-frame.html | 23 ++ ...-details-service-workers-helper-frame.html | 23 ++ test/fixtures/wpt/websockets/WEB_FEATURES.yml | 3 + test/fixtures/wpt/xhr/WEB_FEATURES.yml | 3 + 27 files changed, 1151 insertions(+), 44 deletions(-) create mode 100644 test/fixtures/wpt/eventsource/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/fetch/local-network-access/iframe.tentative.https.window.js create mode 100644 test/fixtures/wpt/fetch/local-network-access/navigate.tentative.https.window.js create mode 100644 test/fixtures/wpt/fetch/local-network-access/navigate.tentative.window.js create mode 100644 test/fixtures/wpt/fetch/local-network-access/resources/iframer.html create mode 100644 test/fixtures/wpt/fetch/local-network-access/resources/navigate.html create mode 100644 test/fixtures/wpt/fetch/local-network-access/resources/openee.html rename test/fixtures/wpt/interfaces/{cookie-store.idl => cookiestore.idl} (97%) create mode 100644 test/fixtures/wpt/interfaces/origin.tentative.idl create mode 100644 test/fixtures/wpt/websockets/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/xhr/WEB_FEATURES.yml diff --git a/test/fixtures/wpt/common/get-host-info.sub.js b/test/fixtures/wpt/common/get-host-info.sub.js index f680cd8b199..42369c7370b 100644 --- a/test/fixtures/wpt/common/get-host-info.sub.js +++ b/test/fixtures/wpt/common/get-host-info.sub.js @@ -20,6 +20,7 @@ function get_host_info() { var REMOTE_HOST = (ORIGINAL_HOST === 'localhost') ? '127.0.0.1' : ('www1.' + ORIGINAL_HOST); var OTHER_HOST = '{{domains[www2]}}'; var NOTSAMESITE_HOST = (ORIGINAL_HOST === 'localhost') ? '127.0.0.1' : ('{{hosts[alt][]}}'); + var OTHER_NOTSAMESITE_HOST = '{{hosts[alt][www2]}}'; return { HTTP_PORT: HTTP_PORT, @@ -45,6 +46,7 @@ function get_host_info() { HTTPS_REMOTE_ORIGIN: 'https://' + REMOTE_HOST + HTTPS_PORT_ELIDED, HTTPS_REMOTE_ORIGIN_WITH_CREDS: 'https://foo:bar@' + REMOTE_HOST + HTTPS_PORT_ELIDED, HTTPS_NOTSAMESITE_ORIGIN: 'https://' + NOTSAMESITE_HOST + HTTPS_PORT_ELIDED, + HTTPS_OTHER_NOTSAMESITE_ORIGIN: 'https://' + OTHER_NOTSAMESITE_HOST + HTTPS_PORT_ELIDED, UNAUTHENTICATED_ORIGIN: 'http://' + OTHER_HOST + HTTP_PORT_ELIDED, AUTHENTICATED_ORIGIN: 'https://' + OTHER_HOST + HTTPS_PORT_ELIDED }; diff --git a/test/fixtures/wpt/eventsource/WEB_FEATURES.yml b/test/fixtures/wpt/eventsource/WEB_FEATURES.yml new file mode 100644 index 00000000000..3c883f82f76 --- /dev/null +++ b/test/fixtures/wpt/eventsource/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: server-sent-events + files: "**" diff --git a/test/fixtures/wpt/fetch/fetch-later/quota/accumulated-oversized-payload.tentative.https.window.js b/test/fixtures/wpt/fetch/fetch-later/quota/accumulated-oversized-payload.tentative.https.window.js index 6439b392e2a..143546ff723 100644 --- a/test/fixtures/wpt/fetch/fetch-later/quota/accumulated-oversized-payload.tentative.https.window.js +++ b/test/fixtures/wpt/fetch/fetch-later/quota/accumulated-oversized-payload.tentative.https.window.js @@ -37,7 +37,7 @@ test( // Makes the 2nd call (POST) to the same reporting origin that sends // max bytes, which should be rejected. - assert_throws_dom('QuotaExceededError', () => { + assert_throws_quotaexceedederror(() => { fetchLater(requestUrl, { method: 'POST', signal: controller.signal, @@ -45,7 +45,7 @@ test( // Required, as the size of referrer also take up quota. referrer: '', }); - }); + }, null, null); // Makes the 3rd call (GET) to the same reporting origin, where its // request size is len(requestUrl) + headers, which should be accepted. diff --git a/test/fixtures/wpt/fetch/fetch-later/quota/max-payload.tentative.https.window.js b/test/fixtures/wpt/fetch/fetch-later/quota/max-payload.tentative.https.window.js index 8457060797a..aa797f13557 100644 --- a/test/fixtures/wpt/fetch/fetch-later/quota/max-payload.tentative.https.window.js +++ b/test/fixtures/wpt/fetch/fetch-later/quota/max-payload.tentative.https.window.js @@ -36,13 +36,14 @@ promise_test(async _ => { test(_ => { const uuid = token(); const requestUrl = generateSetBeaconURL(uuid, {host: HTTPS_ORIGIN}); - assert_throws_dom( - 'QuotaExceededError', - () => fetchLater(requestUrl, { - activateAfter: 0, - method: 'POST', - body: generatePayload( - getRemainingQuota(QUOTA_PER_ORIGIN, requestUrl, headers) + 1, - dataType), - })); + + assert_throws_quotaexceedederror(() => { + fetchLater(requestUrl, { + activateAfter: 0, + method: 'POST', + body: generatePayload( + getRemainingQuota(QUOTA_PER_ORIGIN, requestUrl, headers) + 1, + dataType), + }); + }, null, null); }, `fetchLater() rejects max+1 payload in a POST request body of ${dataType}.`); diff --git a/test/fixtures/wpt/fetch/fetch-later/quota/oversized-payload.tentative.https.window.js b/test/fixtures/wpt/fetch/fetch-later/quota/oversized-payload.tentative.https.window.js index 0036d040d8e..740f17ea24a 100644 --- a/test/fixtures/wpt/fetch/fetch-later/quota/oversized-payload.tentative.https.window.js +++ b/test/fixtures/wpt/fetch/fetch-later/quota/oversized-payload.tentative.https.window.js @@ -9,16 +9,16 @@ const OVERSIZED_REQUEST_BODY_SIZE = QUOTA_PER_ORIGIN + 1; for (const dataType in BeaconDataType) { // Test making a POST request with oversized payload, which should be rejected // by fetchLater API. - test( - () => assert_throws_dom( - 'QuotaExceededError', - () => fetchLater('/', { - activateAfter: 0, - method: 'POST', - body: makeBeaconData( - generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType), - })), - `fetchLater() does not accept payload[size=${ + test(() => { + assert_throws_quotaexceedederror(() => { + fetchLater('/', { + activateAfter: 0, + method: 'POST', + body: makeBeaconData( + generatePayload(OVERSIZED_REQUEST_BODY_SIZE), dataType), + }); + }, null, null); + }, `fetchLater() does not accept payload[size=${ OVERSIZED_REQUEST_BODY_SIZE}] exceeding per-origin quota in a POST request body of ${ dataType}.`); } diff --git a/test/fixtures/wpt/fetch/fetch-later/resources/fetch-later.html b/test/fixtures/wpt/fetch/fetch-later/resources/fetch-later.html index 955f815d940..b295be116c7 100644 --- a/test/fixtures/wpt/fetch/fetch-later/resources/fetch-later.html +++ b/test/fixtures/wpt/fetch/fetch-later/resources/fetch-later.html @@ -30,12 +30,6 @@ fetchLater(TARGET_URL, REQUEST_INIT); postMessageBack({type: FetchLaterIframeMessageType.DONE}); } catch (e) { - if (e.name == "QuotaExceededError" && - e instanceof DOMException) { - // PostMessage is unable to serialize the QuotExceededError object. - // Therefore do basic checks here and pass error name if successful. - e = {name: e.name}; - } postMessageBack({type: FetchLaterIframeMessageType.ERROR, error: e}); } diff --git a/test/fixtures/wpt/fetch/local-network-access/README.md b/test/fixtures/wpt/fetch/local-network-access/README.md index 95066cdcd0b..d4945a0bfb2 100644 --- a/test/fixtures/wpt/fetch/local-network-access/README.md +++ b/test/fixtures/wpt/fetch/local-network-access/README.md @@ -5,7 +5,7 @@ the Fetch specification. See also: -* [Explainer](https://github.com/explainers-by-googlers/local-network-access) +* [Spec](https://wicg.github.io/local-network-access/) Local Network Access replaced [Private Network -Access](https://wicg.github.io/local-network-access/). +Access](https://wicg.github.io/private-network-access/). diff --git a/test/fixtures/wpt/fetch/local-network-access/iframe.tentative.https.window.js b/test/fixtures/wpt/fetch/local-network-access/iframe.tentative.https.window.js new file mode 100644 index 00000000000..d2b532aeae4 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/iframe.tentative.https.window.js @@ -0,0 +1,199 @@ +// META: script=/common/subset-tests-by-key.js +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=resources/support.sub.js +// META: timeout=long +// META: variant=?include=from-loopback +// META: variant=?include=from-local +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public +// +// Spec: https://wicg.github.io/local-network-access/#integration-fetch +// +// These tests verify that secure contexts can navigate iframes to less-public +// address spaces iff the initiating document has been granted the LNA +// permission. +// +// This file covers only those tests that must execute in a secure context. + +setup(() => { + assert_true(window.isSecureContext); +}); + +// Source: secure loopback context. +// +// All iframe navigations unaffected by Local Network Access. + +subsetTestByKey( + 'from-loopback', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to loopback: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to local: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to public: no permission required.'); + +// Source: local secure context. +// +// All iframe navigations unaffected by Local Network Access. + +// Requests from the `local` address space to the `loopback` address space +// are not yet restricted by LNA. +subsetTestByKey( + 'from-local', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'local to loopback: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'local to local: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => iframeTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'local to public: no permission required.'); + + +// Generates tests of permission behavior for a single (source, target) pair. +// +// Scenarios: +// +// - parent (source) navigates child (target): +// - parent has been denied the LNA permission (failure) +// - parent has been granted the LNA permission (success) +// +function makePermissionTests({ + key, + sourceName, + sourceServer, + sourceTreatAsPublic, + targetName, + targetServer, +}) { + const prefix = `${sourceName} to ${targetName}: `; + + const source = { + server: sourceServer, + treatAsPublic: sourceTreatAsPublic, + }; + + promise_test( + t => iframeTest(t, { + source, + target: { + server: targetServer, + }, + expected: NavigationTestResult.FAILURE, + permission: 'denied', + }), + prefix + 'permission denied.'); + + promise_test( + t => iframeTest(t, { + source, + target: { + server: targetServer, + }, + expected: NavigationTestResult.SUCCESS, + permission: 'granted', + }), + prefix + 'success.'); +} + + +// Source: public secure context. +// +// iframe navigations to the loopback and local address spaces require the LNA +// permission. + +subsetTestByKey('from-public', makePermissionTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: 'public', + targetServer: Server.HTTPS_LOOPBACK, + targetName: 'loopback', +}); + +subsetTestByKey('from-public', makePermissionTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: 'public', + targetServer: Server.HTTPS_LOCAL, + targetName: 'local', +}); + +subsetTestByKey( + 'from-public', promise_test, t => iframeTest(t, { + source: Server.HTTPS_PUBLIC, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public: no permission required.'); + +// The following tests verify that `CSP: treat-as-public-address` makes +// documents behave as if they had been served from a public IP address. + +subsetTestByKey('from-treat-as-public', makePermissionTests, { + sourceServer: Server.HTTPS_LOOPBACK, + sourceTreatAsPublic: true, + sourceName: 'treat-as-public-address', + targetServer: Server.OTHER_HTTPS_LOOPBACK, + targetName: 'loopback', +}); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => iframeTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to local (same-origin): no permission required.'); + +subsetTestByKey('from-treat-as-public', makePermissionTests, { + sourceServer: Server.HTTPS_LOOPBACK, + sourceTreatAsPublic: true, + sourceName: 'treat-as-public-address', + targetServer: Server.HTTPS_LOCAL, + targetName: 'local', +}); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => iframeTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to public: no permission required.'); diff --git a/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.https.window.js b/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.https.window.js new file mode 100644 index 00000000000..abdefb8cd7c --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.https.window.js @@ -0,0 +1,151 @@ +// META: script=/common/subset-tests-by-key.js +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// META: timeout=long +// META: variant=?include=from-loopback +// META: variant=?include=from-local +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public +// +// These tests verify that secure contexts can make top-level navigations +// to less-public address spaces. These are not restricted under LNA. + +setup(() => { + assert_true(window.isSecureContext); +}); + +// Source: secure loopback context. +// +// All top-level navigations unaffected by Local Network Access. + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOOPBACK, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to loopback: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOOPBACK, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to local: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOOPBACK, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to public: no preflight required.'); + +// Source: secure local context. +// +// All top-level navigations unaffected by Local Network Access. + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOCAL, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'local to loopback: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOCAL, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'local to local: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTPS_LOCAL, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'local to public: no permission required.'); + +// Source: secure public context. +// +// All top-level navigations unaffected by Local Network Access + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTPS_PUBLIC, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'public to loopback: no permission required.'); + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTPS_PUBLIC, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'public to local: no permission required.'); + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTPS_PUBLIC, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public: no permission required.'); + +// The following tests verify that `CSP: treat-as-public-address` makes +// documents behave as if they had been served from a public IP address. + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.OTHER_HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to loopback: no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTPS_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to loopback (same-origin): no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTPS_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to local: no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTPS_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTPS_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to public: no permission required.'); diff --git a/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.window.js b/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.window.js new file mode 100644 index 00000000000..4186bad5899 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/navigate.tentative.window.js @@ -0,0 +1,154 @@ +// META: script=/common/subset-tests-by-key.js +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// META: timeout=long +// META: variant=?include=from-loopback +// META: variant=?include=from-local +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public +// +// These tests verify that nonsecure contexts can make top-level navigations +// to less-public address spaces. These are not restricted under LNA. +// +// This file covers only those tests that must execute in a non secure context. +// Other tests are defined in: navigate.tentative.https.window.js + +setup(() => { + assert_false(window.isSecureContext); +}); + +// Source: nonsecure loopback context. +// +// All top-level navigations unaffected by Local Network Access. + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTP_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to loopback: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTP_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to local: no permission required.'); + +subsetTestByKey( + 'from-loopback', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOOPBACK, + target: Server.HTTP_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'loopback to public: no preflight required.'); + +// Source: secure local context. +// +// All top-level navigations unaffected by Local Network Access. + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTP_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'local to loopback: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTP_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'local to local: no permission required.'); + +subsetTestByKey( + 'from-local', promise_test, t => navigateTest(t, { + source: Server.HTTP_LOCAL, + target: Server.HTTP_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'local to public: no permission required.'); + +// Source: secure public context. +// +// All top-level navigations unaffected by Local Network Access + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTP_PUBLIC, + target: Server.HTTP_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'public to loopback: no permission required.'); + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTP_PUBLIC, + target: Server.HTTP_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'public to local: no permission required.'); + +subsetTestByKey( + 'from-public', promise_test, t => navigateTest(t, { + source: Server.HTTP_PUBLIC, + target: Server.HTTP_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public: no permission required.'); + +// The following tests verify that `CSP: treat-as-public-address` makes +// documents behave as if they had been served from a public IP address. + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTP_LOOPBACK, + treatAsPublic: true, + }, + target: Server.OTHER_HTTP_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to loopback: no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTP_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTP_LOOPBACK, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to loopback (same-origin): no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTP_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTP_LOCAL, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to local: no permission required.'); + +subsetTestByKey( + 'from-treat-as-public', promise_test, + t => navigateTest(t, { + source: { + server: Server.HTTP_LOOPBACK, + treatAsPublic: true, + }, + target: Server.HTTP_PUBLIC, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to public: no permission required.'); diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/iframer.html b/test/fixtures/wpt/fetch/local-network-access/resources/iframer.html new file mode 100644 index 00000000000..ede09bc6b0a --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/iframer.html @@ -0,0 +1,35 @@ + + +Iframer + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/navigate.html b/test/fixtures/wpt/fetch/local-network-access/resources/navigate.html new file mode 100644 index 00000000000..5796cb41be5 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/navigate.html @@ -0,0 +1,9 @@ + + +Navigate + + \ No newline at end of file diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/openee.html b/test/fixtures/wpt/fetch/local-network-access/resources/openee.html new file mode 100644 index 00000000000..2e06a8d0328 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/openee.html @@ -0,0 +1,7 @@ + + +Openee + \ No newline at end of file diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js b/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js index 09e234c5f24..157c2b40066 100644 --- a/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js +++ b/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js @@ -5,6 +5,7 @@ const SERVER_PORTS = { "http": { "loopback": {{ports[http][0]}}, + "other-loopback": {{ports[http][1]}}, "local": {{ports[http-local][0]}}, "public": {{ports[http-public][0]}}, }, @@ -57,6 +58,7 @@ class Server { } static HTTP_LOOPBACK = Server.get("http", "loopback"); + static OTHER_HTTP_LOOPBACK = Server.get("http", "other-loopback"); static HTTP_LOCAL = Server.get("http", "local"); static HTTP_PUBLIC = Server.get("http", "public"); static HTTPS_LOOPBACK = Server.get("https", "loopback"); @@ -187,3 +189,66 @@ function checkTestResult(actual, expected) { assert_equals(type, expected.type, "response type mismatch"); } } + +// Registers an event listener that will resolve this promise when this +// window receives a message posted to it. +function futureMessage(options) { + return new Promise(resolve => { + window.addEventListener('message', (e) => { + resolve(e.data); + }); + }); +}; + +const NavigationTestResult = { + SUCCESS: 'loaded', + FAILURE: 'timeout', +}; + +async function iframeTest( + t, {source, target, expected, permission = 'denied'}) { + const targetUrl = + resolveUrl('resources/openee.html', sourceResolveOptions(target)); + + const sourceUrl = + resolveUrl('resources/iframer.html', sourceResolveOptions(source)); + sourceUrl.searchParams.set('permission', permission); + sourceUrl.searchParams.set('url', targetUrl); + + const popup = window.open(sourceUrl); + t.add_cleanup(() => popup.close()); + + // The child frame posts a message iff it loads successfully. + // There exists no interoperable way to check whether an iframe failed to + // load, so we use a timeout. + // See: https://github.com/whatwg/html/issues/125 + const result = await Promise.race([ + futureMessage().then((data) => data.message), + new Promise((resolve) => { + t.step_timeout(() => resolve('timeout'), 2000 /* ms */); + }), + ]); + + assert_equals(result, expected); +} + +async function navigateTest(t, {source, target, expected}) { + const targetUrl = + resolveUrl('resources/openee.html', sourceResolveOptions(target)); + + const sourceUrl = + resolveUrl('resources/navigate.html', sourceResolveOptions(source)); + sourceUrl.searchParams.set('url', targetUrl); + + const popup = window.open(sourceUrl); + t.add_cleanup(() => popup.close()); + + const result = await Promise.race([ + futureMessage().then((data) => data.message), + new Promise((resolve) => { + t.step_timeout(() => resolve('timeout'), 2000 /* ms */); + }), + ]); + + assert_equals(result, expected); +} diff --git a/test/fixtures/wpt/interfaces/cookie-store.idl b/test/fixtures/wpt/interfaces/cookiestore.idl similarity index 97% rename from test/fixtures/wpt/interfaces/cookie-store.idl rename to test/fixtures/wpt/interfaces/cookiestore.idl index 8a000cac0b2..ecb277756cc 100644 --- a/test/fixtures/wpt/interfaces/cookie-store.idl +++ b/test/fixtures/wpt/interfaces/cookiestore.idl @@ -1,7 +1,7 @@ // GENERATED CONTENT - DO NOT EDIT // Content was automatically extracted by Reffy into webref // (https://github.com/w3c/webref) -// Source: Cookie Store API (https://wicg.github.io/cookie-store/) +// Source: Cookie Store API (https://cookiestore.spec.whatwg.org/) [Exposed=(ServiceWorker,Window), SecureContext] diff --git a/test/fixtures/wpt/interfaces/origin.tentative.idl b/test/fixtures/wpt/interfaces/origin.tentative.idl new file mode 100644 index 00000000000..c2f91171339 --- /dev/null +++ b/test/fixtures/wpt/interfaces/origin.tentative.idl @@ -0,0 +1,16 @@ +// https://github.com/mikewest/origin-api/ +[Exposed=*] +interface Origin { + constructor(); + constructor(USVString serializedOrigin); + + static Origin? parse(USVString serializedOrigin); + static Origin? fromURL(USVString serializedURL); + + stringifier USVString toJSON(); + + readonly attribute boolean opaque; + + boolean isSameOrigin(Origin other); + boolean isSameSite(Origin other); +}; diff --git a/test/fixtures/wpt/resources/testharness.js b/test/fixtures/wpt/resources/testharness.js index b5847ed406a..f495b62458b 100644 --- a/test/fixtures/wpt/resources/testharness.js +++ b/test/fixtures/wpt/resources/testharness.js @@ -867,6 +867,54 @@ }); } +/** + * Assert that a `Promise` is rejected with a `QuotaExceededError` with the + * expected values. + * + * For the remaining arguments, there are two ways of calling + * `promise_rejects_quotaexceedederror`: + * + * 1) If the `QuotaExceededError` is expected to come from the + * current global, the second argument should be the promise + * expected to reject, the third and a fourth the expected + * `requested` and `quota` property values, and the fifth, + * optional, argument is the assertion description. + * + * 2) If the `QuotaExceededError` is expected to come from some + * other global, the second argument should be the + * `QuotaExceededError` constructor from that global, the third + * argument should be the promise expected to reject, the fourth + * and fifth the expected `requested` and `quota` property + * values, and the sixth, optional, argument is the assertion + * description. + * + */ + function promise_rejects_quotaexceedederror(test, promiseOrConstructor, requestedOrPromise, quotaOrRequested, descriptionOrQuota, maybeDescription) + { + let constructor, promise, requested, quota, description; + if (typeof promiseOrConstructor === "function" && + promiseOrConstructor.name === "QuotaExceededError") { + constructor = promiseOrConstructor; + promise = requestedOrPromise; + requested = quotaOrRequested; + quota = descriptionOrQuota; + description = maybeDescription; + } else { + constructor = self.QuotaExceededError; + promise = promiseOrConstructor; + requested = requestedOrPromise; + quota = quotaOrRequested; + description = descriptionOrQuota; + assert(maybeDescription === undefined, + "Too many args passed to no-constructor version of promise_rejects_quotaexceedederror"); + } + return bring_promise_to_current_realm(promise) + .then(test.unreached_func("Should have rejected: " + description)) + .catch(function(e) { + assert_throws_quotaexceedederror_impl(function() { throw e; }, requested, quota, description, "promise_rejects_quotaexceedederror", constructor); + }); + } + /** * Assert that a Promise is rejected with the provided value. * @@ -1240,6 +1288,7 @@ expose(promise_test, 'promise_test'); expose(promise_rejects_js, 'promise_rejects_js'); expose(promise_rejects_dom, 'promise_rejects_dom'); + expose(promise_rejects_quotaexceedederror, 'promise_rejects_quotaexceedederror'); expose(promise_rejects_exactly, 'promise_rejects_exactly'); expose(generate_tests, 'generate_tests'); expose(setup, 'setup'); @@ -2296,7 +2345,6 @@ NETWORK_ERR: 'NetworkError', ABORT_ERR: 'AbortError', URL_MISMATCH_ERR: 'URLMismatchError', - QUOTA_EXCEEDED_ERR: 'QuotaExceededError', TIMEOUT_ERR: 'TimeoutError', INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError', DATA_CLONE_ERR: 'DataCloneError' @@ -2321,7 +2369,6 @@ NetworkError: 19, AbortError: 20, URLMismatchError: 21, - QuotaExceededError: 22, TimeoutError: 23, InvalidNodeTypeError: 24, DataCloneError: 25, @@ -2352,12 +2399,19 @@ if (typeof type === "number") { if (type === 0) { throw new AssertionError('Test bug: ambiguous DOMException code 0 passed to assert_throws_dom()'); - } else if (!(type in code_name_map)) { + } + if (type === 22) { + throw new AssertionError('Test bug: QuotaExceededError needs to be tested for using assert_throws_quotaexceedederror()'); + } + if (!(type in code_name_map)) { throw new AssertionError('Test bug: unrecognized DOMException code "' + type + '" passed to assert_throws_dom()'); } name = code_name_map[type]; required_props.code = type; } else if (typeof type === "string") { + if (name === "QuotaExceededError") { + throw new AssertionError('Test bug: QuotaExceededError needs to be tested for using assert_throws_quotaexceedederror()'); + } name = type in codename_name_map ? codename_name_map[type] : type; if (!(name in name_code_map)) { throw new AssertionError('Test bug: unrecognized DOMException code name or name "' + type + '" passed to assert_throws_dom()'); @@ -2392,6 +2446,137 @@ } } + /** + * Assert a `QuotaExceededError` with the expected values is thrown. + * + * There are two ways of calling `assert_throws_quotaexceedederror`: + * + * 1) If the `QuotaExceededError` is expected to come from the + * current global, the first argument should be the function + * expected to throw, the second and a third the expected + * `requested` and `quota` property values, and the fourth, + * optional, argument is the assertion description. + * + * 2) If the `QuotaExceededError` is expected to come from some + * other global, the first argument should be the + * `QuotaExceededError` constructor from that global, the second + * argument should be the function expected to throw, the third + * and fourth the expected `requested` and `quota` property + * values, and the fifth, optional, argument is the assertion + * description. + * + * For the `requested` and `quota` values, instead of `null` or a + * number, the caller can provide a function which determines + * whether the value is acceptable by returning a boolean. + * + */ + function assert_throws_quotaexceedederror(funcOrConstructor, requestedOrFunc, quotaOrRequested, descriptionOrQuota, maybeDescription) + { + let constructor, func, requested, quota, description; + if (funcOrConstructor.name === "QuotaExceededError") { + constructor = funcOrConstructor; + func = requestedOrFunc; + requested = quotaOrRequested; + quota = descriptionOrQuota; + description = maybeDescription; + } else { + constructor = self.QuotaExceededError; + func = funcOrConstructor; + requested = requestedOrFunc; + quota = quotaOrRequested; + description = descriptionOrQuota; + assert(maybeDescription === undefined, + "Too many args passed to no-constructor version of assert_throws_quotaexceedederror"); + } + assert_throws_quotaexceedederror_impl(func, requested, quota, description, "assert_throws_quotaexceedederror", constructor); + } + expose_assert(assert_throws_quotaexceedederror, "assert_throws_quotaexceedederror"); + + /** + * Similar to `assert_throws_quotaexceedederror` but allows + * specifying the assertion type + * (`"assert_throws_quotaexceedederror"` or + * `"promise_rejects_quotaexceedederror"`, in practice). The + * `constructor` argument must be the `QuotaExceededError` + * constructor from the global we expect the exception to come from. + */ + function assert_throws_quotaexceedederror_impl(func, requested, quota, description, assertion_type, constructor) + { + try { + func.call(this); + assert(false, assertion_type, description, "${func} did not throw", + {func}); + } catch (e) { + if (e instanceof AssertionError) { + throw e; + } + + // Basic sanity-checks on the thrown exception. + assert(typeof e === "object", + assertion_type, description, + "${func} threw ${e} with type ${type}, not an object", + {func, e, type:typeof e}); + + assert(e !== null, + assertion_type, description, + "${func} threw null, not an object", + {func}); + + // Sanity-check our requested and quota. + assert(requested === null || + typeof requested === "number" || + typeof requested === "function", + assertion_type, description, + "${requested} is not null, a number, or a function", + {requested}); + assert(quota === null || + typeof quota === "number" || + typeof quota === "function", + assertion_type, description, + "${quota} is not null or a number", + {quota}); + + const required_props = { + code: 22, + name: "QuotaExceededError" + }; + if (typeof requested !== "function") { + required_props.requested = requested; + } + if (typeof quota !== "function") { + required_props.quota = quota; + } + + for (const [prop, expected] of Object.entries(required_props)) { + assert(prop in e && e[prop] == expected, + assertion_type, description, + "${func} threw ${e} that is not a correct QuotaExceededError: property ${prop} is equal to ${actual}, expected ${expected}", + {func, e, prop, actual:e[prop], expected}); + } + + if (typeof requested === "function") { + assert(requested(e.requested), + assertion_type, description, + "${func} threw ${e} that is not a correct QuotaExceededError: requested value ${requested} did not pass the requested predicate", + {func, e, requested}); + } + if (typeof quota === "function") { + assert(quota(e.quota), + assertion_type, description, + "${func} threw ${e} that is not a correct QuotaExceededError: quota value ${quota} did not pass the quota predicate", + {func, e, quota}); + } + + // Check that the exception is from the right global. This check is last + // so more specific, and more informative, checks on the properties can + // happen in case a totally incorrect exception is thrown. + assert(e.constructor === constructor, + assertion_type, description, + "${func} threw an exception from the wrong global", + {func}); + } + } + /** * Assert the provided value is thrown. * diff --git a/test/fixtures/wpt/storage/buckets/bucket-quota-indexeddb.tentative.https.any.js b/test/fixtures/wpt/storage/buckets/bucket-quota-indexeddb.tentative.https.any.js index ee9202777e7..9dc345579d3 100644 --- a/test/fixtures/wpt/storage/buckets/bucket-quota-indexeddb.tentative.https.any.js +++ b/test/fixtures/wpt/storage/buckets/bucket-quota-indexeddb.tentative.https.any.js @@ -17,6 +17,9 @@ promise_test(async t => { await indexedDbOpenRequest(t, bucket.indexedDB, dbname, (db_to_upgrade) => { db_to_upgrade.createObjectStore(objectStoreName); }); + t.add_cleanup(() => { + db.close(); + }); const txn = db.transaction(objectStoreName, 'readwrite'); const buffer = new ArrayBuffer(arraySize); @@ -34,8 +37,5 @@ promise_test(async t => { type: 'binary/random' }), 2); - await promise_rejects_dom( - t, 'QuotaExceededError', transactionPromise(txn)); - - db.close(); + await promise_rejects_quotaexceedederror(t, transactionPromise(txn), null, null); }, 'IDB respects bucket quota'); diff --git a/test/fixtures/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js b/test/fixtures/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js index 551cede9c6d..d89a78e0956 100644 --- a/test/fixtures/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js +++ b/test/fixtures/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js @@ -16,7 +16,6 @@ promise_test(async t => { const objectStoreName = 'store'; const dbname = self.location.pathname; - await indexedDB.deleteDatabase(dbname); let usageAfterWrite, usageBeforeWrite; // TODO(crbug.com/906867): Refactor this test to better deal with db/log // compaction flakiness @@ -32,6 +31,7 @@ promise_test(async t => { // builds that fail); all it takes is one iteration without compaction for // this to pass. for (let i = 0; i < 10; i++) { + indexedDB.deleteDatabase(dbname); const db = await createDB(dbname, objectStoreName, t); let estimate = await navigator.storage.estimate(); diff --git a/test/fixtures/wpt/storage/partitioned-estimate-usage-details-caches.tentative.https.sub.html b/test/fixtures/wpt/storage/partitioned-estimate-usage-details-caches.tentative.https.sub.html index dc2af7c213c..4954a996f22 100644 --- a/test/fixtures/wpt/storage/partitioned-estimate-usage-details-caches.tentative.https.sub.html +++ b/test/fixtures/wpt/storage/partitioned-estimate-usage-details-caches.tentative.https.sub.html @@ -7,9 +7,12 @@ diff --git a/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-indexeddb-helper-frame.html b/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-indexeddb-helper-frame.html index fd2cfb669bd..10f5b956b8b 100644 --- a/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-indexeddb-helper-frame.html +++ b/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-indexeddb-helper-frame.html @@ -2,12 +2,22 @@ Helper frame + + diff --git a/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-service-workers-helper-frame.html b/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-service-workers-helper-frame.html index 25d7554868f..0bf7bebf2bb 100644 --- a/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-service-workers-helper-frame.html +++ b/test/fixtures/wpt/storage/resources/partitioned-estimate-usage-details-service-workers-helper-frame.html @@ -2,14 +2,24 @@ Helper frame + + diff --git a/test/fixtures/wpt/websockets/WEB_FEATURES.yml b/test/fixtures/wpt/websockets/WEB_FEATURES.yml new file mode 100644 index 00000000000..6ca9ddf4301 --- /dev/null +++ b/test/fixtures/wpt/websockets/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: websockets + files: "**" diff --git a/test/fixtures/wpt/xhr/WEB_FEATURES.yml b/test/fixtures/wpt/xhr/WEB_FEATURES.yml new file mode 100644 index 00000000000..07f9d430181 --- /dev/null +++ b/test/fixtures/wpt/xhr/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: xhr + files: "**"