From 22543870fdc49a099ebf9f7f6874f37660fc970f Mon Sep 17 00:00:00 2001 From: Khafra Date: Tue, 14 May 2024 13:02:44 -0400 Subject: [PATCH 1/5] add bodymixin bytes --- CONTRIBUTING.md | 1 + lib/web/fetch/body.js | 9 + test/wpt/runner/worker.mjs | 3 + test/wpt/status/fetch.status.json | 62 ++- test/wpt/tests/.azure-pipelines.yml | 110 ++-- test/wpt/tests/.taskcluster.yml | 4 +- .../FileAPI/blob/Blob-constructor.any.js | 3 +- test/wpt/tests/common/media.js | 4 - .../security-features/tools/generate.py | 2 +- .../security-features/tools/spec_validator.py | 6 +- ...ventsource-request-cancellation.window.js} | 0 ...indow.js => request-credentials.window.js} | 0 ...y.window.js => request-redirect.window.js} | 0 .../tests/fetch/api/basic/keepalive.any.js | 3 +- .../fetch/api/basic/request-headers.any.js | 1 + .../fetch/api/basic/request-upload.any.js | 4 + .../fetch/api/basic/request-upload.h2.any.js | 23 + test/wpt/tests/fetch/api/body/formdata.any.js | 11 + .../fetch/api/cors/cors-keepalive.any.js | 2 - .../aborted-fetch-response.https.html | 11 + .../fetch/api/crashtests/huge-fetch.any.js | 16 + .../authentication-redirection.any.js | 2 +- .../api/redirect/redirect-keepalive.any.js | 3 +- .../redirect/redirect-keepalive.https.any.js | 2 - .../redirect-referrer-override.any.js | 2 +- .../destination/resources/dummy_video.ogv | Bin 94372 -> 0 bytes .../fetch/api/request/request-bad-port.any.js | 170 +++--- ...uest-constructor-init-body-override.any.js | 21 + .../api/request/request-consume-empty.any.js | 22 +- .../fetch/api/request/request-consume.any.js | 29 +- .../resources/dump-authorization-header.py | 5 + .../fetch/api/resources/huge-response.py | 22 + .../response-arraybuffer-realm.window.js | 23 + .../fetch/api/response/response-clone.any.js | 1 + .../response/response-consume-empty.any.js | 23 +- ...clear-site-data-cache.tentative.https.html | 27 + ...ear-site-data-cookies.tentative.https.html | 27 + ...ear-site-data-storage.tentative.https.html | 27 + ...tionary-decompression.tentative.https.html | 58 ++ ...tch-with-link-element.tentative.https.html | 71 +++ ...etch-with-link-header.tentative.https.html | 52 ++ ...ctionary-registration.tentative.https.html | 61 ++ .../resources/clear-site-data.py | 4 + .../resources/compressed-data.py | 28 + .../resources/compression-dictionary-util.js | 120 ++++ .../resources/echo-headers.py | 10 + .../resources/empty.html | 1 + .../resources/register-dictionary.py | 37 ++ .../br/bad-br-body.https.any.js | 12 + .../br/big-br-body.https.any.js | 55 ++ .../content-encoding/br/br-body.https.any.js | 15 + .../br/resources/bad-br-body.py | 3 + .../content-encoding/br/resources/big.text.br | Bin 0 -> 49 bytes .../br/resources/big.text.br.headers | 3 + .../br/resources/foo.octetstream.br | Bin 0 -> 15 bytes .../br/resources/foo.octetstream.br.headers | 2 + .../content-encoding/br/resources/foo.text.br | Bin 0 -> 15 bytes .../br/resources/foo.text.br.headers | 2 + .../{ => gzip}/bad-gzip-body.any.js | 0 .../{ => gzip}/big-gzip-body.https.any.js | 0 .../{ => gzip}/gzip-body.any.js | 0 .../{ => gzip}/resources/bad-gzip-body.py | 0 .../{ => gzip}/resources/big.text.gz | Bin .../{ => gzip}/resources/big.text.gz.headers | 0 .../{ => gzip}/resources/foo.octetstream.gz | Bin .../resources/foo.octetstream.gz.headers | 0 .../{ => gzip}/resources/foo.text.gz | Bin .../{ => gzip}/resources/foo.text.gz.headers | 0 .../zstd/bad-zstd-body.https.any.js | 22 + ...ig-window-zstd-body.tentative.https.any.js | 9 + .../zstd/big-zstd-body.https.any.js | 55 ++ .../zstd/resources/bad-zstd-body.py | 3 + .../zstd/resources/big.text.zst | Bin 0 -> 2509 bytes .../zstd/resources/big.text.zst.headers | 3 + .../zstd/resources/big.window.zst | Bin 0 -> 599 bytes .../zstd/resources/big.window.zst.headers | 2 + .../zstd/resources/foo.octetstream.zst | Bin 0 -> 25 bytes .../resources/foo.octetstream.zst.headers | 2 + .../zstd/resources/foo.text.zst | Bin 0 -> 25 bytes .../zstd/resources/foo.text.zst.headers | 2 + .../zstd/zstd-body.https.any.js | 15 + .../activate-after.tentative.https.window.js | 4 +- .../basic.tentative.https.window.js | 3 - .../iframe.tentative.https.window.js | 4 +- .../new-window.tentative.https.window.js | 4 +- .../fetch/fetch-later/non-secure.window.js | 3 - .../csp-allowed.tentative.https.window.js | 4 +- .../csp-blocked.tentative.https.window.js | 4 +- ...irect-to-blocked.tentative.https.window.js | 4 +- .../quota.tentative.https.window.js | 4 +- .../resources/fetch-later-helper.js | 206 +++++++ .../fetch/fetch-later/resources/get_beacon.py | 30 + .../fetch/fetch-later/resources/set_beacon.py | 83 +++ ...-background-sync.tentative.https.window.js | 128 +++++ ...nd-on-deactivate.tentative.https.window.js | 4 +- ...send-after-abort.tentative.https.window.js | 4 +- ...h-activate-after.tentative.https.window.js | 4 +- .../send-multiple.tentative.https.window.js | 4 +- .../resources-with-0x00-in-header.window.js | 6 + .../tests/fetch/http-cache/freshness.any.js | 28 + .../wpt/tests/fetch/metadata/WEB_FEATURES.yml | 3 + .../appcache-manifest.https.sub.html | 341 ------------ .../worker-dedicated-constructor.sub.html | 120 ---- .../metadata/tools/fetch-metadata.conf.yml | 76 ++- .../appcache-manifest.sub.https.html | 63 --- .../anchor.tentative.https.window.js | 191 +++++++ .../anchor.tentative.window.js | 95 ++++ .../fetch.tentative.https.window.js | 2 +- ...ed-content-fetch.tentative.https.window.js | 1 + .../resources/anchor.html | 16 + .../iframed-no-preflight-received.html | 7 + .../resources/no-preflight-received.html | 6 + .../resources/open-to-existing-window.html | 12 + .../resources/openee.html | 8 + .../resources/opener.html | 4 +- .../resources/preflight.py | 21 +- .../resources/service-worker-fetch-all.js | 20 + .../resources/support.sub.js | 140 ++++- ...background-fetch.tentative.https.window.js | 1 + ...-treat-as-public.tentative.https.window.js | 101 ++++ ...r-fetch-document.tentative.https.window.js | 114 ++++ ...ice-worker-fetch.tentative.https.window.js | 117 ++-- ...ow-open-existing.tentative.https.window.js | 209 +++++++ .../window-open-existing.tentative.window.js | 95 ++++ .../window-open.tentative.https.window.js | 67 +-- .../window-open.tentative.window.js | 27 +- ...tigation-allowed-apis.tentative.https.html | 80 +++ ...kup-mitigation-data-url.tentative.sub.html | 0 .../dangling-markup-mitigation.tentative.html | 0 ...ing-markup-mitigation.tentative.https.html | 61 ++ .../fetch/security/dangling-markup/media.html | 27 + .../security/dangling-markup/option.html | 51 ++ .../dangling-markup/resources/empty.html | 1 + .../dangling-markup/resources/helper.js | 63 +++ .../dangling-markup/service-worker.js | 41 ++ .../security/dangling-markup/textarea.html | 34 ++ test/wpt/tests/interfaces/CSP.idl | 24 +- test/wpt/tests/interfaces/DOM-Parsing.idl | 16 - test/wpt/tests/interfaces/FedCM.idl | 18 +- .../wpt/tests/interfaces/anonymous-iframe.idl | 12 + .../wpt/tests/interfaces/compute-pressure.idl | 6 +- test/wpt/tests/interfaces/contact-picker.idl | 2 +- .../tests/interfaces/css-anchor-position.idl | 76 ++- test/wpt/tests/interfaces/css-animations.idl | 2 +- test/wpt/tests/interfaces/css-fonts.idl | 36 +- test/wpt/tests/interfaces/css-nesting.idl | 9 + .../interfaces/css-properties-values-api.idl | 8 +- .../tests/interfaces/css-scroll-snap-2.idl | 16 + test/wpt/tests/interfaces/css-transitions.idl | 2 +- .../interfaces/css-view-transitions-2.idl | 34 +- test/wpt/tests/interfaces/cssom-view.idl | 4 + test/wpt/tests/interfaces/cssom.idl | 24 +- .../interfaces/custom-state-pseudo-class.idl | 14 - .../tests/interfaces/device-attributes.idl | 13 + .../tests/interfaces/digital-identities.idl | 27 + .../document-picture-in-picture.idl | 1 + test/wpt/tests/interfaces/dom.idl | 15 +- test/wpt/tests/interfaces/edit-context.idl | 2 +- test/wpt/tests/interfaces/fenced-frame.idl | 4 + .../tests/interfaces/gamepad-extensions.idl | 38 +- test/wpt/tests/interfaces/gamepad.idl | 36 +- test/wpt/tests/interfaces/geolocation.idl | 2 + test/wpt/tests/interfaces/html.idl | 111 +++- .../interest-invokers.tentative.idl | 7 + .../tests/interfaces/invokers.tentative.idl | 4 +- .../interfaces/long-animation-frames.idl | 54 ++ test/wpt/tests/interfaces/longtasks.idl | 21 +- test/wpt/tests/interfaces/media-source.idl | 116 ++-- .../tests/interfaces/mediacapture-streams.idl | 10 - test/wpt/tests/interfaces/mediasession.idl | 33 +- .../tests/interfaces/navigation-timing.idl | 1 + .../tests/interfaces/observable.tentative.idl | 31 ++ .../tests/interfaces/orientation-event.idl | 2 +- test/wpt/tests/interfaces/paint-timing.idl | 2 +- test/wpt/tests/interfaces/permissions.idl | 2 +- test/wpt/tests/interfaces/sanitizer-api.idl | 57 +- test/wpt/tests/interfaces/serial.idl | 1 + test/wpt/tests/interfaces/service-workers.idl | 37 +- .../tests/interfaces/shape-detection-api.idl | 6 +- test/wpt/tests/interfaces/shared-storage.idl | 51 +- test/wpt/tests/interfaces/storage-buckets.idl | 4 +- .../tests/interfaces/text-detection-api.idl | 2 +- test/wpt/tests/interfaces/trust-token-api.idl | 2 +- test/wpt/tests/interfaces/trusted-types.idl | 21 +- test/wpt/tests/interfaces/turtledove.idl | 50 +- test/wpt/tests/interfaces/ua-client-hints.idl | 2 +- test/wpt/tests/interfaces/uievents.idl | 14 +- test/wpt/tests/interfaces/url.idl | 1 + test/wpt/tests/interfaces/urlpattern.idl | 4 + test/wpt/tests/interfaces/wai-aria.idl | 3 +- test/wpt/tests/interfaces/wasm-js-api.idl | 2 + .../wpt/tests/interfaces/web-animations-2.idl | 1 + test/wpt/tests/interfaces/webauthn.idl | 22 +- .../webcodecs-opus-codec-registration.idl | 14 + test/wpt/tests/interfaces/webcodecs.idl | 32 +- test/wpt/tests/interfaces/webgl1.idl | 8 + test/wpt/tests/interfaces/webgl2.idl | 1 - test/wpt/tests/interfaces/webgpu.idl | 86 +-- test/wpt/tests/interfaces/webidl.idl | 4 +- test/wpt/tests/interfaces/webnn.idl | 288 +++++----- .../interfaces/webrtc-encoded-transform.idl | 26 +- test/wpt/tests/interfaces/webrtc-stats.idl | 4 - test/wpt/tests/interfaces/webrtc.idl | 7 +- test/wpt/tests/interfaces/webtransport.idl | 16 +- test/wpt/tests/interfaces/webxr.idl | 5 +- test/wpt/tests/lint.ignore | 95 +++- .../tests/resources/chromium/fake-serial.js | 30 +- .../chromium/generic_sensor_mocks.js | 523 ------------------ .../chromium/generic_sensor_mocks.js.headers | 1 - .../resources/chromium/mock-direct-sockets.js | 94 ---- .../chromium/mock-pressure-service.js | 33 +- .../declarative-shadow-dom-polyfill.js | 25 - test/wpt/tests/resources/idlharness.js | 1 + .../tests/unit/IdlArray/is_json_type.html | 1 + test/wpt/tests/resources/test/tox.ini | 2 +- test/wpt/tests/resources/testdriver.js | 74 +++ test/wpt/tests/resources/testharness.js | 170 +++--- test/wpt/tests/resources/testharnessreport.js | 25 - .../error-message-event-worker.js | 2 + .../error-message-event.https.html | 47 ++ .../service-worker/WEB_FEATURES.yml | 5 + ...inting-video-with-range-request.https.html | 29 +- .../fetch-request-resources.https.html | 75 ++- .../local-url-inherit-controller.https.html | 18 + .../navigation-timing-sizes.https.html | 113 ++++ .../fetch-request-resources-iframe.https.html | 43 +- .../local-url-inherit-controller-frame.html | 60 +- .../range-request-with-synth-head-worker.js | 36 ++ .../static-router/resources/direct.css | 3 + .../static-router/resources/direct.html | 5 + .../static-router/resources/direct.js | 1 + .../static-router/resources/direct.py | 11 + .../static-router/resources/imported-sw.js | 13 + .../static-router/resources/router-rules.js | 120 +++- .../resources/static-router-helpers.sub.js | 76 +++ .../static-router-no-fetch-handler-sw.js | 35 ++ ...outer-race-network-and-fetch-handler-sw.js | 57 ++ .../resources/static-router-sw.js | 29 +- .../resources/static-router-sw.sub.js | 29 + .../static-router-fetch-event.https.html | 39 ++ .../static-router-invalid-rules.https.html | 73 +++ .../static-router-main-resource.https.html | 91 ++- ...r-multiple-router-registrations.https.html | 57 ++ ...tatic-router-mutiple-conditions.https.html | 112 ++++ .../static-router-no-fetch-handler.https.html | 48 ++ ...-race-network-and-fetch-handler.https.html | 91 +++ ...atic-router-request-destination.https.html | 77 +++ .../static-router-request-method.https.html | 59 ++ .../static-router-subresource.https.html | 141 +++-- ...ket-quota-indexeddb.tentative.https.any.js | 1 + .../bucket_names.tentative.https.any.js | 93 ++++ .../buckets_basic.tentative.https.any.js | 50 ++ ...kets_storage_policy.tentative.https.any.js | 32 ++ .../buckets/detached-iframe.https.html | 68 +++ .../buckets/idlharness-worker.https.any.js | 22 + .../buckets/opaque-origin.https.window.js | 58 ++ .../resources/opaque-origin-sandbox.html | 52 ++ .../tests/storage/buckets/resources/util.js | 2 +- ...orage_bucket_object.tentative.https.any.js | 143 +++++ ...Send-binary-arraybufferview-float16.any.js | 40 ++ .../websockets/handlers/basic_auth_wsh.py | 2 +- .../handlers/delayed-passive-close_wsh.py | 2 +- .../websockets/handlers/echo-cookie_wsh.py | 2 +- .../websockets/handlers/echo-query_v13_wsh.py | 2 +- .../websockets/handlers/echo-query_wsh.py | 2 +- .../tests/websockets/handlers/echo_raw_wsh.py | 2 +- .../wpt/tests/websockets/handlers/echo_wsh.py | 2 +- .../websockets/handlers/empty-message_wsh.py | 2 +- .../websockets/handlers/msg_channel_wsh.py | 2 +- .../tests/websockets/handlers/origin_wsh.py | 2 +- .../handlers/passive-close-abort_wsh.py | 24 + .../websockets/handlers/protocol_array_wsh.py | 2 +- .../tests/websockets/handlers/protocol_wsh.py | 2 +- .../tests/websockets/handlers/referrer_wsh.py | 2 +- .../websockets/handlers/remote-close_wsh.py | 44 ++ .../handlers/simple_handshake_wsh.py | 4 +- .../websockets/handlers/sleep_10_v13_wsh.py | 2 +- .../handlers/stash_responder_blocking_wsh.py | 2 +- .../handlers/stash_responder_wsh.py | 2 +- .../websockets/stream/tentative/close.any.js | 116 ++-- .../stream/tentative/constructor.any.js | 8 +- .../stream/tentative/remote-close.any.js | 74 +++ .../stream/tentative/websocket-error.any.js | 50 ++ test/wpt/tests/wpt | 4 +- .../xhr/send-data-sharedarraybuffer.any.js | 2 +- 285 files changed, 6877 insertions(+), 2391 deletions(-) rename test/wpt/tests/eventsource/{eventsource-request-cancellation.any.window.js => eventsource-request-cancellation.window.js} (100%) rename test/wpt/tests/eventsource/{request-credentials.any.window.js => request-credentials.window.js} (100%) rename test/wpt/tests/eventsource/{request-redirect.any.window.js => request-redirect.window.js} (100%) create mode 100644 test/wpt/tests/fetch/api/crashtests/aborted-fetch-response.https.html create mode 100644 test/wpt/tests/fetch/api/crashtests/huge-fetch.any.js delete mode 100644 test/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv create mode 100644 test/wpt/tests/fetch/api/request/request-constructor-init-body-override.any.js create mode 100644 test/wpt/tests/fetch/api/resources/huge-response.py create mode 100644 test/wpt/tests/fetch/api/response/response-arraybuffer-realm.window.js create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cache.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cookies.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-storage.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/empty.html create mode 100644 test/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py create mode 100644 test/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/br/br-body.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/big.text.br create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br create mode 100644 test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers rename test/wpt/tests/fetch/content-encoding/{ => gzip}/bad-gzip-body.any.js (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/big-gzip-body.https.any.js (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/gzip-body.any.js (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/bad-gzip-body.py (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/big.text.gz (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/big.text.gz.headers (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/foo.octetstream.gz (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/foo.octetstream.gz.headers (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/foo.text.gz (100%) rename test/wpt/tests/fetch/content-encoding/{ => gzip}/resources/foo.text.gz.headers (100%) create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/bad-zstd-body.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/big-window-zstd-body.tentative.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/big-zstd-body.https.any.js create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/bad-zstd-body.py create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/big.text.zst create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/big.text.zst.headers create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/big.window.zst create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/big.window.zst.headers create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst.headers create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst.headers create mode 100644 test/wpt/tests/fetch/content-encoding/zstd/zstd-body.https.any.js create mode 100644 test/wpt/tests/fetch/fetch-later/resources/fetch-later-helper.js create mode 100644 test/wpt/tests/fetch/fetch-later/resources/get_beacon.py create mode 100644 test/wpt/tests/fetch/fetch-later/resources/set_beacon.py create mode 100644 test/wpt/tests/fetch/fetch-later/send-on-deactivate-with-background-sync.tentative.https.window.js create mode 100644 test/wpt/tests/fetch/metadata/WEB_FEATURES.yml delete mode 100644 test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html delete mode 100644 test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html create mode 100644 test/wpt/tests/fetch/private-network-access/anchor.tentative.https.window.js create mode 100644 test/wpt/tests/fetch/private-network-access/anchor.tentative.window.js create mode 100644 test/wpt/tests/fetch/private-network-access/resources/anchor.html create mode 100644 test/wpt/tests/fetch/private-network-access/resources/iframed-no-preflight-received.html create mode 100644 test/wpt/tests/fetch/private-network-access/resources/no-preflight-received.html create mode 100644 test/wpt/tests/fetch/private-network-access/resources/open-to-existing-window.html create mode 100644 test/wpt/tests/fetch/private-network-access/resources/openee.html create mode 100644 test/wpt/tests/fetch/private-network-access/resources/service-worker-fetch-all.js create mode 100644 test/wpt/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js create mode 100644 test/wpt/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js create mode 100644 test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js create mode 100644 test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.window.js create mode 100644 test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html rename test/wpt/tests/fetch/security/{ => dangling-markup}/dangling-markup-mitigation-data-url.tentative.sub.html (100%) rename test/wpt/tests/fetch/security/{ => dangling-markup}/dangling-markup-mitigation.tentative.html (100%) create mode 100644 test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html create mode 100644 test/wpt/tests/fetch/security/dangling-markup/media.html create mode 100644 test/wpt/tests/fetch/security/dangling-markup/option.html create mode 100644 test/wpt/tests/fetch/security/dangling-markup/resources/empty.html create mode 100644 test/wpt/tests/fetch/security/dangling-markup/resources/helper.js create mode 100644 test/wpt/tests/fetch/security/dangling-markup/service-worker.js create mode 100644 test/wpt/tests/fetch/security/dangling-markup/textarea.html create mode 100644 test/wpt/tests/interfaces/anonymous-iframe.idl create mode 100644 test/wpt/tests/interfaces/css-nesting.idl create mode 100644 test/wpt/tests/interfaces/css-scroll-snap-2.idl delete mode 100644 test/wpt/tests/interfaces/custom-state-pseudo-class.idl create mode 100644 test/wpt/tests/interfaces/device-attributes.idl create mode 100644 test/wpt/tests/interfaces/digital-identities.idl create mode 100644 test/wpt/tests/interfaces/interest-invokers.tentative.idl create mode 100644 test/wpt/tests/interfaces/long-animation-frames.idl create mode 100644 test/wpt/tests/interfaces/observable.tentative.idl delete mode 100644 test/wpt/tests/resources/chromium/generic_sensor_mocks.js delete mode 100644 test/wpt/tests/resources/chromium/generic_sensor_mocks.js.headers delete mode 100644 test/wpt/tests/resources/chromium/mock-direct-sockets.js delete mode 100644 test/wpt/tests/resources/declarative-shadow-dom-polyfill.js create mode 100644 test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event-worker.js create mode 100644 test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/WEB_FEATURES.yml create mode 100644 test/wpt/tests/service-workers/service-worker/navigation-timing-sizes.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/resources/range-request-with-synth-head-worker.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/direct.css create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/direct.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/direct.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/direct.py create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/imported-sw.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/static-router-helpers.sub.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/static-router-no-fetch-handler-sw.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/static-router-race-network-and-fetch-handler-sw.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.sub.js create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-fetch-event.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-invalid-rules.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-multiple-router-registrations.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-mutiple-conditions.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-no-fetch-handler.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-race-network-and-fetch-handler.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-request-destination.https.html create mode 100644 test/wpt/tests/service-workers/service-worker/tentative/static-router/static-router-request-method.https.html create mode 100644 test/wpt/tests/storage/buckets/bucket_names.tentative.https.any.js create mode 100644 test/wpt/tests/storage/buckets/buckets_basic.tentative.https.any.js create mode 100644 test/wpt/tests/storage/buckets/buckets_storage_policy.tentative.https.any.js create mode 100644 test/wpt/tests/storage/buckets/detached-iframe.https.html create mode 100644 test/wpt/tests/storage/buckets/idlharness-worker.https.any.js create mode 100644 test/wpt/tests/storage/buckets/opaque-origin.https.window.js create mode 100644 test/wpt/tests/storage/buckets/resources/opaque-origin-sandbox.html create mode 100644 test/wpt/tests/storage/buckets/storage_bucket_object.tentative.https.any.js create mode 100644 test/wpt/tests/websockets/Send-binary-arraybufferview-float16.any.js create mode 100644 test/wpt/tests/websockets/handlers/passive-close-abort_wsh.py create mode 100644 test/wpt/tests/websockets/handlers/remote-close_wsh.py create mode 100644 test/wpt/tests/websockets/stream/tentative/remote-close.any.js create mode 100644 test/wpt/tests/websockets/stream/tentative/websocket-error.any.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 306428a453a..fe2a1f3b551 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,6 +113,7 @@ git sparse-checkout add /websockets git sparse-checkout add /mimesniff git sparse-checkout add /storage git sparse-checkout add /service-workers +git sparse-checkout add /eventsource ``` diff --git a/lib/web/fetch/body.js b/lib/web/fetch/body.js index e4dee2bd9f4..0f4ef04d2fe 100644 --- a/lib/web/fetch/body.js +++ b/lib/web/fetch/body.js @@ -385,6 +385,15 @@ function bodyMixinMethods (instance) { 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".' ) }, instance, false) + }, + + bytes () { + // The bytes() method steps are to return the result of running consume body + // with this and the following step given a byte sequence bytes: return the + // result of creating a Uint8Array from bytes in this’s relevant realm. + return consumeBody(this, (bytes) => { + return new Uint8Array(bytes) + }, instance, true) } } diff --git a/test/wpt/runner/worker.mjs b/test/wpt/runner/worker.mjs index f0f45542e97..184e35273a9 100644 --- a/test/wpt/runner/worker.mjs +++ b/test/wpt/runner/worker.mjs @@ -99,6 +99,9 @@ Object.defineProperties(globalThis, { } }) +// TODO: remove once Float16Array is added. Otherwise a test throws with an uncaught exception. +globalThis.Float16Array ??= class Float16Array {} + // TODO: remove once node 18 is dropped if (!globalThis.crypto) { Object.defineProperty(globalThis, 'crypto', { diff --git a/test/wpt/status/fetch.status.json b/test/wpt/status/fetch.status.json index 4660688d424..adf8562e669 100644 --- a/test/wpt/status/fetch.status.json +++ b/test/wpt/status/fetch.status.json @@ -61,13 +61,20 @@ "Fetch with Chicken", "Fetch with Chicken with body", "Fetch with TacO and mode \"same-origin\" needs an Origin header", - "Fetch with TacO and mode \"cors\" needs an Origin header" + "Fetch with TacO and mode \"cors\" needs an Origin header", + "Fetch with POST with Float16Array body" ] }, "request-private-network-headers.tentative.any.js": { "note": "undici doesn't filter headers", "skip": true }, + "request-upload.any.js": { + "note": "no Float16Array", + "fail": [ + "Fetch with POST with Float16Array body" + ] + }, "request-upload.h2.any.js": { "note": "undici doesn't support http/2", "skip": true @@ -403,6 +410,59 @@ ] } }, + "content-encoding": { + "br": { + "bad-br-body.https.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "Consuming the body of a resource with bad br content with arrayBuffer() should reject" + ] + }, + "big-br-body.https.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "large br data should be decompressed successfully", + "large br data should be decompressed successfully with byte stream" + ] + }, + "br-body.https.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "fetched br data with content type text should be decompressed.", + "fetched br data with content type octetstream should be decompressed." + ] + } + }, + "gzip": { + "bad-gzip-body.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "Consuming the body of a resource with bad gzip content with arrayBuffer() should reject", + "Consuming the body of a resource with bad gzip content with blob() should reject", + "Consuming the body of a resource with bad gzip content with json() should reject", + "Consuming the body of a resource with bad gzip content with text() should reject" + ] + }, + "gzip-body.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "fetched gzip data with content type text should be decompressed.", + "fetched gzip data with content type octetstream should be decompressed." + ] + }, + "big-gzip-body.https.any.js": { + "note": "TODO(@KhafraDev): investigate failure", + "fail": [ + "large gzip data should be decompressed successfully", + "large gzip data should be decompressed successfully with byte stream" + ] + } + }, + "zstd": { + "note": "node does not have zstd yet", + "skip": true + } + }, "content-length": { "api-and-duplicate-headers.any.js": { "fail": [ diff --git a/test/wpt/tests/.azure-pipelines.yml b/test/wpt/tests/.azure-pipelines.yml index 1a21d2f7a00..c018c592d1d 100644 --- a/test/wpt/tests/.azure-pipelines.yml +++ b/test/wpt/tests/.azure-pipelines.yml @@ -39,7 +39,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/affected_tests.yml parameters: artifactName: 'safari-preview-affected-tests' @@ -56,7 +56,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/affected_tests.yml parameters: checkoutCommit: 'HEAD^1' @@ -77,7 +77,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - script: | set -eux -o pipefail @@ -98,26 +98,14 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_fonts.yml - template: tools/ci/azure/install_certs.yml - template: tools/ci/azure/color_profile.yml - - template: tools/ci/azure/install_chrome.yml - - template: tools/ci/azure/install_firefox.yml - template: tools/ci/azure/install_safari.yml - template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_manifest.yml - - script: | - set -eux -o pipefail - ./wpt run --yes --no-manifest-update --manifest MANIFEST.json --metadata infrastructure/metadata/ --log-mach - --log-mach-level info --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_macos_chrome.json --channel dev chrome infrastructure/ - condition: succeededOrFailed() - displayName: 'Run tests (Chrome Dev)' - - script: | - set -eux -o pipefail - ./wpt run --yes --no-manifest-update --manifest MANIFEST.json --metadata infrastructure/metadata/ --log-mach - --log-mach-level info --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_macos_firefox.json --channel nightly firefox infrastructure/ - condition: succeededOrFailed() - displayName: 'Run tests (Firefox Nightly)' - script: | set -eux -o pipefail export SYSTEM_VERSION_COMPAT=0 @@ -132,8 +120,8 @@ jobs: - template: tools/ci/azure/publish_logs.yml - template: tools/ci/azure/sysdiagnose.yml -- job: tools_unittest_mac_py37 - displayName: 'tools/ unittests: macOS + Python 3.7' +- job: tools_unittest_mac_py38 + displayName: 'tools/ unittests: macOS + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.tools_unittest'] pool: @@ -141,15 +129,15 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/ - toxenv: py37 + toxenv: py38 - job: tools_unittest_mac_py311 - displayName: 'tools/ unittests: macOS + Python 3.11' + displayName: 'tools/ unittests: macOS + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.tools_unittest'] pool: @@ -157,15 +145,15 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/ toxenv: py311 -- job: wptrunner_unittest_mac_py37 - displayName: 'tools/wptrunner/ unittests: macOS + Python 3.7' +- job: wptrunner_unittest_mac_py38 + displayName: 'tools/wptrunner/ unittests: macOS + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wptrunner_unittest'] pool: @@ -173,15 +161,15 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/wptrunner/ - toxenv: py37 + toxenv: py38 - job: wptrunner_unittest_mac_py311 - displayName: 'tools/wptrunner/ unittests: macOS + Python 3.11' + displayName: 'tools/wptrunner/ unittests: macOS + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wptrunner_unittest'] pool: @@ -189,15 +177,15 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/wptrunner/ toxenv: py311 -- job: wpt_integration_mac_py37 - displayName: 'tools/wpt/ tests: macOS + Python 3.7' +- job: wpt_integration_mac_py38 + displayName: 'tools/wpt/ tests: macOS + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wpt_integration'] pool: @@ -206,7 +194,7 @@ jobs: # full checkout required - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' - template: tools/ci/azure/install_chrome.yml - template: tools/ci/azure/install_firefox.yml - template: tools/ci/azure/update_hosts.yml @@ -214,10 +202,10 @@ jobs: - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/wpt/ - toxenv: py37 + toxenv: py38 - job: wpt_integration_mac_py311 - displayName: 'tools/wpt/ tests: macOS + Python 3.11' + displayName: 'tools/wpt/ tests: macOS + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wpt_integration'] pool: @@ -226,7 +214,7 @@ jobs: # full checkout required - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/install_chrome.yml - template: tools/ci/azure/install_firefox.yml - template: tools/ci/azure/update_hosts.yml @@ -236,8 +224,8 @@ jobs: directory: tools/wpt/ toxenv: py311 -- job: tools_unittest_win_py37 - displayName: 'tools/ unittests: Windows + Python 3.7' +- job: tools_unittest_win_py38 + displayName: 'tools/ unittests: Windows + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.tools_unittest'] pool: @@ -247,16 +235,16 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' addToPath: false - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/ - toxenv: py37 + toxenv: py38 - job: tools_unittest_win_py311 - displayName: 'tools/ unittests: Windows + Python 3.11' + displayName: 'tools/ unittests: Windows + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.tools_unittest'] pool: @@ -264,7 +252,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' addToPath: false - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml @@ -272,8 +260,8 @@ jobs: directory: tools/ toxenv: py311 -- job: wptrunner_unittest_win_py37 - displayName: 'tools/wptrunner/ unittests: Windows + Python 3.7' +- job: wptrunner_unittest_win_py38 + displayName: 'tools/wptrunner/ unittests: Windows + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wptrunner_unittest'] pool: @@ -281,16 +269,16 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' addToPath: false - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/wptrunner/ - toxenv: py37 + toxenv: py38 - job: wptrunner_unittest_win_py311 - displayName: 'tools/wptrunner/ unittests: Windows + Python 3.11' + displayName: 'tools/wptrunner/ unittests: Windows + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wptrunner_unittest'] pool: @@ -298,7 +286,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' addToPath: false - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/tox_pytest.yml @@ -306,8 +294,8 @@ jobs: directory: tools/wptrunner/ toxenv: py311 -- job: wpt_integration_win_py37 - displayName: 'tools/wpt/ tests: Windows + Python 3.7' +- job: wpt_integration_win_py38 + displayName: 'tools/wpt/ tests: Windows + Python 3.8' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wpt_integration'] pool: @@ -316,7 +304,7 @@ jobs: # full checkout required - task: UsePythonVersion@0 inputs: - versionSpec: '3.7' + versionSpec: '3.8' # currently just using the outdated Chrome/Firefox on the VM rather than # figuring out how to install Chrome Dev channel on Windows # - template: tools/ci/azure/install_chrome.yml @@ -326,10 +314,10 @@ jobs: - template: tools/ci/azure/tox_pytest.yml parameters: directory: tools/wpt/ - toxenv: py37 + toxenv: py38 - job: wpt_integration_win_py311 - displayName: 'tools/wpt/ tests: Windows + Python 3.11' + displayName: 'tools/wpt/ tests: Windows + Python 3.12' dependsOn: decision condition: dependencies.decision.outputs['test_jobs.wpt_integration'] pool: @@ -338,7 +326,7 @@ jobs: # full checkout required - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' # currently just using the outdated Chrome/Firefox on the VM rather than # figuring out how to install Chrome Dev channel on Windows # - template: tools/ci/azure/install_chrome.yml @@ -364,7 +352,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/system_info.yml - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml @@ -373,7 +361,7 @@ jobs: channel: stable - template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_manifest.yml - - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel stable edgechromium + - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel stable edge displayName: 'Run tests (Edge Stable)' - task: PublishBuildArtifacts@1 displayName: 'Publish results' @@ -400,7 +388,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/system_info.yml - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml @@ -409,7 +397,7 @@ jobs: channel: dev - template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_manifest.yml - - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel dev edgechromium + - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel dev edge displayName: 'Run tests (Edge Dev)' - task: PublishBuildArtifacts@1 displayName: 'Publish results' @@ -436,7 +424,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml - template: tools/ci/azure/install_edge.yml @@ -444,7 +432,7 @@ jobs: channel: canary - template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_manifest.yml - - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel canary edgechromium + - script: python ./wpt run --yes --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --install-fonts --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --log-mach - --log-mach-level info --channel canary edge displayName: 'Run tests (Edge Canary)' - task: PublishBuildArtifacts@1 displayName: 'Publish results' @@ -471,7 +459,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml - template: tools/ci/azure/color_profile.yml @@ -511,7 +499,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml - template: tools/ci/azure/color_profile.yml @@ -548,7 +536,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.12' - template: tools/ci/azure/checkout.yml - template: tools/ci/azure/install_certs.yml - template: tools/ci/azure/color_profile.yml diff --git a/test/wpt/tests/.taskcluster.yml b/test/wpt/tests/.taskcluster.yml index 2a005df772f..38af7fa10af 100644 --- a/test/wpt/tests/.taskcluster.yml +++ b/test/wpt/tests/.taskcluster.yml @@ -7,7 +7,7 @@ tasks: run_task: $if: 'tasks_for == "github-push"' then: - $if: 'event.ref in ["refs/heads/master", "refs/heads/epochs/daily", "refs/heads/epochs/weekly", "refs/heads/triggers/chrome_stable", "refs/heads/triggers/chrome_beta", "refs/heads/triggers/chrome_dev", "refs/heads/triggers/chrome_nightly", "refs/heads/triggers/firefox_stable", "refs/heads/triggers/firefox_beta", "refs/heads/triggers/firefox_nightly", "refs/heads/triggers/firefox_android_nightly", "refs/heads/triggers/webkitgtk_minibrowser_stable", "refs/heads/triggers/webkitgtk_minibrowser_beta", "refs/heads/triggers/webkitgtk_minibrowser_nightly", "refs/heads/triggers/servo_nightly"]' + $if: 'event.ref in ["refs/heads/master", "refs/heads/epochs/daily", "refs/heads/epochs/weekly", "refs/heads/triggers/chrome_stable", "refs/heads/triggers/chrome_beta", "refs/heads/triggers/chrome_canary", "refs/heads/triggers/chrome_dev", "refs/heads/triggers/chrome_nightly", "refs/heads/triggers/firefox_stable", "refs/heads/triggers/firefox_beta", "refs/heads/triggers/firefox_nightly", "refs/heads/triggers/firefox_android_nightly", "refs/heads/triggers/webkitgtk_minibrowser_stable", "refs/heads/triggers/webkitgtk_minibrowser_beta", "refs/heads/triggers/webkitgtk_minibrowser_nightly", "refs/heads/triggers/servo_nightly"]' then: true else: false else: @@ -57,7 +57,7 @@ tasks: owner: ${owner} source: ${event.repository.clone_url} payload: - image: webplatformtests/wpt:0.55 + image: webplatformtests/wpt:0.57 maxRunTime: 7200 artifacts: public/results: diff --git a/test/wpt/tests/FileAPI/blob/Blob-constructor.any.js b/test/wpt/tests/FileAPI/blob/Blob-constructor.any.js index d16f760caee..6dc44e8e156 100644 --- a/test/wpt/tests/FileAPI/blob/Blob-constructor.any.js +++ b/test/wpt/tests/FileAPI/blob/Blob-constructor.any.js @@ -290,10 +290,11 @@ test_blob(function() { new Int16Array([0x4150, 0x5353]), new Uint32Array([0x53534150]), new Int32Array([0x53534150]), + new Float16Array([2.65625, 58.59375]), new Float32Array([0xD341500000]) ]); }, { - expected: "PASSPASSPASSPASSPASSPASSPASS", + expected: "PASSPASSPASSPASSPASSPASSPASSPASS", type: "", desc: "Passing typed arrays as elements of the blobParts array should work." }); diff --git a/test/wpt/tests/common/media.js b/test/wpt/tests/common/media.js index 800593f5343..a5a8e957e9b 100644 --- a/test/wpt/tests/common/media.js +++ b/test/wpt/tests/common/media.js @@ -14,9 +14,6 @@ function getVideoURI(base) if (videotag.canPlayType('video/webm; codecs="vp9, opus"') ) { extension = '.webm'; - } else if ( videotag.canPlayType('video/ogg; codecs="theora, vorbis"') ) - { - extension = '.ogv'; } } @@ -52,7 +49,6 @@ function getMediaContentType(url) { var extension = new URL(url, location).pathname.split(".").pop(); var map = { "mp4" : "video/mp4", - "ogv" : "application/ogg", "webm": "video/webm", "mp3" : "audio/mp3", "oga" : "application/ogg", diff --git a/test/wpt/tests/common/security-features/tools/generate.py b/test/wpt/tests/common/security-features/tools/generate.py index 409b4f195ff..d1cd33147fc 100644 --- a/test/wpt/tests/common/security-features/tools/generate.py +++ b/test/wpt/tests/common/security-features/tools/generate.py @@ -321,7 +321,7 @@ def generate_test_source_files(spec_directory, test_helper_filenames, # Choose a debug/release template depending on the target. html_template = "test.%s.html.template" % target - artifact_order = test_expansion_schema.keys() + artifact_order = list(test_expansion_schema.keys()) artifact_order.remove('expansion') excluded_selection_pattern = '' diff --git a/test/wpt/tests/common/security-features/tools/spec_validator.py b/test/wpt/tests/common/security-features/tools/spec_validator.py index f8a1390ef0d..e4c9e145f83 100644 --- a/test/wpt/tests/common/security-features/tools/spec_validator.py +++ b/test/wpt/tests/common/security-features/tools/spec_validator.py @@ -5,7 +5,7 @@ def assert_non_empty_string(obj, field): assert field in obj, 'Missing field "%s"' % field - assert isinstance(obj[field], basestring), \ + assert isinstance(obj[field], str), \ 'Field "%s" must be a string' % field assert len(obj[field]) > 0, 'Field "%s" must not be empty' % field @@ -34,7 +34,7 @@ def assert_value_from(obj, field, items): def assert_atom_or_list_items_from(obj, field, items): - if isinstance(obj[field], basestring) or isinstance( + if isinstance(obj[field], str) or isinstance( obj[field], int) or obj[field] is None: assert_value_from(obj, field, items) return @@ -236,7 +236,7 @@ def assert_valid_spec_json(spec_json): try: validate(spec_json, error_details) except AssertionError as err: - print('ERROR:', err.message) + print('ERROR:', err) print(json.dumps(error_details, indent=4)) sys.exit(1) diff --git a/test/wpt/tests/eventsource/eventsource-request-cancellation.any.window.js b/test/wpt/tests/eventsource/eventsource-request-cancellation.window.js similarity index 100% rename from test/wpt/tests/eventsource/eventsource-request-cancellation.any.window.js rename to test/wpt/tests/eventsource/eventsource-request-cancellation.window.js diff --git a/test/wpt/tests/eventsource/request-credentials.any.window.js b/test/wpt/tests/eventsource/request-credentials.window.js similarity index 100% rename from test/wpt/tests/eventsource/request-credentials.any.window.js rename to test/wpt/tests/eventsource/request-credentials.window.js diff --git a/test/wpt/tests/eventsource/request-redirect.any.window.js b/test/wpt/tests/eventsource/request-redirect.window.js similarity index 100% rename from test/wpt/tests/eventsource/request-redirect.any.window.js rename to test/wpt/tests/eventsource/request-redirect.window.js diff --git a/test/wpt/tests/fetch/api/basic/keepalive.any.js b/test/wpt/tests/fetch/api/basic/keepalive.any.js index 899d41d676a..d6ec1f67920 100644 --- a/test/wpt/tests/fetch/api/basic/keepalive.any.js +++ b/test/wpt/tests/fetch/api/basic/keepalive.any.js @@ -1,7 +1,6 @@ // META: global=window +// META: timeout=long // META: title=Fetch API: keepalive handling -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js // META: script=../resources/keepalive-helper.js diff --git a/test/wpt/tests/fetch/api/basic/request-headers.any.js b/test/wpt/tests/fetch/api/basic/request-headers.any.js index ac54256e4c6..8d2ad31e708 100644 --- a/test/wpt/tests/fetch/api/basic/request-headers.any.js +++ b/test/wpt/tests/fetch/api/basic/request-headers.any.js @@ -54,6 +54,7 @@ requestHeaders("Fetch with POST with Blob body", url, "POST", new Blob(["Test"]) requestHeaders("Fetch with POST with ArrayBuffer body", url, "POST", new ArrayBuffer(4), location.origin, "4"); requestHeaders("Fetch with POST with Uint8Array body", url, "POST", new Uint8Array(4), location.origin, "4"); requestHeaders("Fetch with POST with Int8Array body", url, "POST", new Int8Array(4), location.origin, "4"); +requestHeaders("Fetch with POST with Float16Array body", url, "POST", new Float16Array(1), location.origin, "2"); requestHeaders("Fetch with POST with Float32Array body", url, "POST", new Float32Array(1), location.origin, "4"); requestHeaders("Fetch with POST with Float64Array body", url, "POST", new Float64Array(1), location.origin, "8"); requestHeaders("Fetch with POST with DataView body", url, "POST", new DataView(new ArrayBuffer(8), 0, 4), location.origin, "4"); diff --git a/test/wpt/tests/fetch/api/basic/request-upload.any.js b/test/wpt/tests/fetch/api/basic/request-upload.any.js index 9168aa11541..0c4813bb531 100644 --- a/test/wpt/tests/fetch/api/basic/request-upload.any.js +++ b/test/wpt/tests/fetch/api/basic/request-upload.any.js @@ -60,6 +60,10 @@ testUpload("Fetch with POST with Int8Array body", url, "POST", () => new Int8Array(4), "\0\0\0\0"); +testUpload("Fetch with POST with Float16Array body", url, + "POST", + () => new Float16Array(2), + "\0\0\0\0"); testUpload("Fetch with POST with Float32Array body", url, "POST", () => new Float32Array(1), diff --git a/test/wpt/tests/fetch/api/basic/request-upload.h2.any.js b/test/wpt/tests/fetch/api/basic/request-upload.h2.any.js index eedc2bf6a76..68122278ccd 100644 --- a/test/wpt/tests/fetch/api/basic/request-upload.h2.any.js +++ b/test/wpt/tests/fetch/api/basic/request-upload.h2.any.js @@ -184,3 +184,26 @@ promise_test(async (t) => { await promise_rejects_js(t, TypeError, fetch(url, { method, body, duplex })); }, "Streaming upload should fail on a 401 response"); +promise_test(async (t) => { + const abortMessage = 'foo abort'; + let streamCancelPromise = new Promise(async res => { + var stream = new ReadableStream({ + cancel: function(reason) { + res(reason); + } + }); + let abortController = new AbortController(); + let fetchPromise = promise_rejects_exactly(t, abortMessage, fetch('', { + method: 'POST', + body: stream, + duplex: 'half', + signal: abortController.signal + })); + abortController.abort(abortMessage); + await fetchPromise; + }); + + let cancelReason = await streamCancelPromise; + assert_equals( + cancelReason, abortMessage, 'ReadableStream.cancel should be called.'); +}, 'ReadbleStream should be closed on signal.abort'); diff --git a/test/wpt/tests/fetch/api/body/formdata.any.js b/test/wpt/tests/fetch/api/body/formdata.any.js index e25035923c1..6733fa0ed70 100644 --- a/test/wpt/tests/fetch/api/body/formdata.any.js +++ b/test/wpt/tests/fetch/api/body/formdata.any.js @@ -12,3 +12,14 @@ promise_test(async t => { const fd = await req.formData(); assert_true(fd instanceof FormData); }, 'Consume empty request.formData() as FormData'); + +promise_test(async t => { + let formdata = new FormData(); + formdata.append('foo', new Blob([JSON.stringify({ bar: "baz", })], { type: "application/json" })); + let blob = await new Response(formdata).blob(); + let body = await blob.text(); + blob = new Blob([body.toLowerCase()], { type: blob.type.toLowerCase() }); + let formdataWithLowercaseBody = await new Response(blob).formData(); + assert_true(formdataWithLowercaseBody.has("foo")); + assert_equals(formdataWithLowercaseBody.get("foo").type, "application/json"); +}, 'Consume multipart/form-data headers case-insensitively'); diff --git a/test/wpt/tests/fetch/api/cors/cors-keepalive.any.js b/test/wpt/tests/fetch/api/cors/cors-keepalive.any.js index 08229f9cfc7..f54bf4f9b60 100644 --- a/test/wpt/tests/fetch/api/cors/cors-keepalive.any.js +++ b/test/wpt/tests/fetch/api/cors/cors-keepalive.any.js @@ -1,8 +1,6 @@ // META: global=window // META: timeout=long // META: title=Fetch API: keepalive handling -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js // META: script=../resources/keepalive-helper.js diff --git a/test/wpt/tests/fetch/api/crashtests/aborted-fetch-response.https.html b/test/wpt/tests/fetch/api/crashtests/aborted-fetch-response.https.html new file mode 100644 index 00000000000..fa1ad1717f0 --- /dev/null +++ b/test/wpt/tests/fetch/api/crashtests/aborted-fetch-response.https.html @@ -0,0 +1,11 @@ + + diff --git a/test/wpt/tests/fetch/api/crashtests/huge-fetch.any.js b/test/wpt/tests/fetch/api/crashtests/huge-fetch.any.js new file mode 100644 index 00000000000..1b09925d855 --- /dev/null +++ b/test/wpt/tests/fetch/api/crashtests/huge-fetch.any.js @@ -0,0 +1,16 @@ +// META: global=window,worker + +'use strict'; + +promise_test(async t => { + const response = await fetch('../resources/huge-response.py'); + const reader = response.body.getReader(); + // Read one chunk just to show willing. + const { value, done } = await reader.read(); + assert_false(done, 'there should be some data'); + assert_greater_than(value.byteLength, 0, 'the chunk should be non-empty'); + // Wait 2 seconds to give it a chance to crash. + await new Promise(resolve => t.step_timeout(resolve, 2000)); + // If we get here without crashing we passed the test. + reader.cancel(); +}, 'fetching a huge cacheable file but not reading it should not crash'); diff --git a/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js b/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js index 16656b5435a..5a155074378 100644 --- a/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js +++ b/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js @@ -24,6 +24,6 @@ promise_test(async test => { }, "getAuthorizationHeaderValue - same origin redirection"); promise_test(async (test) => { - const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py")); + const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py?strip_auth_header=true")); assert_equals(result, "none"); }, "getAuthorizationHeaderValue - cross origin redirection"); diff --git a/test/wpt/tests/fetch/api/redirect/redirect-keepalive.any.js b/test/wpt/tests/fetch/api/redirect/redirect-keepalive.any.js index beda8bb8e78..c9ac13f3dbb 100644 --- a/test/wpt/tests/fetch/api/redirect/redirect-keepalive.any.js +++ b/test/wpt/tests/fetch/api/redirect/redirect-keepalive.any.js @@ -1,7 +1,6 @@ // META: global=window +// META: timeout=long // META: title=Fetch API: keepalive handling -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js // META: script=../resources/keepalive-helper.js diff --git a/test/wpt/tests/fetch/api/redirect/redirect-keepalive.https.any.js b/test/wpt/tests/fetch/api/redirect/redirect-keepalive.https.any.js index 6765ecac6d7..54e4bc31fa1 100644 --- a/test/wpt/tests/fetch/api/redirect/redirect-keepalive.https.any.js +++ b/test/wpt/tests/fetch/api/redirect/redirect-keepalive.https.any.js @@ -1,7 +1,5 @@ // META: global=window // META: title=Fetch API: keepalive handling -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js // META: script=../resources/keepalive-helper.js diff --git a/test/wpt/tests/fetch/api/redirect/redirect-referrer-override.any.js b/test/wpt/tests/fetch/api/redirect/redirect-referrer-override.any.js index 56e55d79e14..337f8dd0698 100644 --- a/test/wpt/tests/fetch/api/redirect/redirect-referrer-override.any.js +++ b/test/wpt/tests/fetch/api/redirect/redirect-referrer-override.any.js @@ -3,7 +3,7 @@ // META: script=../resources/utils.js // META: script=/common/get-host-info.sub.js -function getExpectation(expectations, init, initScenario, redirectPolicy, redirectScenario) { +function getExpectation(expectations, initPolicy, initScenario, redirectPolicy, redirectScenario) { let policies = [ expectations[initPolicy][initScenario], expectations[redirectPolicy][redirectScenario] diff --git a/test/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv b/test/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv deleted file mode 100644 index de99616ecebe6107c33156c1c5513ef007b4e63d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94372 zcmdqIcT`hPv_G1J00}h-0YXudaDap+O$9*>hX6?^CZPz3N(l%G2#CEA2$0ZBLO?)N zNJ?Q4??DG5Dk)EM0GCdIk!Th-ls3~<){xVxUTUz67tgIZY>}_oDB;Tm*+u|Z(IHBanF@>}!!QCW;le^OoeR}rTC{rRf{q|X-y zw&2dz&>5(0B{SC9Y>AYBu+9!c@dyaY@B9B0bnGdgP2!#({+J)T9M;9mn0|~p=}Xce z13{=?yB&_?MEW3iHTjYA1VD-oVxQBZT!jkf{EC!s>9A5xdAPlddJSg>Aq=QpG*|oc3A(P76O4KDw7q0bLekU#tQu-mi?{&sQ$+aO$eO_LC~4t zoI?CfLsKv^0y@wCmj2hp8BIv}v~1ZjYXVDZ{F`zs(q&|s&4=I8|GHTJ-y`wA#}Q-? zAb~4t{D-vtmD+~>C}m^y`y2>g#S6%L2WuE_`-`j;rc{zwXle+UAy zZ$og>{tXXatzL4fK{wK5Uz%RqZH>K&{jL{tm$x36#!#=cHHyF8X500j>KfxAd)i(v z@;p2kdiJDhb}jqz_nqp!j{NAN^;wPZB_j9qlgt>-G1vutWN?8Ti(ALf>;Iu-=?PuxA& zca8m*3@LNWz3xX7kHqe5SSxRujkpRdWVzNQs-LGi9@#iYh}Ki-$}e5+&6r|--nv5k z(KjFQ-rhUEZp?CdCVD+r%I$rUacTSG1y1+;7+LX5Yx7ITTyy_$_2j->w;bwl@7aaX z$_wNA0s9P0;KRMmyDTPbUp&+DhX;lC=KsEgtY3WjmBOg&g522G{m2WvSKro!)W;ha zcqMf~XT0C|(UO6?FCSd-Ha@&rb?{6mW^}o-nd<7U#qI~{=)2zven->ExMUZLtbFu#rLo2 zGY9VRs23WZ1#v!mD;Itd-Q2FpHm}=A+W#i`o?zi_)h;)pFrowd;GU)9$gU9Omx0yE zFBa?VyIAIJw(eNs4*rLH>ahFQDmRT8?ZH#68ShUd#AeRkQra9H6+eyhGP?)756)iR zuX`c=Q(pd9_k`j7&x#4KP0b!AM}1G&n%uGoJ+JP#Ov(6DAaRT8>shm|$dKvLJ)H9k zhcat|+9BUQS!ql?*mBRZq}{xJ8+6hvOmIcKQaSM2q#yBc=T^rbN4ulUH6J`o-s~Cc z-qfq%ofYW7>;9JJn|)!$pvI3lH8ihF_3Wqhj*aW3dWQ!wyR{4urfj!2$}XSCPW9n} z>z3s%+v59mnfEtF9ya@hJzo+wP!4c-!>{YFFF*Qy;@j=~6n^|d!eOSbsnjtFS$WSZ zYG$%zdF!g?A0PGC2l(&iz3y+EU=x|DRfhxZ(hj;SUf9*#an=2fne`@vDdgN%X5f0p zPUM^+eBkV1x=mW!!K-R%I+OPv=X!?yg8G>dJqyF96z0^!nzzquZ{zntTh2sV?mYD~ zBCM9?f8}_hqI$(*%byXOT<3R8h4SrU>&E*kNNK+3phw*9O=q9mkCe+udxiD-lGzpQ zxFYCN(muW^dt#5Z-saw_q|k#q^ve9tuI!AuxYz@KWZ{CM`qGChXG{RE>U4$eS!6%a zHkT8jc1?Q?X2*gIP+vRr`p>O)bU$sIWfmOu9A$hsdSwNyID4Ww=&IbT!MPGuEC$nZ zWaGZ}33u&%(}59;fHT_>`)(-t8C*<m5c3@Fx`+cQ}xNwyxiZ{RdFwd$yJ#$YXoPK1BdC`iiOR_dQO++F1nc6)VX@G;Y#smt(;r++SNLC{r7_dyjDkPj`=XNp4K=Rh`%Ro zwbx{?xW=2o&97=ER9w5IGNf=%>SZaKejX9=D31K#ecCms7V{yr@2$|wEH-J-$>37B za|Gwjo@(}&Fxs-)X7n?C{B`HIKRXbrYxZMp$npG>mzS0I^T}F24Uf}Mb#>X=c{b5UemU2 zJ`x*sZd{YXUNP4dO<(wu*6H;FdnnYdq8d3CfADa}-t55lfgw~H<6dm|n;UmVO{M2= zT!UHt?pU6k@5Z+WUfxx)cD@js*|WREkLE^WMBB!0EB|aBPk2AEqi@OU#UHz$?5$f1 zQL?`F;YVv#+2%J;eE)Fdy&DB+!@2h6C420@CtSGI*=t=Ddp6$?e*x=4rR}2*lVcAM6^y%W2jArncsiJQdnyAO)qojUYs+WkO*|Gb}X&Ql(^&GK{jp%c;cX`s+b@}Rt_2Y{S=9I(JE0+Xc zD-8@*Jd)O{g3GRoE^29dyjt-f@tOMUY@eTbodHX`ev%ST+aQs3l3o;^_ON2zt-!#( zyCYxRCQV|0;=!Z?604#DvjN-K`cZ0Cd0r3q!-pMDkAs&CljAH_>x7NiL~0+|l5~Zx z3S!szuVqN(b}xp`M|}2f-cs>1JARYo@}>BS>K;@DfONgnaJh&&yMOh!u8{j{on-f} z{5fyroBuzI-e30y^&dWljLOMA+bo9ODgJd;1|YEa>&$ndc(#iMudv(>cjyeXF5(w1 zSD!7JGj;Y*b-G&k>_K|M(V87|=ox{J(n7(lZdmhV%Rp3vdepZizmj_GI{SZAZtl3D=qwyrWL-XIkc^vV2Bh+Izb!kG zzRI>{SQ_5G)SzDP#HtxHR>=J94TBfqao+aHU#o$^+U>i#*#|3Tyx`|wnhHyPg>@c6 zah7UFWam}{VArIk&rMTeT{0GB>TiYxxvM$4G{(P*(R*vTsQT^0jMU6z%;)ga%jHd? zj&~yX=!&urV6|Hq z@*Y?bk;WD8u2ZajR~>X=1NWCEc;Myp#$6h+Skp1%Q+wQIV zy6=2`tuH3%dT+xs`u%+6ZTD2=etg|P&tIqcjPPM`B3Hpa@fxd|UG@`v_XW&5N2&3y zU>^9YurY+;zj?bsR{3k^*2i+Zx;s&tU5CM(IH`NJnf_05(k)EHv(-nneVRwjgR2b0 zaS}({GUoZMvyXwk{Xv6GuaG0m^0n_jC4b47xx)UQgn4%EOGZ$dht-btdB>uv)b=lS zNPEaW5m;xIPrgCDCg*Uw0~5I+4u@ZV{YW~ZI5b{VWwfi*PsjBUzrZf?`)gsL(p7-h@b(o=&$A^u)jYEfgPZ)jv zWo;V2@qhxQAhScB?+(YbhWX=)I?b)uEKOHlvy{7Je^Ab<2G><}60caknU2*3{!Tj_ zw-lpg<9_@d>*&Y2$(*RwhHsBFRP7bwU8d~R&(__9q}4fd*0w$9w39P@doV<9`Tnp@ zmCYzeE3Fv3=eYleDU2c5GUl~J$z4n!Y26c%+kjC&8m+cjD(;zIJ_8G6o1lj zD{Xd{eVGoJ6yLrm{G=prmYi1aF1>36?;F>bn zzF{hXv2`*8y#C|qH?l~=ydLS=9c6*EvT#CuC{l}1(>##H|zIi+XlHc^;H4Om9V?Iz@>G)ke(E0j`< z^pt?&@c?r`&8zWX=K^!aZDBz}<44F0XU&JLZ)-unR$s5|1Z!)n-p*Ry+7Fw{D+})j z6omd-$oW+{RdYRuIG=#IJ} zAf|%$?c|vVjdaUZnX6jz!|u3Fx*8>H&nmMIx3zIZPdHny9iOdrNa!&XCO*yOxP|pp z*WPsU^^5P?z@hq0S{Xh+{MD#v8N-~?7iy|{2Xp#+{^P% zw&FyM1#9~JQHSt`>FW$J)1k62c%yIqEk~uGby^IQMU*w`Us3apXi+_kp6n?Y_0~8W z8rreG;)Yg7O!xa33!lgxtGA85(aVXDu6yF&<8}7>mh2aseA4bB3pb9A-jOEK-t64= zu$VR_D#|Z)HU`rW=-vDG{|z=m@c-KA;4;1#S9KbiL|F}?kN$uLe0`k4HQBVzc5KhU zF*)(YHK)Ac#$N}eU!RqK10ee1G}GZHZPA&9J1uV4=Hy zdX;{B3#Gl`^uX)k;rfFSMH>A@3sJQ-XX0YUPkm>-us;cb;6p-oT0Dv5Jfi&Fc>lx; z|FKD4Mv2zZGti6wvL`uy<&KF@$L4NA2TgVsBqhELDZJRU{GC)Z{Cx9aMTGUa1GDSV z&1sXjuE6{q+?V!S?8&{oKkhX*yp)2%KmW4!$6m+Xz6QeGkzV}i8;6Zj+J}}4?;qx6 zcL?^k+>f&k!YUlpqmkbEwwpN2U;vtk=<#leug0OqA9B@ur+wQ+i%kqVeyv*=4TVS& z9bhd{tO9oQmjf4Ho{CUYPuhLGsHpJhrHfn2UeZ^6i`}w{`A@X;Z>aW9r1W>_^8$l? z82KNO&piuY0uc}=&wqOT519eRFI|jtcPhf>o}K8SssA!q_;PSza$zB3;s1Z^Qa?XG z1qd8s+Y?aUr9708U&HS2T8!M$z^lF>DGG4$-T7=!Wz3IF`Ik?Vu7!O4DnS=ok4Vx# z1X^yz4r#aNAl4M7&3XwaF6nQVPqf_A{`StOW$kOB+G)%tPUfqHuyn_ z-J@;(>Jbrts>CL+*#07Jpv1F_#44zf%Ph=iieH6I>19~ zo^Q5ow3?=r2Js&`l)i}C`_XdS!vbn1*8a(hm?Mv0N1;N%91OL(GdWWAeQURid?m9r zc?Fca{(bP>mHpeW&F1SbQK?k=ZpOCe-3j4cxrnf}AsZa-ejl8>gwhZMy%Nm{9AJ@~ zqcDqIvj|E0*=dVU1A@BzYqbmcL&}7aUT1EKX60GakQ6@4(7Fh-9^r!MF4sWvI6Dix zReHZY?yF?iUU0W(;Wmqkjw0P6#q*sKkLWZ0aCoNINL^hgh!@J&1z1I2q&=gL-ubol zX==KrYM`T2){W5;GmnvmLTV{RQ&%;|fD*Z(#Ws1cu#+`{PH^1iYV54`bob)Xs0DorPk-`W~@qy>D1w-g#xQ;ajoyV}n*GGJK&2!~tY~R;P zlKPwh96!=(th=hW|6H_w%J~%GS}SKf7;1Kd#KU=*-`VEN?u93f>8G~Vzk6AE>CH?s zNPX2-x7vu4Pu$fnWGEBlSL}WnK8=%Rkk|MuhC}_kHIX{FH(4CSj0+kEpbL8fi~64* z$V?Gy<$u|x@2{2b*Rv;m+>*WLI5#KdZHR$d(6266oP}3k-WX;2EENyt&^^&rdPX1nE09n4Z55}3|p#5 zsr*;vaPxCsheVe1%Tn|EZ`ckDe}Hw}jKy88+H`6N(Z0|A!3BXi<)gukhYP6p%X1V{ zBg1!?(r8RF5>BlYBMsDCyCQo=*Sp^KOBzNra>BqHEE{-}pJqT{(vj^Ed!)-vAe}^3 zUdZFwdRybk#8|VIN50=8zrJ2yAN^u59F?Z$fXd40@1!U|fD`teyPmH#%g^o#b3mXm zC=vC}h06y&y&u$9%@G`PijUg%^d?ka;vk0ekl`Rf2LbA2x8plILQ!spIwDk|1m<<0 z>WXzQS+;BJ?YqfFRW-d$*S-#MXxTXeRbF=(if@eQBp^5%Dm;R9Be`01Hg50%?ZD*g z!mYMCf`&+ad;4AE=Xm~1qPz+Kh>13~HfVmgi#1<%u;XG?*oo&;LsVcHBq4)jdfmJ+ zi&98-LN)xlV3fN%LM!H`-s;`2T@uhlG!aT6!GtL2Ex;7BeyazrtI^8^1Tpe(J}H0^ zpjGM1N-j-d)YjJi{C@gn@Xpz998MRnASyC5$~dLQj%mAC_xREf28eLRrHA49F)AmySh#<@?hB8FWYrrchOb3=S4^)3=Z%>Jm1p= z#|y=Q;iz;MeBmvj2$f)URwNoYWS_NW@uj<_!&lbADO6wQ3N}k(PUC|Fgfom9siz}* zs@*myo-=|E@C1OVo6Dos;n|xC$0Ba5f8=<@$$Xu&mbnPQAs{er{Vvvxh27y+h1o6y zgr}jN;W8?gj*j+H$+iEr+)G8Ck0g!X|Na#fNm~BQXF9oUT zS(SJpu;=%W(Xaq1%Y6Rp(&g6I)`hiEXsnFFeFuKNdG+h{Jqo{>QKSSyNjXJ1McJK1 z9Ufqf>om4eg?EFYXi{JV|MiLdpz}ZXe%$cma^NBBj?hPYoH$)%ED?hO(bhuGNVJNl zwJJZ%(5kSff2>G@4HG-z+Nc!j#oEtne|&r%dJ6(gg&O+y(anoQxI!LLho6dWgomTL ziPl!PvH>hu#?r3DRpiWUDt!B{@VdfHMTLROkJ8PG0!bo#IXob{Gt3$hiOO!5O=f2u zTu(ZUnr%v6x%E+Zs_Dn){`r26iw!Xn z%k7cY9g_w8y3N9?2kYO!a!fJ^Bs+|+Z^uO+1vFFvwN9qR`6?pUkMe9E=nF6ov z-xrEHZ52Or>almu&fo_hTi}<+Nm(=P=b|>h6hFv*?6E7%$g;5YLHS+9Lu>CLbKBGx zh1b{KwT8`U(Hux0-@3=e zC;c^&`7a|CWxUOnwHRpsUqA@y{%xer$IMyj{JxO*FKR6NDt70Rt`CQD)_)8eY)&dI zS6O$oDmnPw1g1V}&mqi>+zq>(#C?xP+Vvejh_0=x2pffP?yo1eY^L(RmQm_zXFX&3Pgo9!0rHas+^!o}SFSlQ z1z9}zd|Z8-&YIwbtBp>(cM2Sj9s7`w_0UMP$()##s>x0q@!bFU!=u$RzkYo?Xw_wQ z@?+v%NJ_`J$qpmIt-t*Y`By&?GJbDnm^<|7zkn~-WcbRy{bd~!tFFf!9zdQb5wO9C zR}Ve0Mb(nMsxP0Cp5IW#@&t{eGjeD zU*y_-Bm7I$jpNoiS^n+nC+|A&JEn}@(sE{x6P|d-KGTSNpD_0np0(2=xUsp^a;)oR z92q_YXDo01e8{=w(Ot75gXkU8PxkEm2eCtdzn1^b0{9&&Go|Ij5dUD?r@x^s)X^K6 zgpSu)N3bA0wBjtkxN3Z+5%Zp*YI5Er=Vtct{i%~E1<(^ue-z;dX@ImrT`|87Sr=^L@_nM12cgz=E(7cNH7F-UQ?MQ=F=Gn zCXKoUl65-UxScZ9;|T!W1PmEXBqLE8NJMB+FG8=3^aSh-WTF(FR9^5@U22mK0GYwA zrNMxJmSF|G;>S16NdzD^XuO`vkBF{Sx8I_mlgNB{1~rXhlZON&DP0&5z!Qq8$-Hz7 z&nk^jhUAQ!XHp`HNozTa2NPQ-a5T>;ga*P6ixf%_IsrgtRO&mXhR5@Di`k@N1QY=;A~>kf zoC#)b#+KX^8mfL=u&ar2--#ML?s|=-L|2PCmih{4d{8 zq;&JCRJjZ_F47!E(`h%gl{zsMJd1z&*h2t1LPKb0BFD=j>ZWNLAu~=vuAbkOvTg?^ z8_>{E6;@N4hL?@o#GY9OfwCH)x&1GH4w2bn2{{UCuHwLXzM?R*h+iy7mMuJ|)A$fD zWd5c3-Knw?drv?N(p!4OczuZOrUE{!5y_xtno%^KFXu2#^UJx_J$vbF1_n{uv;<>R zP9*9JP!c*4%g2`?Z9qbC_$$6 zehO18mA1)ZSYeDaHo|^m3a=0Z;W4fRA{n3p8c0Cdp4nhD5TTUK(?rTkE2MJq5^=ng zC?GKaq6kAl@wmMNl^&c+hC{xc(Qpm>JV{JeYAR9F>2X04@NP^23B-tr$zphsN)fM( zSM!mvrMiSX~OQR8vNp(0$;K5u?Jh zp|n#aR9XL|Kr&&1t`@Q@c}-%4l9bd4YkUK1qUF);(IlxWhDIkMJOBYf8w2pk2;VeO zT4-}-2RcH{*cHi~AsdV6m|HwPhbNMQV3z>IWS+1mK~j(gN2 z1GXTgJyL2D3m!Kcr-oh|Pk~T{CnXn0AopP<9BK*ypX<4e z$G#Rr6`|eu6vXwalP4iZ)Z*>%AfprD7xI}*$Z}Vf4NS;30(ix8c$(E@#nZK;Pzbbu z?Jd98M9hXyB55uNPb5wdii{FrdXfPW4+#b;g+#x8C=9TwrM4AvVP%P+7M6>N5@2W| zD#jVfmE)=8Awt9&^!Vm7QXf#OB2qGQ_tF+3q90}-lH1mLamfu0Twp|dGPY(s>9%P2;* z(@hX`D*}%}S0XUQq!a}#K2p3JVMhy(a7rewTMC4PX^?x^x+tDMRUlh3AhVnRl(CQ& zC=8l=M4Y0t9=O^G34v&mb#M@c>g`4jJO{Z_7cRlJ?nS0-Hzknv?(nQFr{_EWMj0FY`52=w+U}0Z`JyXTjKxj{(owutmeI2Gz(4mFSw;qE%?JR(bUJ+ z4~<$%QL&l@oK$ZTe8)DqOotNgW8H^m-m8= zHf~n)6;EoP>RD@D(IcY8)s}_^E9^FI`BIUS{@RYo+#c*-``4J{e;K1B<1=4u zxeLwzFUCCIvSLke=#+yacHG4J^z66$`#!CG{<=}^e%`5qxsFzqS}vI+G{AY}1nf4u zuxYa(&Sz&G7pBIFxVE__G}*v0p~J^#$%Ci2s_>KZa`&@QNCT_4EdL0i%cjD9@>=wR z0|6616B(B_B+MLh^Ib0;PSCDAW-QT5=>KGTVJz37sXo;e*egE(`D?K7Uk3k9oUGS4 z#rv-YFS$|G(fqMwvCGRH#;>f4veTjX3+p&XK8!^6c?F({IM#l0vlg$vj5l)1{IIS? z*2Onv8nc(qTz7f@urg#`)k?$-1W4t89@1#cka%C62(1X(T^?}eb^WHWScG( zd&Ymj>GcZT>S5`_O-k5p@{pTRbq5aG|LR&^#2Kr$9@}*(S28hky7aHXkY#@@{*Cs2 z$BXehc9Z`zk_rC}_h2^OAKQO+$@i-cXtpjoYP8P$<4gnOZKC$$Hj|P=ksF>;vxV?w z;I`tErwSF5UrxWNAA^|eF;2I^9Ci?v9P?*d(U#bE=p0;BD2qPG6RdcXFtN~%=ZxQ- zCWT~`0WAnYE&sgyz@$!xeO7-@O@DTsT=HTTsS^cui;QD!Al?d|eRP#AJy$1i5N*8> zZcyNQ8&!nxgD`^&Sn>L~2nLlw6Jhwie69&Foi0UGiB-JGYCm-$++QDCrKF_Kr?8!eGc$3K#&cxw+zE z4K$=wXVvZ{F16;SS|{k`l;OZ$8|+a8UftKK5==Z()qtSw(m>`RT*P6Fq+~pVQd)~4 zYL}zTQ^2w>o*dy7BpTI=l6mV*D2YhcEL#KSOc>m~&Cdr9KwQRJPW1`hEdpp{s+{Zv zFbraR(+D9*h|b|&RCLBuQ$`A3q+Cv9iy)kan#tVK%bH52ANy{T(9Zg3XB8`XqX^F< z(i$=YBPB$!lNFPfK?8J>af-0?tYWGgXT~2YHmI_*^5Ctnm(Ue2U)wA@09oRHLZezY z8i^4n3Z<1wV1Ybje{Ms$zArTkY!HB%d>D=@98uW=GP64-oK9=FpJgXVTy)l%OWKOQ z#2e+6%0tu%mDrh8W~Q}PatQr&(31=lL|2Nvq97y)6h*_~tXi{PlArU4yz}kBlI%(n zn=K7NBGR9id!ukqJEQX=hcrTv7?jk`#Dc6d%;Y1AAr`wbkD5IhlG93K-jVG=;$AVO z4&CF_tdb<;mJ3D)02`_)8Y9+93o}1y{EfN>bBY3yt0;xS-x7mMvKdyqGE%>WMzfvM zQE#f7tD7HFE*Uywp3GH2?JbhV>KVg&Xg&x{9{<#7ZEmvmfmRDsYT_A1Ad{vKD!9?a zfoKeotU{zoq*MV1N}ihcp^R{R3bWNJkbVTu%3kB%<`F9eh%t)NjP@QiB`e^Y$4Wsg+-cmnJ&jCav+^M!J}KuqwzdoqAt6w@XDFP)Axc;%DI10Xd(+$5 zaDJY3!85-LakLIzK0(C<2*qYng_%?;4#dPGI1K=BrX*jAM2kD`qEbP3vR#@Bg5knz z$8vRud|#Gq6NcLXHauxBgrSs{*csvKXtKGcO2v&23=BA@5l0g!<3IL+&1Voa+0*%5F zgn5#5>JuUnZwEJ`Z+L0!Qdb@rkwWv$vq^-{*sDMSgSs7*`+NIg8UzqrgTR>s zr$B*hBCHvGyz$&c`6etl&$eM(AUTv&BmpFrlX#*iz&vH13bNJ zAv}XZ61rUx-e>|D#Y9bLAPF|83E5jWTarFONQGIZ6Il>aS<-Vum`<+=(g7i?1%Yxj z3y)94;CztcrDnm^auE4}WFjP-o~5E8b2E2Q#fj@;Z7HpQc2!|TBV`grD)iap4N3$*xjEMH8e2z%mh_0g4hFQWBatdfq< zOPe&U-j-RrN@>kRQ!3tul7{4oX-o$PJE;*IRp2iz7pn`%+a@^bJVK8Sq9+r`M3a$t zWf&bG+i+}V96Xq25lLpD9X{!kKq54pQB}NFLJbkDnj)s#Sm9-h9&Xgc>9j#f31osX26TlNW)c~+ zOnf_5A+{_v-0SL2D0=3gn)xLyyiBTCDZ=wXASendo+7Mo* z1myChRTxiW9RyneEpt-9VIr030`O)MY33^*OSQy~pFnX0p`T|hrU|HJN<<0>w(?|- zWNTWUN+W@RWLNI7Dz&+|J0C$n&ck`elyWK^B`qhIqVj$I;)Bw^@Ze9aBCB_J{Q3sn z{%-_kEO@M@ar4ZHo`m5$(WlmEBrNKLv#6_PL#>1x`hOavowmGfrDp3G@=U?@PSPUC zxjo7yN2q2*y$f>;q`U`z+cTFS&f44VDW79vK6b46LOzk}unf*>X}KB{lx{%VUfjpJ zND9&%4^94SP{Y3rQkC(mDpc^$Qkg+(ecr+g_PuFUOZ(|yN{ zm$trNXzkf=zv_yLeHOAdQ%Hg-&#xpYUfKA#W6MH&^h3K`qoQ!pxy|Wc{ax*NE}N%c zy_x?qa%gtA^}V`NhiSLJ24+02BX6So6@WSBvE?PP+Dm?R#kJ`@@&C=bi|} zlaroCkHQa}xo@CgjoTLkbGaGqMS8sK8!P4?u+{o6+tp+|J|^M7ZWvzuy|N_Fn8vS9I||C>28cYshcV_xn)( zuCdDae}wo>|HgcBOTC|LdEqi|`^_3=r?eDfu8W>&3VGqHd+?lrRDJ(~I*)?Fa5{NP zHoBw)e?>h{I=jP~Kur$?a#=)EfX^Uec~Vd)4FVLc(%)A*qcSznBedi~D@(GRPI7#a zI1~|u#`uB&-&YeQAU-kD=nUe5z9T{Uc(OF8K%g?k#y%$?^nvu>&Y;YjhroQQKDj0c5jhYJ5Dye$!OW>K0x5v$aB?s2wy|FwKx}ne_MJREY^js zZzGQnpaVjlm_#itCu0}@oZv!06{)l#9jLr@*@!}cSyno1w>TQcRN;Vu7`zx@A<=+@ z5RGYq#biA1gS2OD2Q$-_@METOB?6AC2%vG$0F?m(EGSUoAT{FUvdghVb4&B=noJ6j zk%sUU(_H!fMg$}=fFFioVO#}hW0cBx22+$a#g{(ov7>u>k*%veB(e~NEH470v>crs zagP|ra5fi!#8Cl9jFFn)`b3f&M6RJWxJDtG`z=x*l!?tsV43hML$UVrUGS0tD@S3Y*}z zHJXj8Ol}8VxF$T6x_g|K*A}qkr7%be5nBG`?XPHhp$j0x*Fql9g(k9zvTcHb(dkv| zEi->=najP7|CEp4N9YZq^kQjZD$fW+Q)K5}0HK6{-(4)Mu+s@oDlgGSNC9ex5}8J8 zq7$NcHbj~Tpk@&vJq#SDw@^1@^NzMG>vgK3obz;2q$`HXm+kUId=H4?^?34xLWxui zjAm4G%fGAm)D`sG8qkP&s$IuykB&Lmr%93*6$`U;rtVjmGeK$*p3Lfoj*pYdGa@ zVOn=eW0OcF*+q2mNNl-rD+=;t8>&5+CdL?HuoTa{e)I8BY9~!T8I}{eWn;b;GC0Dk z4>L^j#?Ye40m_g^a~;{{$D%ibAm3Gqt_%dZTL&ISCyZv@o2t;e_ zNVXjYzRkg^9dpF3m7+h&<5craUD3!Wfi?;gIW^*`5tEV@%}0WKe;=*_F))z8qInRg z7P6strctp3Ib=vEIfRq_DV#(Mz{2TwT}-_Lkv(Is1iW*a5Tv=3k%(zj2GN;-;@y;R zDey)JqV4pAFrsH#jHegL!fHLZazFsG5WxURAQYPd<%LjHvR!UrdOoQ@Gh2-x8Jxz| z6oG7_Y;~ZKY=S1L+yg=o#~(#v@$Ncfm2^k~5Y$fxBN?C&U?R|^)pU5#??{e--%Kko zRa!)6*cqRoK!tMqUQX5Q-FO*k8qJSJGGcA0_ajy0m*aV8U5tQ;O%uvfxMZu|CIpSI zCm`m_X|YsvES;M)+M_V2Y%u{SS1LoQP{l;52eDTU;l-!5mx2CeLrX(wqe^{=vPccm z7;cPq)?%QEBoPfFW}ws}xF`Iqm#!_EX@;t|`{T#tzg_dJ;KAuAG0{sH`c;ZhDITsD~A1<&T!;T>*w^7!FLICyQ|bx=p+KT(SU?BS zMw=<(>ejiMx}snZ8!Q?TOh$O1v2bo4fo#K82v>=RL)wYyj$Z5nMX*FzMWa^pEBRs! z5#)-f_OwJnnr0A zyI2j<9g2%j3IQd=HH)iKSesEd?Ss0qF}rJ$%a4REq+hn`ES;nB>zDsrY^L3MhwnF5ySHqPY4^^M49n8#yh-ow*tB(W( z10#8gNWo*b8y{>`fSAsm(3*yM(l+HGOE~WSa>;gw$PelTvUdjo?$9 zd`o}SVlPRM)eH5C_%YpbB>G4awMXW0%k|UyT{O_+>valG*#3n>xBl+re`KFUCIbVfQ`-HKw?ouOZ{L^MfZME+1`^|1`_cmqc4w^zd9_C(tfIV8iSKWW- z*wKo(&X2PUF7P#@Nm`?L=1n-yyJ4|zYpufxei0%!AZQBmb-G53bLz3$EnZ~P_TI`X zWTkr!c)Ra|`rBH~W2dw%ZbdC!kpDmPM`&M4<2#F4U>^#U*C^ z^43YSrN#0I5w|MV)u3M}U0=;uZwD{hqdx08`g*CdHaTPJNTpi;Gha)+ub+Euj{Bdf zy97%LGE4k+_qOTMzC(xC#~y5arn+D2#?=2Km%XkCM-zQJb21(1zx=?8a_P`=6|`N) z+Y)}2>#owZ|6AGYM5+J-_~|pn2Qe&H&C*-u-w$G>mp>mMFiH+?_h`=!A6i+WacIgl zh@f%kgG(#FUHnsfQrpreYo!)zhi1st#Y6_$%r!Kn(=Y`YVr_f^`#vC~`F#N9^X=-p z7KiU(1oe&b;l|q5YBQ-QI~dqad;iPS_UhRZ>w3kh0~$N9HxuEi^SX-n9^ARMF#0mK zpxn!>&=7@lW691Naeu-8I3qud?Z>nm^ zFLsa5D8BKi4`V^-WfPGCvyhPfArOm_FOu;C`3`?{ZRI(*I{ij9ah>jVwf zttS`7m7{ohq|6k};ybslM<85qXA_mq4sOD-H~vfJI#hR|6EL!wx)W721TsH-Wz+FxT&6Ay!W`J5uw5Q zVO(@ePq+1*Mxz2X8xPHa{$kKqIv<(YpLx;5-A&26DBQQ?WN>q=e7oit)NtRdiuKY< z9*1|XyK092aL*p4y?H<*<@p)!ap``)@g_lswn3K7s#U92-|MQ&YR_amxdETuVQXvH zxEPL^-hXUQ#bNyvKyjZ+1jaZ;lknOC~o0mxT4C-)wgr>JzX z?aY`(Lp>Xw|ERuH3|4M#s>BIYI$5qmu7{Ot9M^pqmQ=(n>sXxknj}7jJ4-w3VVPc4 z+#?BN?Tj7ZzFzi8f9H1Vw9Ip7tyE9m2d8syKk7{RRDaTTS-I6#RE%mw>!Qq=*mcaz z%MGeooAoXaoHX;L#aGv-y(U%Hp9r(~v?wEVY>)Tw?#5W-7M+H|0}of+ICu7B?wgRk zev4MWmgS_E^p7Pzt)!Fmhf_x{Es9he=toD=a{5Ey&BQ|$G+WXJizs_h!KH8u6LDe>pnM>BS!Uc(DP za7-A9$z6M8;U7}%^>+K0#Fc$f zU+QA|cN=~CFFVmP{u*K>^1o&4+(F-@&!pen7+Xtv#WAA|yl8Je>2A<4lM@toDfDJR zs!D2?vWxqrE8jXQS3%w(&AzYH-M3m zkG#Kpk+U-3&QhGFjh4LIYIyi(--?--C3VM+ox5~#s8gg;T_Ua?rtd#@^Xi3f47#LX zIc|7ki2L=U;KBKS!Xc2Lzn1@Y<$vdVZS$8a{?GFpx8J@z9PfS8_s+dXf|L=%4 zGzI3&nYy_<&@-3U9=mzPU;y@BXfGUM)9G&Pd<#>CmLes-`7VBYsY^`%2)f)D0}ymE zmcbhec$3I(G^9Z(d3_4#9Vmh3GD0%G`kP6hxcWvixCRNezM_%(tMilaE25F3NVgOL}yA z`rLfk=VdvkJfv70@#*5~`x7b<*g0DTNVXfk$|8-%B8UM*s3sbN7a$2JKFH@$g*-Bb zNTGB0uvsLvawGW^zC@lya)uwFl!`rzV4x6zD}z(1MkoT6i04`nxHh<5Ty>4h3>A_@ z(|3DFeSU@-mW@V7r2u?Bk3@&_hNBwir^dtJIfj1v-Zz`9q5!JA*(cf)0&e08*(mo~%89N~B_iP&uIy z;52Nu@B{@Jt$WKK(sm7{DwUW66acWTn-ey`<6=4;$@(NRtdx#sAww{9Hd><-epCU% zus{er{U5~itkc2%Vi1Fm7zT89DL9i{QL@tyfIvjk0s(R`rZ9ro3BYOg$z7Sb%A~gQ zZjm$wJPeJYfkY~ephN{x#ZMAKOw)>W+`}}U7|6S+9 zUF+UrvBHGtZmv&7pUGie^9nG6Xpd)cgZx*Id(T|DdYIcX4bME}-{ zF-%wdkg3yjwi}Hk1B5LxWtq`w$k1oNDB1?WqpCfQMT&{3vbgE_kwEIG!gC(ns zm_uLtjL=HS)&43yS>RRJhv-D9+D=7akde9-5Cle0jI1P1hw~DoBV{0MAXId`LqDQA zBbG3E?R4izaHF2)ltKaAt3Cra#}^QQjp86R?=JS=(t&P{!4> zbdaO~MR6QM5Q^B160v{+7m)Sk3DZ)p;~DE6B0E68D zGYhw%_2n9ghMf+2aY(A1uc31T;@}2C^4MsCIb6UOP&081MOC;l7XnMX*+i~vKO|SJ zP8OV+u{RJT9f7G3*~+x%aX_3R2IoRtilC}yK{h?%lQ^4W1{~&28op~l<$}!1NkWJ$ zgHZy3sg@aF;s9|RkVz0_^sT7>wgnt7xl)L$aXu4llSZqkK7KCsyu@JbEH(#*II&90r zBf5*V9pm!_W&}sS&l3kU0db%S5)eN9Txgw#!-hXe0#a-m9fe!fiy4E`kK3`kP8xVvDkq|XVBi5iD zjnTTav~a3+BLUqJckqEA! zLPc-{#;NSZp$0&_sJT2MfB{xmUOkG8BXVE8XKR=KAtH6rS?*MwK2Ml=^CQ0f-NF-#RlD_5ym zZV*R)CSKmAkWJX;HCxtebs!1BG-~}~dMc_Kz&MyiiLl}&aLg60rXrOtq${D!_-shPCK!1>Mrx{0!Fjbwo&|gRxR!H!#69(y<72_=!_#izipfDVrn| zhhvE9YHUf4o?B7-V7t+UTL<(yNHh=}P~azoF58iQN7M8MN=RxBiXcX8J7(!u^L;uKuvkJ8u2H;X7KEM?DGoGHjG~ zr!}eIWowhnuFyvR^J6`ddRkeR%(q~cA>Xz}QQ$t!b@Y<}};_PX}cvG1RV z`_H)a;ot~j@X^(VF}2Mbp58AQ61ifZZYza(KabgRQCVCPg2^y4!Tfv8$SE7;OfRnDP4pL6=l zVFkJ3?zf{;VO>D{BF75m_={P|H|u=FbphiXzfKDY;0xwFo&67R1-}ETe=axbbEL(8 z8hz!zfUCh<#qc`3eMe33hk+|CiV5RFR;9e#)e+XFp05`nbu-($8>0$7S9l~pnU&Vj zhq+;ZcS-(&8|9A|m9C9^aeR*^qHo>TAyI4H!P(UX3s$%eVwH}(;o(z0=e}#$^aIj`A&C9Lh(|4(Fse?`WnA}cOC^y$A?{{Oea^ZD$i_s6177k_?T za^i??_n!7cPghRNSUFudwz%L^@#+owAz#i$7(-L`ot!dwT}Z-)^yw2+NnMY7E|%5w zjrZFWnLjw$cR;|btnQDwt-@b6Zp_-3@uumo_IKkBUM=ey`1=nbDU%zt32SMrkhg!v zC&gv_axj15!PPDHv9~@{{&Ul0{XKWzWBtzZpTxiO!+&7u>F==A(L4HtQyZf*?27I7 zs7Ie)Tzz-Cch?8U&0qc6`;l)(oScnMBhd(0RT+h9eBu+?xdqioh+hA5ppNK5no!&f zn1Ea$jdGAf09*|i87iT}pcE|ADtoMaj@injfD#u& zv7s{Xq9yl5Whm0dnczsc$9g@lRA^w83BajTtJP(l!F7c_OgtXxny?s8`LVi^5a?X@Tftc+*76Gl=$}ZyvXm zcR4*ItJB&jrp~p=Vq$k{Ts`rKAz-IvA3QmgcnaeExiiGl5qOgZQ>jckS4Oe5Bp~VO zfe!Slnwv|&kjjVSix=mKSXe#rGk`NHGx~b1cgj6eAni?Gm^Mo;({jGgjf%w0}>=KYOp{v$A{@pUnTJ%DdA{a%$ zi<}A9RD6AT>bhweBu1fkGeBBEE{g~$FWr*eEo||#MBI=xZ)E05Y;ra$)nrk`!VoUO zTLN5-spS@MBSK-JI0%hnXEI$T^wsb0!Z{BRg+fAPSpFyxDD$XN0H#0<5~4(eQaA+F z(_LV~*X@X#qh939jLD!CCJGLkd^v)2ltmJ(VzEO%-p$-dtvwY*1!jmMpDLtNj#MX} zm_c(WX99=H#vMspnKtW{V@q5U7U9jYS|=oVRXRH9`>)}p#Hv5FAtQxH8}tOsNDS}G z4l3AlYY%yw*14qSpq}Q!$sMLBlAZC0d`N3&F6JNz0-6~pg*spgAt6K}6Pd?P7BYN8 zks>^UwqG-%f+!e*64eL>nT#FgMvhd91YmRr7QuS#3)Rezjg-kUDut^#X%f$BHf zf|W3bT3B}mB%mPTcMM=eTOJ?k26AK5a{E*om4))ns`h8QOH@^oT*ThlK^7VbBmq>( za}c~4o6I2CO^H`=dK!{j&8{`7fDJ-MmH|?OYbo6zRbMehOb$#PzK8@2XJ9!ziFKiK zaz?PjPIS-cy0^?zT-+Y7Qn7PH0(C31?+_iMW#nj3aG2-SjKcp+4SjJxQfWr7c`q&m#Z~WM42_2MB}Z2qHq&vEsJq zyY((~-OVL5VQxU2ewWeqJo7#J*g;ant1xd2 zQ3_cIRnW$QCY8Q}WeRdc<(_?V*@8!G1}Yy6#?9~&gA_hV{L>hVEDi~{kY zn9Q?+Av`V5gCSyO2rIWqh{ATQ5aZ@bFdP!d*|-ViyVT{IHx@L2;1N>Y%=YFfqcMfRf*>jb zB*3^-dgCx8MwOA};0^C175E%3$IO&Zc8m>BY7-v$u-j469F&!h>y%opkc}f81Tt#K zn7p{F?s`4Ql*Oj`N^;wAk2@n_)>Y-SMcSuy+T?ivY<$&eG`6>IOc7ve*n5ZD&W%SZ zob??C_KYZ^`vP^l7PI_Vj|r6c>PM^GpYzZ;=r!&gQ{9-BgEUS* z&zVccFX9YOslB$1T`}j%l+qtdjV+-qwEnuBoYd>%;zl$a<|oo*)Uki-k}8!_TA0pj z!6(zo+{op1S zgWs^^j8!eq66VhNnzSTogXCoD1>a=G?`aTwepBQ9>Y`|A!XFFf-U;|4=MT443gLnH z>mi350)qQ-9!`4k;cM{dn%86Qx38Sz z#W?c9A9T^yk#8e=p1qoQY{hP(U)3Y;%YTd+g2sKDb1q}d8uH_8pXO2PcF0N&Z&ROD zF8g8foGr}@7e4&eYx0<-%e9P?Z_b`^Ve|sOohzmrTEK~SIvSGJ7$$z`y?Z5cx)vjz0!NNf7{_J@l}7En75?-;WgLZ#lIe1 zO;_K%c*r{Ga8IzUrF@x7U&Yk4sb^jkNiK}~2aB(yW{A4J&)Ih+>cw|&&ix0A_xX;+ zorXs5Wi-Vdd41|)`U-PsHKYFEBoeF%hV%rNnSN&3>IOfsMo z@DlTw0eO@Ha1FO+YQs1>%>`^>;CJJ=kzl{)D$hhWV|!iUI!FDY#w#8O!$J`(d_^>f z42|XkmC?Rpl;`PD>sPw7!jYUdGzrWTSuK$=O9!TBFs#$gsFMU$l-|meC?Yxe6p1J> z;1IHbYiIb^7nTsI+-5~}8fwmnu~lL5{-yv17FnMMN3(+B;!oqQ=1@xz#(;Ks=T8&$ z(Fn@v@Vh?u4$nv^p^2R^ifWOs*XCI)=Sm-pcDTHvX|bXIw^J^G;Y>lrY)#&)We0!BcmtNs+@BqL4K;iBO+iCD?8w0xP(GKkMU%LV03Q0}VFa(pl~7BHmJTqvS? z@0X_nKT2@A1uXX>0@`_+X+RQ!&71E;xqW+?3$*>I zhR>{4X)ghB^4+YAxRiQ_Ts-8XYIk8>U8NW-kIW~|X*jlNtaDIdr5Eklm1-Ij$fbQb z@-lHg7G6rb*dRya!kJydKzeFH-r=8#Ld%jf7wQAZ<-x*e$5UTcRn}htgAM zQ)F||+N{sgkYB@HI33J67^@-RydH_l)7q?2aXHNCVVDjq?Gd-p*|T%#>aupP@Gy~&H%sFEhoA%A|<`6{w?gu<-m(F%W%;0nmh86ua^Y-=lf?q& zgh?Y|X&^F$<<3DP0k=MIFJa_QW4ZUSvBloYO{+!kQl|BVGi722X8gCOB|H0QWT`+- zFc8xLQ~bqL$g`8Crxj=Bg1vi@ZE3qlov@6L$lQKAs>zX_zs=KbjF86^=;4-pZM#P0 zGP*+^+_EsP<|^VOhv#xgZ*Fy*%GiJzS!e)haRanK;^MjZD8_Z|3Z6V`=j8~2!9SqW z!Zk3wP{NYR0%W8W#s;MthH9psgM+5RNW}mQF9~So+{7PA%rQxTi?EvGwZRoM1Q3P*iqidKzoh_4vGs z4MWwgiRwOZrK%loucoBwh!ZH~i=ACV%gfs42rZ||D_*zeK7SrA#rT93rZkSlh)RPR zZ80DlW)W4*c)#c?(8HKVa8q!VD1>QiS96naf}&vJAlcHchNVt`Y-b-9Q%vZaeq~=s z$PDTq$4Ft@&~!=AH8es)32x(wXb-f(4LAFnxgrR$xIy-zG#?e!3~UcsZ7P7*lpJaIS3AvkoXa$e!2*hGzqlYkj{S<}87;=OsuH}(k=4)j zvi6mZma)&TG;`OwW->~3PBY#+s5I6+*1bpCm&O*jGW)zFGCJPty5?ZX?&ge!aG%x# z&zLlxIEMFy<-b$jM;ud&k&;Kd{|c66k-K6BF^@`h7_(g!NrZx}kwF*T zS;6M~n)W`Cf5U{7e_<*f1rrwd&pAua`v>-kzZ-L?dv3UTXMPxQce(Sh`Bir|(bVrZ zE8eeFe30oTB~a>R_LC7;S6o}4^L6w3Uw`(h`+6bxPVmJABjz~yv^${re=e`zeQ?av z;5CgAn!Fp2*T;`ei~r4`M>^-PL7yhX@ot373|zEoU*VrK?j1^AOSSKvoS3q2u&ELK zZ!5W+e+9^=0u*i@ZKn(Vg-iAG9^PL4^6ta8o^^Gb-`ItR=XBQp(=^rp>${%RcS}}p zJNRF!0X2mee@cHFlgj8$(MQl{HT>D2tp%o3t6tz9y=ViM%lS#ZMybH zcv*^nTa>m(!x~KdUrsnHyL&P!TYLZA-#+4 zoGbrmYP)I64%Pp?Fyy~tA3(+4GGSckKNP>0|36XumX|@EC(k51G&??BC6Zhr%&(qm zaQx?$658CrJ!i`*_7~h}`2UpWw`xTobYc8DhIW5j@SN5&H`ZBnp}yI7dU89j?d5RV znuh|@rj$uX6s5$Db{%lElzza~YoTOLEi2P2^tEf->HYiNPX2i5)tmUDkBhx${BKl0 z)2mH)Zo7>fJK^oq+W*1!TOhSB9}-EP>XonVB%hb1NG}c3|2Ak?pNW25V_ItymNq9I zl2=}R;m_GKa%o=bx9G}^12ZJkd>eM~Mn9GK2Frshq-LIw_Wd_`UD9O7wi!su4qXWY#7gjvRyDl`Nc&Rps2jbV6P7<-q1?H{Euujot8q z{&0H5wQ+R~&r9+I|2BWKBnc{9vO&f z{g)y+2WIP5Ktms^PWm-W;r{riV$(XCuVi^aAKoirH}YYM!;*>9O-&n9*uwuX|E>d9UL>HNt+B%n z3U+H-d*M4>X-;}oV#lCWZOTR08}o-%{;|GwR;%?Fnc+mZ>}&GKm>-`W|KNV`#uMkN zXJ1#H9n^m!8MNrSzwAtvLnD1w8kb=>KPgn!XOd~pD!oIC`DyKGXE%Jnx}^3L|u^Wsmex)~!_2_OGP+ihO^ zvGiCY0~6Z#VS_nKensTR+TWEYSQFYp4vx5EeHo|Q^7On(mF2R4FS#%*`}dbCjo}9) z{QqTR+j&u&efh-L0(hvs;XRIak6k0p1+2v;_Mc%r1_bG#p zvD(>hacOrp)8g(op;Tg5>5zh<1^&%b)VJ<$U$f`jt)`K0#x+%In$xZ!*ZhxoR%D`$ zdn#ux`rjFZUHS6V&SUcWVH;=*{j3MFG2eGBAwz0s)g~;tw=7SQ^+TpYkv)<#yZ%VF zL3gET{%h}z9+zjPa-3gZ*?n|ba&u$9b6ND?({I&2zpI{qbxHP=9lxC3Fwj>m>n)v9 zmcVTao}VrL$JRFdK=u@%t6Gf)I$hm2?B$Kf*{i)j+{`Y&m!1DH%Dg&CJUu=19xwX6 zFlqj@Z3j#<)d!9Qop2aOaB|GvmV0>_g}}_3$&xjzlWhpzl=miq?PH7$mj# zQP=m~@VhZZi`?n;e~Z@EeeYs8`$TU2qh{_d<^G&Qy(esK+k2ZoKVNl!z}cS;JwOby zH8iOzS{q!JGy!HNmKV;~`ECq;++M>ikfZUiI|pcR1FPBX4h(Ho%T-Z?|F)sE=<HNZtT(*kJB!qEJMZ3~qN3ls^;3Govn<6|>tHO$m~piwjudZ=V#ly>XmT~MFo zso2=+c!IPU)l{E`M=-fOH2^+zf%~w;;kwo?EpFx~oSN1RX|^~^6G1ENLZGA}03}ER zjjBol9-fZM_xJ4wKKt1vd1eFp*d~=$BSSz+zeDwAsLrMhiREfgYw$*O+_>#!FJGk4 zVrVqkjuB@VKFHyuoSWkqDiVl%@8?J3(HII|EN4Md(v+mwQupo&fn%=?Pa4me;1)P+ zD!q~x(0JjG_{s6FV!n;4YqD9Y%{WzB&}ah6FbtH*Ff}GI!M>2F4cVopRjP(60#T=PA>~6}8P@LOPFb9)w>Jstsc$LNOPI1do zW1QTbq@#TWT2wVR;FRcvG$t0aNAJum4Ab^S5sR5(wsiv*#2+(rjWu)_s25Q>>mZL2 z5A?;H_RqF1s|5=ufOf)%jZtY1F)CW|-Imm_VOmK*1EMNW)$oDG(yLUhUdI;roytatvperlW)NH<6FMSzJ{W&diZZ zjMdyYNS?$ccTe5v9rtnXcPb~{A+RE9$bAeTpB}(ns>L zCaEN`E?iL}@9kcd#m&G~Ra3{_pbgPe20>&8E5@+BhAnlCN#_7^CK)$zXvrF)8zBoQ zN>eg~A`*(Ar$CvfVReYwDcu*>*|7<~hLUDX$3=}`sLC#(4jl^QsuFN2?2guw+E_t; zMqo04Zep;c0tR)bq_W7+#dEW2YWKBJ)5CPpy%}Gz>Cn*Rl7i~&?b$&%5+iT~3!`j! zd$FLvuF%FuJlcNX#T?2?ff1;0*uxe}c&%0HzG$d|i&sH38QtIr4&~q)$Ok+|Buo(P z(jGpSdS4JLVp`z zW2IU!E#?cODx=M)p}J$int1)y4}_DW635ns$T#^|B$M{!eCgm)s789&mHUBHb3)nO0kWHAlI5o~q4Cr461=21*ZB#xtI z#iDtPa6xR)a|xxRk53mewV9H<%N(Lakl|!7NJtaLQV|*nQILlhRBH|SwzV$fJzsAF zc4Fj}U=e|y22`?k6~?S&GPJM*!D>}$^T(erp;#=_yyCVq=I?1WVk2xe{2|-jOz{e+ zXRAcO3eZPGYKdl{>ZRlq#GTP9A$C%)K$I1qHupz}5_v54UG=IWk`**{3CkAf(j>{l zB_E6ql17jCE8Z0U+X5O}MJ$+$`v4kD-AC@@pG(I)Jkxi&CwF%T#B{9d=-fm(96(?I zfl?XKO`wrcdOeuY5%5F+Nw=?$y^iw%BrEWSy{1}?C}0Uj@y8HZ&*i^&>dJo=tX|Yk zo!O#2Ous`F>fae-f$KMa@`hIUKX}&t+v86y4___uS-Ej5_s4G&I6*;vKb$!6$C%%K zEq%7s`j=Ym<5(j){Dw6$baToj*`vgupJ@KwX@`$dvXURf#(q@>?8L7#_M zw9HZc@Hpwis%rS{@rt^=MDL*Hf%W?fXh#~vGN)l_Ek*ro2GOR_whKW_r+GiFRl46w z-hbuZ6hr3~?-M(J%xTLE@A~*-UIn-QAIs1Be~o(}HSUz+0rTlg|J9)Q&KYx`9DU_V z_p=e-&_mr0o%1HdzH|yip3^j$N8PPQSwq~9j{2n}BjTn_P!al6C>Z^z?#7?^(sjD+ z6^*aYLvEuW&zwNcsjp?uue$Npep^7N)zQ@_O}+`6$9Qu+I}=Z!7nYmagh^x6-i}3o zuSUziqWb=Z(x*3m9!;a{+;kRd@`mtv(7zT_uN2$c3_pNaqqa`#y_9$k2QY`y9SlipTw@f~&PZXSa$Ub0B zVNSQuf9i@ib1vQ8e*u0k8aS&bVNl9H*sq!joVvab<+~4f=%keH zQ?656z%Wa6c%A?>Q5NO2Cn~ytIM13!tPbT}bT8*kC&F5pa=~1!G_sXm3k?t({9S@K zQKuFzoiXB8OL(~V8Ws-(+NY5$$`eWz0Vv&ENFTH~MV%TR6rL&}C8<1jAf?y=G;E0^ zjWQFhzBrWG$l!D*xtp|w1+32%>XVvHSUk+~V|ZeTG6_U>6{B5nO96O= ztmTKO^O!6Z+pADSAY#zL30e+h3bdWx2q@A=wr9HL5Fu_3PW(>LUi@r{tiyja)>74h zekT>{xocQbN`^5pvpPGA&`W3+kxPN7>CuyVw#7OcySNSU^{uA0IDP;xGH66EmV`37{k;D#T>X(m&1^@YU?8NN8yvqZn0eD1Vc8DAlM=ibO|%`5G(=nCl#bJ_0vEasAay zgJzJ2GnG{l(0@Bq^4&tF4zSj8)r0zks5ZQSbF;Qia0W?<>-sl@;>rk-MhV+Njwm|M> zv$!&V?!hS8%=&;zDId?dpubHNI#;E}xYp1dgT%{fQInAmH&tlpm#2xWmAtg_8ER5s zW0Mt5sFYq6#>la>GE3GNIv>(X14=OMCjMyZrD8y8YM){lhl$oWN>-l15i2wbaueDD zlRHtD7_vndohgnKpPMJOa7UpP(I7=a5o<-To=1ccR)2$d({rkv(F}GiSNWqB8yuj* zAS+?(A_PkqN~SFgmZrw>ZIW;`&oi@?!&g?c!{fK;2IAvFcF1)o#J3g%a6 zFdG@9;*ruch;E5z;AAIBd0l;WAtYGK7v>%GJPV2`1?JkSf`bU}axBq}l!VKO)Rqzw zbFOmLco{(JWjt8Kd};&>=E}u7&0H@Q+G&B>F_%6L`Km-!cyWiwLSBprnFbo3P=D1+ z<2;F=P>37G0x4rppw-O>mW&Urb&$DyNnVs>9u~(GSn^Wcs!G%gZ@s4YJ~Y}HnnU}Q z!nC4wrWc(zX+Eq}sh4GQ0*+76MpJZjrf{ubq6CSsoSz$&+wmcw+AZwd&hzV>Y0af0 zkDM}e!mWglK*zMZSfvr$yVy=ZLEUzBEj&`1I$6Io{Nbl@%oNs(K;zchiuOvWn(by~ zc?TxNx^qf745gl7talyRq8`>ECRw!_O+E~4R!%!#Z zpID}d8TtW@`>JJ2cChm^R8gl(P7~JF7_$^&Rk>_eMF^<%yC9(iX{vYX?HFox+n&j= zSmH-8k#H8KJx#Pj>W=ccJU&ZphhH%)X$Jp>6l*@wp?2YUOatc0$nv?6tdM-b&l8OA zL}iBQYzr6T@_ol$_HMO~ln!XuCy|s2XRGL_;|&mP#F}MP$M7CQFQ2 zM!Qnx(LVr-&kuNF@s#aEOJVY-U4QS*UH`%tgQ>ka{rITK^jH4_UtHS$bzIV^H=Ck= z*@DFmTz>c4&5K9&d<^x-B+q_1_x2GVXr7oz-jYIprMax3-r$Hxh;;#_D)KOg2V z%uY{#l|22lahjdiQsAG5>r65emQHDJeL#_yBPMh3>!HToV4 z8*P?P&02iy{Pbm0AJs?yJr585H4g%69)5k(BmJM&pZTUs412rfuhD-A{pTsy zNBDMEY`?i#t(bVCXo7Cru%rUy1T;$!^lHe*%n08BLwuU7rtDUB1%VHrI=r9H@Q(@$ zdv@5o>o@0-SBNR`b1F!fcM^_T|7Oi3?bMZsU ze%RYD-)8z6r`$bJSHADvor!-3{Pte~!&JcZt;z((Uy0Ppp>wwC2NpkDNgMu?{!8?= zDQ*{u-~C2!sM?Hw8JF5IWs7F=hr*~qH4U$%$te5potAp>{ui-_^o7Akmx3NA#vLr5fYW9Q^oyX=M z${Yfm!;`En9$#k+x#jHFwzv3RyMWr>iQ&;;_g0<)`ycQvP~+C)J=n`D*bhmnUNJ!>8ht zopU=jFK&GLxc>3UPd!5rWzWDF1A7tHo#m`6PsX0?Dz?)q3;bi(?U}(e7m=c{IXVdPsX*mSx zk56EQiOEgyl3R_NW)G(v+v~Z{X)?Xn$!n8l-2c(35iEfNP`}_}1%5e7wu9#CWjTPI zq6Z0sg0yh3&c-0BZ2E(UA9LPWFl&=T!`mF$=ZK^SWjT}&`Bh$L>Hstzg;0ti-srTD zhA>%#oeZdBg8e2(f=($D;cwHCNhf=SskT(M!17T5qVrp3;yoHy_ z%@)RKZ3L2Nv#4zqnKnyh7){oPzCzcV!(1plx^rO96r&y{MPLhx$Pmq4)!$nOI`eo=DZs0IKbbX!~W1EbZyJQ+MtajVxy()9o|4 z4X3Nq&W>f_-^yLn(1kc z5{w0LbKhBpF~mT#z829}cXnOkU-TU6i6vL zwH{sTZDchn$6$_>(+E)inhz9GOHL>N-ztBxRPd;;%u~E%%bA|9qaNTX<)2aUq`)8@ zCKU#nD8B@qJjC|MA|9Im@-K7hrgCD0y9on(xBGbZz88S1cywDgf4B{JG zF%ZX57>cpD3@uGVL45IKefZhLqY;XO?Cde3C<{faYZCX2yf=Js;o{z&;|Hft zukgBdKs!~ADH=IJAepPvQL|B~%)+&}ZnqP&BF?7ZRUP}7^}haMi`<>A$}RIj&etqW-w92%_kH8Dd_#|z}JnO+oL}I+}U(?0_q2&h}H=vcGewT zN@YFgx{^!feag2_PRWCyK(n#auEPchfn1dE;lzyt55Jr~reDwgWg$JUE@sW3#k9eX zVGJzMb>e9VNGQ(;wQd|fS;RjmGl&>zs+~@2$4oD39LYaMm&-XS;pA=Fp>l*8n32+n zisAi$(XJI#Q6^HPTHT*MId(Od%VEx2HmGOQ^skPU*7=m@z&5d6iR-j5&t7;&hXiu0 zRU8_?r97jx?%ZX|(#tOm-#@7H)2)!k2v4{6YJW;Sjc3`FIw5Q%&IH&1f|6SpO&M&^ zc&msG8COpKwB*w#9ZR0^WE1SuD3U@jue}^Yq=9Ca4x9O_1WRpvxPm6 zZVx_|{eCu4cu-NTX0bRy(m)Q7g$bo#VK+15f~hf>U_o(;$qJ@U*L&vnnbG#)rz0;f zSrTJYQzCHy$C!xyd(bizU@pJy~iZOlTdLdUwjx{_A-Y0my%*Waco0W&iz>MWw6%KB53(#38M3JL_; z?ICgirFxP5p`mFWGdJD4bSZ6svZwhh|Cf~D#2FYVg`$jBQ(;%GG>F3o>{?7LRpKm| zRaL)Q>7D-ZV_4%DkF3ls(>uPgmU@^FIV3Q1z}vhYmrf-TK*|w@(E=oi!vjY(#x`Yr z3|oKp;F8w|Tjg}2#IhJu3#Cwch>|jvD=+V~hop-`@@Xw1S{^(dGF_Q|Xn*Gg-mxHS zWv6oHd`U-6vxS3!7R&aUfN=MV%HGJkaCBC-zZSD*_~RykgCXf|d;muR&AdodR!#MeYy9)Q#(n6_xD(`cPj^Xo ze`8Ay?!TFO6HS`3lakee?Pu(Ep_p>PC;~4i0n0fwb3{l;@9g1g&V<@OF6{hmV|=>f zqcqAHAjW`b(;&bYEFSh|ul01Lvh3$)%ci2S5XM2YI!qW~b)l3o z1huKcl%R`~N+p;Iww*hvr!lL0{ivG~eR=nlJgzS!YhnQskcd2^3zu@NfH4w*VMKx0 zu8#gP{J6Dh%ZeZW0ZWYUQ0earp*}#bdM14xg-^Mg$F)F7?@uzXA1{7++Vt|-yfcGY z55n4XoA2}cw!}u*W3l6D-5hjV<59|`(7>vV8()_j1$gqFE%Rtnttjs1VPwwiN(!_WxFyaStkKCXD& zZ^P~EgFm}VzzM9XtK*OyGeY%Vfls^Gy|dr?dKRoXdyn&I-kTnSG5punO$UYMvj>Je zooCwZ`Eu{!f!p4P&88mlL3=BNo_saB&kOqLrT2%mks|}g&A1)^_o4{?HB^)u>XxeD zL-d{hlzSADE|y}_&HwbK-Qi>n)cwPakxTzDy=nISVoEGO8NZxi`noGDWn662!PSxx zh7ryOe!7=kzIn{xt(S4(_;u@cy3FHQ_;r8HHZ+8eGsZAap)HlJ5m>*%(FK?O*fw;c z=ep>vfeQXlwpBZ~+;QrJo}}V#-?n%PeX(@=N|tuqvH^GQPLdzr;a4?t;l7?v#npSh znLURp2i|juQU1203sl~h{c~rX`Fpv(!|?B}#uD=R(EmsxEZv!4pMTm^RCs}0 z-FxKl!9P_`U!L83nQ6=eC}cxt#$Oj)_t)cwjN8#D_wID8>c%3mP1&=17{#q4IK( zlfvj**}W~<@KbRyb->L3{wc~>7KeOi?kx%4^gM7 zVyW!}=28w1l&eG)2bQZSN6o3U_?xA?Vfxi$9V^dNvj&U81AHLLD%VJqYx99SlNNR=a#cOU`&E zsokDx1;RDS9+jby%u`j129@s2Oi88GVRCLPBzL*%Gmx18k8bJuzi4|CsHU!ee>4dJ zlMwY}0K*_AhXaH`B?%xX)+C(7gvk&AL9If-2#VIZqEeH5Fcz{ZEjLom$l-?1#k)a>bqW)*Xc@y=W7cTg#| ze8|j1s1zKP!UzV4|9leL+S+{1Q+}_+vARxix=c>)MM|l365s{!fCv{@43o(1zCe*; zj;g$|nj>H*R9muYjvu!|e2u~kR)3%;luA(s_+j!iB&K)>sZfh!y&OtpD0^9m+>cGG zh;cy7WDTOn^?F!}ASj0DjYTdLLJRsyQbT(hlzDmo+a+|G32jI$V>JL#$&m|v)imN% zZkmdV+i`1Um<{d-U?=H8hb0U1mV%5K};rJ zZ9V~D8-Bcd{>TONVtp;Yu`mEGOrulMq)JhKAn=8ugP-8@ki&C4fMA>)49tRz&gCUV z)^e-0`cvl}JgEno=g#Jvc+zpvnX7K5ELas=3{$~fnF3OIbmn-)#fIdRk%C}%y9Xi7 z8<{rw7-dMusNMZdh;p^U3|vW=HrjY%I4yQ>scSY{oxn~M6L}7FL@2c?)T^*`N{1+a z%2*vuH`TJWJdyl>Bz0ZedsPGr^I%hzo1n9(9C&UBwLPJ`@JJY!puv#0mfl0J4>vOs(GD zzS<6OL!u^hclB=n!e*F17R`~@4lPR^v1yF8fJcJxfp#2}0boI)qAUty=7^k8+t7^H zLs=)zO?P2W^??mEAc$8;!P+MUT%kF(%l(MGcMxq2&^b~fZ)ATV)r2&v^6yTR0 z_&UG{0#lh>gkHc=FLp@oU#H>AWWYK^+8(_bu~IZLET5u)K@>6u>UJ1K^LgU5*vXWN z-85&m25(AadoJ{{`XIOEy)Bs^9V{4xG3{K@=;T24^#hZ$=Mx|voz5$dkCrAB*Kv9n z1>$i!BKdcf&eQAdi3zsW#SSL39c#B7@KMZHHxYHjb(bavzyA1K8uNkSE|)GeU&>%x zhWHL?*T$O{pTLIPNR1}-zVvdX(M-k=;BmxYNWcMK44Ivxc)+XI5caq$mYmdBgkzG> zLl&OUhbaZC3s~O`kOD_hypTsmFiQlyQ5NQ$nASkKl4M{WWVZl;4XCNW&VqpN7Q*vC zS}jTO1KgRE*ZgM3l&<+yiP1^kQh$+B2$J@gJm8XHpvcJJQ{e4VqC%^D!#>YQ4njHf z#Ki6EbPxRKdF_~i%AnGKFAf4TCBSn@id$4-JI=W=g>bt>!fm98XOFouN8ry}0+vZs zhY!o;K(=f(2a_@54qkp4@C!^@JuQSua!@>Xbf-eGC|*v=nZlxCSSg&D+0J!zm?46x z;HW^ZVZ-=#T9g!;6FucUok%=SHi!RJ|;J6`{) zPXEg>=4#*U?+;(Q7vcAE|AWw1bMGz=c_+4hb-Gx#$?M?LiQAhtkkl6>+GnZ7IkT3o zQF?BCd&6?wGsxqcUB5eb$MpAA=j?3K>f^q-7Zk8ZUf%kHzxdCr?Vha(atB+P=I;IG zk3@|2{a0Ks1C0EqH{utC66@!3zHv;-L3N?$DX09nt6%8d7{!A{MU%2lmkxR@JL}9j z6hw6}*t-{|E(p?kd*}!ky?cj8V(*?kGk4p=FHXM-v-79@psk?6 zrs@Z#`cxR5_qSH!?EeR?#9UoZOVrxsj{L^&CG^a~6G_nzuSN>w3x6+rAAe|8Ht!zziVho5C+?PKll zv-*}>_gjvp3bk8^#@@eOD2hT=lXw~+`anb%?WVBQSco#h}z5diYX~UhS+_evO zy{1g_^L)PLI`dH2-udMh=G+lj`lE;Ho^O|(87!8aPv5!wR)%-OO-I(fp);pc9;%`9 znjP-CGE?VH`}N5^=Wg8|`_|2q|8vh*_UEfrq(x8HkKU{QnKiJf_1mtWJvMmtMs7%d z`}oc0Wz~wGy;=w2O41@j^Agw20X@9P-?-D?nLqjUA1aB7krzXC@BZJYBu=A&$;Tp; zyXUEXA0Hv!J~8L3>&w^;+f-+_LiOjX6AvXcY)SDeH25Y@$Q~1OsN!cy`;QLTk(*gA z-Fu`ZE9+-B1bY7IzpkITQ78Lk5}7GGMNd}Ju~$dj-A;-rSf zmv1}VpH%TW=1KJ)*(~yJ4@s$0OE<*l)|N;7x^Z;EXGiBcHd;pF($>$TE3LU-r{#U_ zwUPrJp6h`bVlREmcabN2UhC(a{ATGD_!(OC@{G@ws2@jCZI7k3J8ad>J3D4Fr^;?k zesW!5ni&ZCtv}WTp2~S>f`47CPnNH>nburKU*0ZR%IG#s*y^LqefD0uJN&Q5d-6Hw zYd44cO|lgpm-Cv6c zW4pKit&m7tys*>#i?m~BlK2&aP_i(6E%Yo);2=hVGI!0oUpM1PYNvbumesb%g1k2; ztQ7KOjWhi1xKZ9yPUu%}+&o$T8++t;*7x?#@s!kaUYdrW<@2|H_hfsmGVt;CFGDgm z#`ka@KR_<8cMPq%m3mB86+8A+1>p3+`%2xa1$j1auT`aw7{j}c`t4v97lq_4s6wpD ziZ`i+_dKr>@}`jJ|Dy(iB88eig#T5Txr8maxhX3lg+09Qms_&pqo3S*bo7AMbcZ+E z^4p;>%Zq2%V6MTkpv?;18hi-OF9)Lw*WSL=R>V5K`FPdY+l)jr&3s^`Z~J-Dk@cr> zDKoY0)K0wQJL$6tQ==|{`qj@K#w6hXKH1w@YT z3W4>)O>GSQAL@r6X(3BW^LMImD4r-UoVY0OU&#AZ=Q1y%lO87t(@)EE%=D_9O(Ok_ z9&1FXO~j%Hm$jzX^RyR&?kwE2>uCL&=H>A7+MStK)))CLO|V)g;?KEz0-wIR!QClO zpEuDn)XFGt2?%Jt?BX)dXQ>cwV-z0#Pt^m(l6E)Ed(8szsHOVmy{#F{v>`R^0^jGj zXw&T(5+pzR#*%0O(+#gJsx3OYEZu!sBGh0A&G3u8EdAmDGpW<{uxrB?N0&$Ycpct- zsr>HX+O*Aa-|U!pH#S{!^ygTIWpA#1CU}xPe<18Y$<(!Q^X;RHxC8DNbu(P-x7%u5 z)`iM$^v0)&eEFNko%`Q3yu1;SFJ?TASbepA|Lx5#{f)`5JUbG?ACuN~ zmF`mGWieg07^<_VGkhdh^Dpf~eyzs-bn8gMjGj)9tn;>W9rcxc>KHn!VUkG z)95@lUe}a&VS7e?E>0_Q*=aH7kMgG9eLOy+@8NIIn(x%QQAiFPx#%{S0^&_m)M)Mb zJ!g7GSC^l+=RAM&naA_)X=lIkW#b7NfCq7GJ(=DD$ex)YV(|>6Tq2cMk8BdKQkDvy ziu&j|DTCU}mp>TK#!MmC19wh_uOPU%fJKSbxRLc_K99;n3X#RrUcQ@|q*Dv z;q;}SduQl>_k$ucf$ANozIkAB5SfrDNRCibOK_m;q>7JQYU(b_DLpG3fS@Yb=Z4Z_TDLwYsC@HK#6^=agL>jkC)*3c& zseDiynfk6}bBJ$m=J!ImEmJPif=;^Dn|lREc|1`4>w^r@2THykz0>nauzilmD<`+@ zL)U|dC8kUmRpMPRk7JL8IhY;vVvV@sQXL^}Vd!~CGp$Xu!dh~&y!h$T^NiD_L3dZe1~y|>WVKlSI(@=?YL{r*#pXOV2=ag z#U0)ZJT_CnC`P@7-Xdb259ZyJ!MgOFNKh4~)eOAfvU$a<{Hbh(>0`6jH^bY_ipwD! zgA^EW+cHq^(dI$G%<2D=se3$bri$ z%@9(jh!Fhjd>V4wEV)0Km7Gb|PbUX7XDngw`lhPQxjkJV;+3)R53N*8rL0d|m( z7l;OKwTkSY5hN;ku<9(Vtkl|ZjHO}VUEo&2<(VQOIEP>gv(Ij~@i~unTqIk^hJuW(wHM4Ew<(`Ze`1_@mt$*$*jJ24kz+Q_7pi7&aN3m-= zI0?YIOp(CgFBF~{|9;gEos;N;9$_n5r|J03Xjp7;nQvBCrWTAl2W3#YG#*?`4s*rS zym72-hL6`D@lg+hw!T0!?1N!1e)!eTKbH!cHITt%<@DG!-W0nO2FTit;u~&0OeCB4 zShR0oSI>%8QLeR<=nn5%!?R@Gs^BrKxLpcvg90IoBA08x2$e{5VukCHwt_o7k>?Gq z+JWVqYn#uVD#s`)K-s@VQAi-nTSV#V(f}J?TX8Q#SLPT}mzWSa@MdGri`Jajt%K7e zJdgWlnSsL{%p)-XI3C}MV3sU646zqv0Z4GWc4PO~0>iG~B5d<$T_}d?DOjvM zlSh&AzylDB17uF2gj#C-mz-MC`0WmYE`_^te9kKSExVlo4mO?TVvEgwi;Q_^0IWl+ z;owqlhSiNMV=$%!tbn@TpL={^h7m{Q z3hg`Y#1PO)qLSsW#?tL?L@SzHNtA@(OdqUPYrEBK#c??do&uW&cH?yEB@_!YHTvDS zi1XI!KDSoR=+fcwgB#v1nCsJ!&qT;cW6R}$4P92QA^d?$jd{YjX${F}`Ec9r~W9cF=b)oWn5 zx?Sr0)ED?SNHet>0gKX=0l*r$Q0M_XrP`gy+et~LL`F&Lz~v0>%;w1RC-?s>$8#0+ zF!1x$gBxG&&EUv+P(iwcECGh+^-6;S94b!XHZd4&E6RT>2;Cvrq_xJm)^|Z*p=gIp+pFwbnx^6{b}NDPWM`crgqWM`_)x zMqKW#mC9F$mJuIog4KW$FmP*F6gjXZEc{(tNvN;V)k?8)!1E4}NP98Q1N=276W9Y= zkfQ9_2GNF`wIV?seNI@<9l^~R8NXi1n*Y&&Hp~|WZwvzh6sahk3iVcM6~v)Ck^oYz z{Z-Cq9@?2rkOWO>+`upa#R~BF0+*(%N6Rx|Fc0sSkQEA4mTAsWyRR4&MV_apT3atQ ze>Zc;$px|l-$pZ|O(YD)z|)Coct-mz%#QaGsIa)qc9b<}Y@&?sc*dGA>kPbf->cE> z`N!2chEx`VM<JD9=!EVCx7-W7vZw-iplCQyr=i8=~1CIIBGy^y7 zzH8%Oi#OfSk$8@`6zRqzrUWE0PFR;l=}>s3?q``eda!8TDlss02+xRTW%(ajcaD7j zd8FIOv9l-4ZrKW9cYV<5)AnKgGKAR%`KQw`NgiPG9GnTSnNVU1cVN4%)4c z3!uFV3<~$=x#1>CFCn;nyKg`PUSoYaaI5)bQ`ZL1wU_oBZ#Ec@OAwTcNEn>rg0Ns~ zv5%k}uQ2F>KuEq+|)cWR#cSQ6dJX2kb|nZ&iXR zOxdxfCp}Y&e|UbYQWP$KadKDRo0nb|GaF-bWrR(COHR`Maqj1X`e^udSJ%H#sgF?M zM*z<6#MAu;jy#&Va$l7Bb8rk8o^$EWF_rz&)ol|F(V?85zmr?;w|{F+sJnP_+`=VS z10TIvZQaxrzI&7Cm!EI$`>1r-Ue0)PR`r(l3y?WXaNfDMpE)b~U`6EaHm^JBV+>n< znzuAnVe;F!)#7#?i@EH;udbOs*M7FR_uw;^M-(5Y!JX+lnsXm&#(#*AovCMB zUYY&uVnFAzDMA14)2#Z(B>cf7uHBk-j=1h`yz^g^XnQ$lf837jhZ`ezOk2@5ZAFR0 zyISg4s8B%E?~i@a^yT*}UVkt4XjShG){I+Fw)E5VhY6&gzn?bL8pR#sVVYifF#fdn zuDjMP<|l;ZFO1$tA5ZMZJJaReADk^Gezbl)6jzWjC*t(vzndw#e~c#pj7KGRzV$Ds z;s3m-U_5(PPE|g7`qJ~t@7KP%zp1k5_Jxpf+5wq9l!-1YL80{%QT>aXyV_j_p{6uxg|$kvIy_h`I! z;mR{*OutLUoUps*dE-@!lDuZFxqIS$R&?ioRK}0N$iVAkWeoJRgfNdl?SDu^Hh)C& z=$v?6xXwklHgB%J1>0FLYWRAzt^L{fdmh)upME}J+pBXIj7TZgA~s%)UgO2Q%gQw? znM5&_NQV4)r6MtXlu?jZ?;jp*vefZa&V)&;ADn`K2&*ibWsB0fXLjYKq?svc)R zAaKZIkuyC>OK!?gLY}j~gEq6UWnqkgm=e1z&0 z+ovXzV7HLGVybE!2Ybup34Da}k?ZISB4b$`vZC{ zw<9HOr813G#dGVVj=@68H42WZKYw2o!V=0^H6f%N;F(#F#>kr@7Er|v#Gt;|{&GhQ zxUc4;^Wg~iMXaK$Fw4jx#%yu!+QUBUDx|#Nh7@dcj8Y;NGJ)N-;zUJayL*g7JOBr- zWGgDDBTWJV$=c1#Oiqn5WGfCTePQB`a1U+<#YsJf;zHGvcNhR0T)|T_5gTybVzrxi z!L{U%{2nSNm!^xO`fziqD2W2Zs)gt{MvbAm@xp;+k1z|WuMbe9Xo+iL>&N@kJV~|= zHjhJmUS*5U4DPqoM^MFfYkxj3t&|ra?xI39l*EF$Kp)^NoETituFDQ*i}T3)$cVzx zvcre#E^biv2IbMy`RkA-9ctpHDi~O`=eRgz#8ktpJ{XYfLu8Uwm@=xD0CQvebUE}B zrE$jqI?y8&iN_%2?X5^&$J&B+t3P5RwiDuixrSWl1vgnN;bOheNo@=gLYJuK8d8T2 zvy@sR#QQvJBE}M`ItN+ev&Ui4SG=LKF_R&+t0NZZU`(%% zj4UNrcv=vqQ{iMT*C{U`jvCDK?QqRnLW+v*Kzw}oY(igKY>D<%LzK~glz9<5(=_5C zSmzX<=ihKl$68*lDI71SjkOvU^)$bLiVQSj)cb$-kVVi zN$rRL(P9eEFR#8Sf^1f1zKtU8ppF7HrOvi?EG>@YSYTVp$ztuugIUsaVX4yY@1lvH zPtZ9@Y1IPK1ckJ-gVnp6!Z6QW-b5pi{g*WSn16?RsS_Ls#E74`(O6J_r-E2N6rOFB|L8*|jAlmP!ng zxsAMY=y0lzQeAawrzBbybg7!XBQH{@fkw&fe7^`%1$mrO)OjyH_PN!T?q@tWhop`QzL&ex6c2F- z$qx`Rt#@QpO3E8&W)D4dvycc5u>#xe!U_Y4nG)~Tp~^GxdR0g(@g5;z>io6S*ZM9G zAR;vpKEBErVfF@PHPBe&X@oozazPm&_RTyE1t>|qyhG#ZRDTiH#8XMf1VDR);-)+| zJ5U`skhu%Z^$=G^tqQSl(Y%&urcJS`%KE&3ZYhHXlvsUGc&>SjFBZUz^9wbF?)h{c z!ANrdhFDGBhau@O*{595OKNcBO;-bjs*`T~*Y_7;LZ_%+T9prx?q}g;`Y59lj8vUd z<%&rcyetPGbFejnrplX44Fb=xQ@Im?m4F$mmil+7>oe&EJQ@~CaqGI77?+94RUrfn z54NW=;sc`fOQL#T5RYU?c0LaH_R>ntUa zS`(o(ry28&(7(Z?zp-&q_GLY!`!d&0>d?X+$J(9j7gqPV%kECG{ z#&h||C(1R%3$fKqq|+5Eaqe1-R!d#D+V;^r(p=;!OX(oYp{n+guW~T|t~~5A(u!4s z?3`Fj0VUTN838I^pCAN;*k175Z>+uBHD@lXG*WsLu*9Rk6&{AW#n zH}RtQlS9;q{W_o4hg%6#+xOl&Vt)(m-LRRI{_EMU-xu#duBsP4-Lm+b{l9(qE%p2# zYkvRWTwYkRbYAkx;r2OyPI^9V>%%|)nE%~}z6Rn9--fpreRj(49$QfN6Tia!+U4u` z?zsvy>{#pj#dmskjh$BKjW~F8wca=F#@>der-ToBwye=Sc8gtpudWpi4M7ilo!FzFj`|MIY}N~O?>uve%6E?rgdDWI zSR4KI%eDdCzo%LKk7)*hX@2Ig@-9*T-%7Su@9rnQeps?)Oo+w&#miNW?|=LglD%P_ zi*R+0P3-vk$nU~(5Bc?4&B~U}Kl9T=B5xm^ z%$@e)r|a(w#YYb=`O#9_cyRcOOO+wxKi_)l&%^C!UpEneWt!u&h1;QQpL_ovUDH2C zHyMm>j_>_(MEl3l?O;83%y_t!dg*Mq*SCK0V~j5+EY*!#;+H?!v;4Qh<0r??-?WJ6 zyk?bOisY(VlP2IiJzGnAd9ui?UF5Sp!ug`bJd#^bS1~krzXbv!U^4XK?()FzPR{av z{_X}FI;n}~*B*Cw{ygJxQzLF#{Nelpz)2h1^XTWh@^G)^R>enB8h6ZAC1JQ*@pR>i z&!>zMyR#l#anGC`xDfq$<;jh^+kU(LeI4ig=PpOS7pQKZIdi7(a>S#a=Zo&Pzi!)@ z8N77w+-}bcth9g6lkgr)4!l0rfFDCpTT;WF{*%r%=VK8#c2cZSoBOM@v3a7kRF6nP zki)5dRXy^%Nq4Vr*BM=*b0lusbx~cyV@{OXZ7c{YVih;a=5d*6gmC7f%i$832eKv@ zS*XrRb#n5VsAg69CJ-EZ*fI0s1(=v~VkVu8w2REfeQDuA5)#GaJ~p&I$xw?}m_|&=FpcF^m`rYtJc@DGkH}bib1imln8aZ7Xh_tAs&F7c`cts?AT5qy+%B6v&G>tnM4bF+fKoKUzql}o(lE|^zt7FD*!z6{ezO4u{v zXJh3>5;le^%EJk>8?3n1#7ZMUM$GI%&Azb^jq6*~l@d&N3G~<1upr@jg5|oc#Q9vn zCr65()-Q1;^w`f+T%lTWwFN;_Oj*ZIxKXQgntn}qKnU@QH~L+1uc8nI?lEAVs0tu* z;4A#d>#2oTb2t84m%t?8yCp83sNRjtH{Rm$b~2a<$f<)-N;DV)f}{9+0u`yBA-M8v z-&3wbl7}NBz-f4;z#+jGv> zjQB87FggT~n3ynR1|91FN(zJ(C_DQ*j^Gw|Z?ROon&U6~I*teW=a3CkQW*@g0mNyM zQ9kYu)h81j)&)FpXSl)*`s*m25n7W9lam;TfdT7bAIX3b-d=R0^ATu-Kh^xZ(^&&^VT%!DUXuqK^8kGQAvd z;T5=*L8Z31i-cSbnFsRBO6?Bp(>GE;ktj^w^8P`IF)+|Co#*FbO)TDdq1s3?Slx*f z6QsgY$tosrb&0)jt^9P+DV>;4R=TC<>a-B}S+BS=LC(&Qp>%s)QP{3j(`Y=;)X&GiXS{{QBG~ zIe#oPm&{^LVS?il6`1F6 z&{{>l(ZrxA41l-QxbiqCGcEU&1NJ)%mDwofOHS^0FQf^49raQAnU>m9!~{m(E4ab?*lfjS*mN1I%^uff@-sZT=J-Ja;uz6)4q1=}NMKQJ0|dbP;do`ZG($V(XM_ z0~4@S6*j3{9*4!52y|}RDr|>M>IVlE;{AAu3t$j)_15)VR~CvE!aP^d@dkPdofP*6 zjXbd@Rgq|>b8+_~&UTq>Qm$x$Z}{YzcxILbH#4AG7)U#)TwEdLgI$NQkZl_)+!amY zi4#3l?g62Wh1qg)gV;OM1l3~U23%dqokSwN{wx2llKre{wzLVvQYyZ6Nngny6RN z1Bt#PNlUl&=b9^Kzy9n^-{|<-H;?a{b3WO9y8O1su98!8Ov63HeMde2Zh`gxV@y-Q zm|pFww-P)4`JL=ANTI`D@}bl$@QgwAY&VGePe1=QsxZfttE8S;0 z_Ctkcz5jE@!s7&=)J0Q|zBv&5XihnCd!F%$_IcWHT^(Ul&)UbnPxO})UFfGhGuL$o zbTmH=n{y*|_{+oHEnfOXbUnS9u=v)k=Iqv?Rn2KBu`eXg)}11xY`>cmXV0BAZo%2i zuA8xs_V#tWVjqgOi#%&)$DL(JF86KDnk@S(;~X`7)I7z%HYEN(O6+_8m|gJ4D?aYA zl34oRX7_T=kr}`4>mPdVc{_e^Q7BWa_+#4K86}3v%KjQwT;brtz-jWmyUH}1Ho{9(LQyoTYTKd$-JE!RW;#g&xle+aK`?{c|S`$^kQ z!i5Q9uZiRboiDpyWtE(HKwU0;^I+}=)2yS@(q7=-yYx{#H5W&*eoOlE_kLm$CqK4^%WNpdVzp8#KpzA~ zJKxtg%RuV<{68%-as|z2I+IUVwELh{9Tv$ zK7A@Esq`8t=kJ;e2a9h|%W=C_;|*nI2*6dM6iB!jz=4&_7a52C=yU6g zjEv3c*{HP-yc%#OM{A%C1v3`6+cHHAC#m2gS_1$F{4N8 z>zT3Qm#yEAtG58r0fQ`IG4KvXIhb*IEWlbRxJr^~;?FI8I6RmWd20A<;P;PTe-X}L z6iOR9zNGp1*?)LsvRX%`f0HGU=)_VsUZ;e0SwmgX2?le`}{W|{J zafb^chKh-<-0zo=mLsMFwmB~3T3jewN@^lsm_#_+>M?QQvFs^kWuw`j>9=_nfKnesgS;x773r@RC9 zzkD0$=bD;WA7XfU$l8mZFD`o4Mc(L2iH4+p1%(B8VL@Txl)edjq>qXpb4ivCg=YXo z=l502&F)h%nq&e}8kfjsI1Z)VPk%=Y*ZlBR@-cPL+uttMm3IXbNCnHbefHP0Usl%k zB-YSCsu6{Yy^wtM&HLIwtW30@6qtOl-23y^&~h{^|cB+PPyVOEpbQvnY8NA0xgjr?_~i|$a(9E zdUCY2%Y4n*zDAPNEiWs@XTB$;32xsRNPO=16%L2rUpRSUPG$Q&LK?Ah+UKlka|NVQ zrb;QsjS5zW6dk+n_u&^=7uJb(SmPWdpSA4*#jy&nm6@-e^JGYZg1du)J0pX8z*_Za z@VD&agFcNxva(N~exbeEc-m^>XU^iLIy9e4A{KDm<0fwmr*JsoDVzz$P};5I7iMnw!|m}+LOgAAO)!hzf4_5w zE11F1Im(PEp_s3jpfA+nhlyJ^NBJ}Ca%2DdNh6MQ66u~BVR7<}6O)%+KR7vtAPzQ) z{Ncvuyf1#TzDoFN{-L6}&3}0QQg*fL$sQFc`w11R0|0W(&@Fa*5m-TZ{1q9{A%8lMWq@4)EMp+*n0$2B>d}k>55J`8*Um=FE8Cs8>dgv)B{Pn#uo^xx z!?<7xrg!k@T7i`=xAPb<++7nE)PL#SzO5cT16>0@Xm36LkhS4(P=XYkxP+oG0O=M> zfkPEV!DCV>HHeD4I=q=lqN~^1iD`uLv2Hgt7bcV}55|KykKM%=T4J-h>{78wM+m6U zDar-uvkGfQl?@fye4T^_C6F)pFF8>NWMQ14GRi|8A0_oi6I)2n+*rD{jL41?#Sb~u zH#aO@sZ45GqoScA(ATq6caKUC1$9R`aGm=`0#2nCHYTMJvECZA8kW^|7LD^F1_xEm zuAjbp)5W_^{9$cw+m`TMg&`viD>&A2+r>oje3F<;%w7K`q<6Bb1Y=WxieD;`CcxSL zZXJTE<+PijN7^GvjZSG>=C1oGH(~PgsD-P~d!{)P`R{G4)OvGvZH1x1d?;9F^f1O} z2J_D*`+H*Q2!fMhUfQG>j(pMx3>GouvNLcGORT}y2!*F2D{C_M04&r zq1t)henhdehi3o`9GImgvFIjCf~0<}+s$DyYy6D3Zch9(#^}td&-QNePu*=`l!BeG zAU}i_Vjp2BMl%E?9t65F)%A(ZrMv1jzE7{t{Bq7vjLW94emRF~(1V^7rMK3bXL5oe zdj^wJoRLAnMOq;^3P?72V_~g3;6WBfT@$<8ejwn>uz_C>{!y*yjf|TcRtr)X-KysMUFQGz8Dl!+% zsXUT@V%}>y;m^XL^ePW<|Gw$um^-;qJi@;9fz7ld{o&b)K7K7!EM*Bwy)_a8P3V2| zZN$Dizx1|;@IbSDusPlS!;GB^V6Py&hDL$87;}NFy?^l9P4nIp@$+v&DseNZ8nqQ2 zuOCXeZ~}X{v9Fa*m=sLlPn*=S>GKVLJ^SmN{w4z}m$D0v1}tzraa6aEz$Scs;(ZD! zA{eY18kTn&iA7Q!heJ3^9{k<9bpYGO-!CEfv-H)SABiRZyW5 zMco;H_4CO~=1)fh zzhTU&Trui2doN3pv9aZ}?%xB2{xMJy80gv~&X50AmSp|^pe*?_{kv-P13=clJ2!FJ zhmLiQ8&|>@&q*R!FMqyF%6X<>G+o@=d5ZAIMECB?_fIo(oSy7DRZ_b#e%tiS3XOKF z`b7GJ?bWvIo#j_PAKy-2(-0)-82d#i|G?{=7qS|5PCYjJ#=4TE!8PX|TrQjC%5Q9P zUp2gH7IuE#qa^>V*VC#3;qWy<@uE&r=o=d14CX&BqEYPdP7L>!|S zI=VbmC_(|BRj!IV#OExAX=G`wh-e`-zI^7ZbyILvrVJ3lM>(-5+C)A)z7 z>pzD&Pw?L{Qh8upciXt{U(LJmWq9)P>CdQFvW{MSvRK{leZcfR>rVW|=yp!7*+4I9 zS$m0n?sDDv;4@z=E1#U)l9^Uwz0t|kE?UXqetN#LfE2 z4XmrJocz7NGfx4Xwh3-{B_g|YZF+ipAp@%Wz^p&}+g4t5)wcC99mN?}W>&FwjmYNn zSi2gZP`YQbt}DKoFlBDhOxJ{^*Y7nXi{EcZIiAc3l7{lR2k;ndjCvV0xZfP-q+YOeU0-G16L_z@n_#x_bU#c9O08zJ1GYGi7H!nUc9+ zX6W%FS7%_6Hy8andUfB-+nFdsShV39>@j1@h?K!@E-62LWOq@-)AO?GNHZT{{!d~N ztyY_UKm2ldGV9v9xsIl~Mj4NWH|0c4ERG6vtV%XVF=q>{88X%jMWj{p!SGj&z2w7R zzJ71d*=_rO`f(@``%>3X$+@r5q?a0!N=p9v>#ve`(PhoswgEep56q;h=6f%Lb5m|? zI7VB1vmtz7pn_9wa;DiY12xG{`$s+tWU*F<8cuBUWXl)74W8{I`Ah#o9i0DS+m}=Q z7Y|Ix9Sr6^*iCXGEb1niSK6IvaI3$|W8S+*@}#=MT{p9%^3=EmTo!lQ*N za@L;`-K@8V7za2{l9zrtWr8~KY13+R%CVK1SD6R=O1JQ@OrIW{&{fLn|8n8+6^Bcj8674P zc{h8JnGe3$x2c|i45fOc-WZWP;++)S3qbpLw4*XXR#)_t=Gw1`7kIImy*Q>wUFuA^`v+2@~>;Y|O(^L5Q;*;?$ zmonF_`*31*nPp(j@B!t_Tzv1rpOOM|OMGIAE%DQJdqo{;WRJm!Udanmx!>P?Bxu3d zR*y?fw|#VO13$i85g5#le_3+E{o>5yrr2iW!$&2EMjIL$df4;kiNjMj-2DaFxp@Bd zlDi$pY4s{q&EmgXL)?GtK_Ord3aPl?L)`Omvjljq0|zKUmoMsl^>$+7>7}Lj{lL9wYaod9SccwA zQeATf7V z!eCi2+7OZiDPXod3v2OtYXR2&D5^Ahurxvz;L@YGk}X49oXpJ@m&COT+)IWN9_eMY z;7T67P_L?Ia=)fv`QSVO<4jCpgp9qN(gV?{H@PGoY5c|S58T5|V`K-SCaM)>E~I{g zk5=DKf|c&`p}v|}bti-sN>ydiusLpW*^|$R?F7P4gfWg+7%PpxW>EV?m@n1MQ>wZu zwOEz}Mkns}=Qm=66ABBxy=g$(j+vNd9c8w;?E-fvknA_ybZ`-1DRHp5Lk&dXFdHP?n;D?x zFC)X5OdqpBZS=?UQ^G?a#de0GcDE{DkXHd!Q4E<9U_ilhm&H55&#Hoy!PLy*Xj!L$+v1}Qpl zwF^aB%fwA=%EFJa>oN)=L2w@`*`Z@g=)K8>h-?B`0^?6tgg>ue$g~+?$N;P%J~qiv z$f8GZiy5{#G3(cBx`HGc1Wo2MmZVOj!i78q0Qh)7k`CB&7>ubS?P5Yd7G_EDM z%NIn4I?rcY4#z0ygbCp&L1)-o$;$M#bto+GOa3>$MhlPA0}y(cAn3kiGPWnL5g}oI z1OC3fybzv$wlq0kr*iOF6f{?LmXDyorUPIyHc9(Sq^mCeJ|T#hC$ETJ+NjkM*mas% zHIFiIR%W!|Mx88P!>G*E#`Z!WaHAA8sZ-MC$rFfqLu z0L`YTk;49~C*GWVODE{as-#3GS=#mhRLUr&q4lWL#so?QVEkY-k%Gi#zL==W%b!sb zJcfEFVuKGey<*7?j+rf zT4j+6nL~UDpU40BpUNnFDT<4w(c}n9N9vdoH>UsrvG#~Ow3f)VA5|=yXro;f-0;$x8%wz+oook|DOkH_26c{sH;vKxN@BLe%DG@4e zEUyg-P?!-EjVlO%43$C{PKOE+5Q?J~PrN1%1x)=y{W(iCr9dQZ0seCU5XYneFvJ61 zIRMS4Do~;?M|#Pks54!z%dRRQm?<|@_-+75n#D~B4+c;*DuMap8tTWGlv-p8SFs`I zQ`A-9f@Z;K&YG$ydto$FfdlD+RGiPF7|06Dhak?&r=)o0OtCd|%OEF|srRKNwr9nJ zEAwb*J{52;xccgk*mgdXTjpcLMvf$yLr)M4j!o_)#W?P{o2F4Lr_cwLQtEj$uqrSi zV>E-JL}5jdPi8rfeIn(6BRQp^qEi)RPhX#MmfEwBK_hhN1Y;Pg)kzL) zy^@m^1q@B1Ateb15+yw!tQf#^j8K5lYqDWggUJb%;|(Me=lYVbz2X&8jFj$+UaI6( z@R*Rk#!=!3o8ju9zW;-?H-T#6{NG0>A;3%wm<#~}qRoU!OaL_mL~sq0fRhj~1XOS< zU_fw1T~!3hvVn@l1SL9Vd1Up6LfEN|u>vW6^wsRBnpCsgi4r)0Dk;Gn0acOn zU<(NmO0RR9X=T*J1OWE9upEk2>N^R`XIKQGl&Bkr#-&P#R9k}?L7g$seDF=+obi#> zf6NmBcdh<{LX$sNphwvUI2%J}7eEqQEiW`PquSj4z`hPi;LH)^Wh@_M zA7Ub~l3z_vrJg!!6{K`fENBtsK;2daanyo{^rLREU!HdO&xHG76+=vs94KlEr8-LS zRG5~HaCO?^MVGHGdI3}ctiaUp?C-8Wz5*R~h%+dMqM4N0ORxu^Ddef?ag_36HHV35 z0S!?GBl{^$MqY<6(9b(oTDzdlGj{seYGx)wO0_UcYirY=4Em02*%()eRlw8}k_u4l4J|>G0R66!+98EHsVR;6_ougz|QIn)(fEf-c z!ONtj#Rv{4bHw#WgT&4aVd1Fpy>|#D&4!5)gsSvH7|hARlL5n0@iMs0UBeMKrUo%| zf+Yz=**7AM^+-p$l-3aj#^K!@Yg0I#da^w%&ae^5KECVHR=G9PF#T^vcwFeRlX9u1 z+y-imwa9{*aAThP+*S~~&{%^|qzI*;n6kRwT~`|X$#MXvxnI99YX~Qj9=pIiC`h1d z@)J~*#31fY22NGKsWky51!fVF7*n>V&sQkCWe>arEN$~T}0oDAoZ{X{z&P~ZYaqE%(;{$z+Kjwe8AWN^Cv+O6=qho!)j@wTE zU)JzHi=SNhdMFVSH})&pmAG}EZT;%2CyOid@>a*C$FJwv-b`Cv$R0ic+Z?rhH88iy zZ!`1C;LzvtRrr}D!ws{z*!i#hANM{wvpNu)BPZyWZ=r zP1^d&gIW5|$1GR|K70J8ToZ8k@tmPwW0u*>FPgXR@0>tc#7~&;7w4Ss+>sX_4S>U! zysoJ$6vl0;oYf)ucCdzG&xu+cuj4nzVfPq;>V*6%RVCE+T_-uvvlQ0&|8r<%hW$?YD=8 zlnoAJo&MvSt?N#=dL-X!E}S*7G-Rat*)IocYn6D8-T$vETh@785Pk9mwr6)jMZu!W zGrxFycjN1?x`WzJ<^lZl&$djTq#FBo4o3U02**+p2Ke^hp?&{8!pfgNboq=O)BM}3 zA)d|aD!+59NQ$_AdCr(Sr_b>o-}(6jNmGtqQ-;6S(@ouoBU{A|UVU49BUZ9nCU?i8KNo!Qw2$j~c zSX3SCp-)WA@t=LtuQf=SD2mApycg|1C=iHjL3#ynuN)=GE zN64oG9zJXaq7m}-GM>C#K~#+>-u4&E>k}P_*4j3cVw_|qF(`|0k=(|&IJ9Mq&ao99JQL0Q!LVZWBrbZMlmCLaNwN7|9ilbfYPmZeU9&3|~7C3h^^Ny5z? zDUduWoruCI^D;G$ZE?^C$Z}CY!qmn)V zrKbZyY=-c$3|vfMHTi(e^~_#ts!p!{7`nUBxsS?Yp9D`CLl;$hDqdS6QYf z*-dOpo?A)~OcgqUo{o|FFv8Vbcr-HsO<(JX52m>AO&KZ~I|`$IDI5jtL@Pqsd{FvM z9r5|Fy;fhH;x#GRM%-Tj$XAbKFE9Rn_kD;S$Rb8S2+;5CBSe&9w_5;x>Xj77BrXvI zVjQ=*J5w=hsgS4hsjUtGK&Anh*4pR}RQdP)*|Dc#Pf4|)7u~3$_?cQo1Sf{ervGJF zaqa70&@8zdJwBr10a^~aR8FTW6)B*mT1NA3Y+Q7~eM|?8W#f8;q>(!J5on9GlseQ| zd%eE=?i)xgEoL8x+>wf*_V8dv2fzW^o!CV+Rnnv^#7kNd*#X)zRWHo+Tab9!^J=vp zS6VHJD1<2+ND4FX9sueOrMKEo(VOfJAT_{VIfeOrkGT)@({h zovE{zz~s;FGFO!GP$$CP0%^`>lIij@P-aOmP>Az1 zGMFf&I(S5utrSC*M0-wo{Cre-p!h^eN-;>YaX@v7Qdgp6kQpS~?g}kiEYS9Gl0L1X zId^9q<71X$6LBysP>^#N)^T!B>UX4tct(*3YZSKosAN-y)I`z6DOQzQ&mM3xrARqP zre+_v7o_pMK#d633xhY^K5)3$6X5BhVi<0!`N3>MU8_J{c&;wLt_kR)w}qOJGcQ}4z4{@XGGvT(PCXpdZgn1!kLc{3FR5*4_Rzg z@^5aZP;McbY&-x+s-bzzV{xYG{s3z0=S8KB%uKUs7(kM}+aM!a0#VWep$>1hvriGa zofTSz2-IN09h#{5)N-me*Y24vDm7nqYYo_x{wXcru0%z?Kcr0MTr-unJ)gg|jvM ze1C5}iu=J5@RY*W5HHa z?jzOwwQ5Y*W!g$9@`W0!!^s3l&{QT!EkmNP>LHvkKb56Q!Kz_Ll!h`>m8TMAL+wp2 znNH3EqB?_uQno1pwAqDtV=4(JBA#3Xbcuax}g&-R>X2o`WLP{KM55P-=77S3-{g4pt7KYO*8zPl-r=-y2!!uKz z$QZ4_oR6XjFu_}cljlDaTN1aiA=YvNz<5;{ z5*(1;xXZtBH@&b{*4l^Z{CrRcU&XSRK>rRlKR=P(q?7mbT#rfurwyiB$?!N;wL`3; z(KD*u27~(8o1rCUE-6>3%60l4m8R#QsvwJLDn$A!?V?ni%{M}j%bS+2cccDuH(8L; z6YgoDAzL7>(71J!FWPKOG@?aR_QEJFR~SVU=ck2(I>MtHVZ3tRF>F3v}(R;KeMR52^e%JrIj~`C-Y?tKsDIjPmB^ zKgJ9mAJoJvj*W=+R7grsTweF^yN<3l(aWGQ?bm{od!-*{((tcQj-y67kpJsNn&W?s z@;z1Xqq)yNrggVHIP&5qIPu`D?+>nw*s^BaU31-y{s;Suwr_JG&b$@%hsmT+x!aQE z1?R(WeP0a z-xod}eP{iVA=6rscRzL;KE1Zu{|ooN)cb2_v->i~s^b>hg3?b(0BZb128L z{p6J??H}a#<$L7EiBGK8*BsrRTmHH2%@@Cv%FW)l_p?1NJlJ{S^W{Gu?)wZQ*mQFTK=y9OYZ2+Y^DPi zRgEvYg$C*TgAj9r)Wv1DM3&Q6?-&wg`>CJqUpjCFEA)9%1R)J<&1a?b&Ir zvBGL*C0a3ZQp)J*sa`z%afUE8l z?`o&e@CNsd8(46Y2@G3^{62j(8#@J&c*J*`My!!t@)h>fr4!!y)w z{HVIL=||uDutBM4^eA?(=8lL$rF)i&fjq#YfpDXFoo-qzB>5aeroKf-+1EM0T%hJy zM!7NeocWpqAczik9tuTN5>~co>Zr$i?=kq1bYe)udBx0Wl3fg#c$0{Jcs_c`j>&Y^d}q1eUsz z2XNttnPbWjr`d8lSP9NbJSY)=9bWU9hD;D z$)oa!u0a5bxmSu>`SaX#E?y=$wX>*`;YDJ(RCBdsFddGk9F(zm1hu4NxS9U^BYJAB zknK>?Is66sag1WN$tZ-H3)9mB38xpGN&$w6V2K-p;6|>R%JZmp?hc1(c!0`M*%3(B z_GugdwT}*f3)odeM+~M?I`FuN(sVE2oi=ylj3-qbn<~R8VlrGLtfNIZYv}17fv<-w zH3?Oij*hh?RzvCWpV<~(NOKwhHy+^gP#U)&J2OPC$|)iM$J`M# zls?-Ab#^RBLH$sHqlu!ydSwXI@_K(SzCIKxhche=NW|brDX6C37|>m_`>3U-K4(ca zQ_P^+33cp71oyzG^na?HWuvYIv8r10EV^Nq2V=-^d6)=w)M4+V_XkxL14fxEr?4ubANqu9q66Pa99#V=%|caIj!C&+!2jg{I7Ft?vcL?9(Y zU;)*69Re4}R|^thE`ZXjGu9u17M&!O?DDl3iMr)8Cb2EI86BRW0zv##73|>Jo#`J$SE@9a~&F<2hxpk5|w!3K#9CeNq4u&+>O=3 zrrCLsiQjT+<-~{)vOsB*Eq)B9<+;7e7=19lwz)M!BE}lcNh7&}XQ@njmBLc#?c>GI zW#W{3__MqzTfZtkuxO)RV|Dga^$@1Qyuw&aL^ZMZ6j#A)Q6VAAb+Z>1RuN-LZI@bKmcJKAP6gH^df z(kceK8ZSN!b~$t3)jdWDK&IbQU-TMG-~(aDwvFRLcj0s%pBP zWR$}so~i&-pbB=0N{oawUjyW{C|c(kNKDF6z0CokBa;2osCR{?ez|{H!Ax$C9X+q- zC#$+;&S%+TPXPW)diKRBrDy&;w=O=TgdI(_J*j&9Jn4t`y_%Mt5|<9Q0gV@5GP zU=GW_Fvoam`(~Z_t?$1LAulJpd;Fxjc=Gy+i!Z3&i@L9`-s%qgVaddtICKvEnb%e0 zw>ojhx6Dh}>UZBRlD!PPEVrx<{mkHdk`OQ9^CYXT#4P^ymGWfP#n*#F#3G6R_xJ8P z7cZMU_qjGG>)QOgm}xF=v(KGlejhJ8((?Vn{G1OlRsAcb2~c5a;={o|4Zc1r?}6#`oB{frXZ4>wB^d9152u&Shv2DUQTxwxx9<3Ot@_TV5!2pm zJWyKdnvFp9k<+Jut2aC>3K@NOR^ZF)AA51+vj1eHI`))r=@rghvWER#5ASSb)=NWasUo*pQ!>tu;>}k z3%Jqq*0y=bhn$;swn27iM1-+dYu_#L6adP3w@OA%dnmS|L_9}76F}IcDSRT{851dY zGp;I1n(H3K!}vF_LqGfH4?$yV*??s@z$_{;FDnhtRR<7~j8IIwQIIwhEv<&sNd#pS zHVH87ZKM5oV{AoQR+HT4T7IIftNea_mnSemQWFNU+cqNN5g_3QxE~;1 zGv(3s{pl0^m11HKoGX!WNnJ0)Sf;oAq&Qq#9fBwMVfZ0s$(MU7JBo| zCEs)d?ky&1Kss>};;NXqnad{V)vSRCTSa}z8AnxP;JTe_=^o0gZ-WCHBl9H2sD_db z58wbKV-wF94F$zc6w9%ogPB+v-h$-f#j#@seR4Hj=|eBn+Ku2cUywuw2?RueT%>@c zmCfYqITzt_tDfVQ#}Eebr)%vc#m?$Mgqe%r${Gm=E&+&6`GqK)zZmDt*+@rcD~1XA zLT?g5MLdhy%*Cj5C0G-SN;onJ4Sn5o9z^1Pk^~d_i_c9Z!J%rbIG(Dy<7uEy8PE1a zJLV&L_7+jK^0HS*bzE{m^FaqIO4)cw2r~sLcC}IkV^(dWH>Hv{KamxZTGkE*b-4Vp z&4NQ}K}l*spp{y9ah#;Xswj;J1+&qoAz&3=lD85Z@U754)5lip(;z|dOsZdQCTv1- z!K0cjmTxmdLvA%mIKUXaa+J1X5rwVD3@SB^;jyq%oNggQac6y%sIyR3R}@e#>a!Gx zOqjM1g19O10zc~S@svs2SPrV9Ga93*45Kd0+2;=qUNZ3e4a8p#Xq}nEUIXbVUXVj5 zNe>OC9n=P9Y}y;F?%86fQh^yQftKxa^oi5jVwDC;%!nn8Focq?9+X_!bL=R@sL0*U zPn<6Z*%glRXqj*j5fg}3Q`chB-4Q0+S*|l`Po?aRi#cU4nIrAFnywLNa8uMAT+mT$ zqRazOlJQKArKTi4v+PTq3~hH}a#Br8kH2&7&Ie6a`JANh}MX=_^GPCDp79 zMq?3WETk+Ej1qJlqE#hpk8zdsiiik*%3WMt#w#+Gm@-ivmc(IF{g_l%uSV`K8lHF0 z>(HfIuJB?pQ5s~lWr*F8LQZjw(#)t)Dm{4e7KOBtfb~t5dOLsc$*mf$acLW>it+)I z2}nvZ)ChIYRRSergd6IzqOxP;5z7o*_Cpo-4jG7qsWGd7gA>&pu2Ll+gXN0sA)Qu+ zB%lEQkquTH%#8@bvXoXaVJ^mc%qUL%bAwRA1@`m1v*m0$s%J|$6+dX;t|wMh6o#3p zeosiOr0RdgB}yvyM^ri(c4KDduT|xcvX&d|BX>Bdn@U&&HMo*`x~)`it~a%OVn#AN zh)(ZjoSr!&-8eEFw7Ae*s>8nkO#+iptQ{n|Dge`yu(KQ5eQ8UZ=O{|rm0f7E6h*-T z9fq}81tx$^ZKG15+0~wXklVAA}+T2acBgI1$f|b3`yusQEEZc{)ml8w|yW&T!Yz70#pL3 zDU-br4qSy2zaWiuAFbG%&oql~xAYrT5~7F&b~33ZQL0yp0ji|l>{ir>^$(=Aaqrey z@|m7#^O-hSCq&gTc|@=nx71*<0)PxO$rKvKy=yrplV)alxjo#3YX%TFRv@H$$D8;d zrZ*x{3Sy9V;y9g285Fo@x_gb9M8IZiCtBkd%Po!}EEcLEL;>OoR;5ANIcCTUH-JVf z{yBy@uJa})=Z4xPRat<2pOo#+(I{`2dGT_RYZi#h7!vPX$&=wDbmD0seXh(&(I!Hf zDvKq`O4a}SVU#eGV=$ba3lUGuuVaxEO@?xF)zIQxNTGl?3Q%>iQpIG(qMQ&W8q*I+ z3M0Xut1>0`Vt^FkW@pi+h_CLSB^Stjv&^`YOdNLLC;Nknm|IHb{_y318 zUUg~*J==TY?iQ?<#yzaszYk!IC&c&~9u8e*=qlY!mCo$ywyt=-9KkZ-E zgsPsll;1ll-5l2%aQXj(Bf;jc>KgV}-TpbsBQDDHC(@@}_{O>D=6|^37f;=&Tt<7Y zRd4IwHBFN>;;CMiBfJqX28sTi^%7mZ0jd8sRKGHyxn_Of*iVAp76e8;)ZWsR_+*X@M8oG=#F#`#b#?Ck7>>(lwU!=jgaBlac)Y$Q{@4`BhbYON}kl7e+8|o~e%eS{HQD zck^fVV>yuQ#Ybm1?!LCbZ&dPN*I4zi?ZI)$uYYg9dM+fb^RN5R1#QiY=PwO!Jr-UX zJ-lEJ_Z|Czwl(Pin9yEG`c5mwr?HNd(VW1^daS?0lNE{kMkxO*xYd7Rl#K~YxBC7!2cu5TxD6mtZd_z3-kL2MFn+DbKkUJcjTs8CHkr$g++<~r~8#deN__eC;;I@!49S6bsql zpU28%CA-aBkb4V$ouOA34|F87?y0u?+{8M)!g zMA3tsFYU8Mju`!7!`+4U>Nbv`ZVvaPKIgs_$7(`{vQN)rcb;5TyK>ZEXNsTiM3Yb9 zVev-D&HC`;r;{&6TfcutG0u6>4xhI}-vnE>hKHlk%%S>Im>wetu~{uh)Zq|A%+MFX^1SGu)m(IXStYfa~Vzo*knJ(d^CK zHfz`t&2PIY-&1n_wbFTqb}7>5UF@!tje8K$^)DBLO_JRvbG@tO4CiY8XH8N6Lsg%? zf6k*H7ktxBxfvuKS{%ZEaV?5HWW|K(-bphflboMUyV_K`eqE0E)t^brUL^f?Xzzb0 z>o;^qkG1zyhW*#g;9^A<>+UF<)v>HSTbUhNQqu7J&ySxi^s#vBmDBpALHD-47CIu; z!MkxAVrolXG$%d8(7vGR^38W)W@+BO*du;vo$`Xs$^1tQ(l=z<7I7P%EhrgP5?a?0 zp)T-^tZiA>^vff$tu}89R&R5k^GHlq@6NxleA|)R%`dmD^B#Y!yrq5CwQ`S3k9Nf! zsa=?Ut!zw2dC$?WfJo1+z^ag4j>C(whz0F`9v*+K;)?L*-O8B%($|+Z?sL}fI&}Ps zx@7Eu#iAqJ%dL%X|6K84v~YOa)-^s~?kan4V6eSt)VP$gSt$ocE}DVup0!waUFfs9 zN|e6!!0mnb_NhK^ZN$UX3pbUlnh$?|ukm_Vd3WQF8@X4vm!6F+PDy-Pc62HCh;8BK zAN()6+JDR4_TIuEf6BUVw^!f;rRP?;EY;Y~jt`q)-@i6NIkgF%axzcT%BUkgzdiUR{^&=mnkQ6mj}?}d4O|DP zal4hL#~karfOa1_?}IzWCIqbBHnSql;AQ~m<4Z=tH*Bt56|QVD^|HF+#2l@|3hwlJY&VcUKN7GBj=?w;h|N@8?&1Sk(?ASf!se2lX6RCq_=cDXe;Kiq!|wji-1 zEov&?&mVBfjYNxzkU}bcUSnukntIUt9v8 zt3|2@(b()2#jFLPv@LRb&c21`YHB9)VSe^=Oi2B`MPK*VwvU#&3W zs$$sIUjiS$?LnD>-DBUb43urX716EMvjy4g) z$+`!Xh8EC*GG;{tFO1BVO~r;2GCd&#@jNhJVGIX{Kwi<2fs+;&N`la|tbQ$uClitu zZi}agA;R!LDrqc(McCZLZYELR+)kQ9Rozi3Mt+OlZqT4EzzVSlrject5K1CKBpcF^ zBm&j9>*wKGd?{1a2x$l=RYIZW%tCpH;#h`_9}vJchx-SOud@^j7?KkI;0zYb=v9## zzQSV2l%rj!5Mfd}w$6HX37#qQJv4T!V38NZMa%MxQ5Mj@nvP+XLb-xL-)n3mGA2;GX%}@jWIRnF zn^a&liu~;qp;#MP*qsKBS~S3*)z*|?B1`Yh~MDrL5UU01BVO%4*)zf z490JrDh|A8+ahWeCht*OT%_&2tT->(epj{lTKuz>*C{pO#VgHq*B4hYfMG1@)~;=$ zO9n_zG%+BGbb{RR3I(Y6&B}~pNyrp?aNY&}q83#b#5NYu10gS<++S)GV7aVDJ2Cqg zJ+3q@`Z_~d9o<3_t%RjTBVYjiC|XV>T`0ne9w`lW$7%^)+?>9Y`ZSn>aU;-jHB3W@ z7Frj?&x@dQMgG!qRGdK8r6~{k!&69M7`T=lD(z7F&@%?nefSwvwMTB`anXS=MCC3u z7iVU=(=dFqJ6ozRCps~uQ9uI`H|nv(^N2o7jTyZ;{M311QgcHHYf(rCVL*TlF0~pn zi4}U2z#}M>G&Jw#GCP-vjC=ysH{1DfltHW9AqCYG!w@CaY=Mz!LF1iL z{h_Nd;A$x$m*#%hn4eHv>OY?t>WjNRvx4UQ03~SuF+$9nxLUeyDncQNs4YjXUOH=a z7LfSQ^Umgs_H>J?-8l-0PF>;tChS|M&-jcMgJyQv>#HDpErYx9qr1}iBQx307f7rh z#7pVNm!+;=`1ZwrXur$pZ&nZedV9jcnUw1p)s(vk+OV0Mm+*MZ4H~^*!kxu8MTPgA zm!_r0G2n)`b4%w9yDT|YLh*gO79U?!(!4~k{To;?{)rv{FzPFmqwxGst>_;kM#J)3 z7k=1@to;4y)zLX**7CQe(?6Vk$zeYC^v?P1PTbMH=7WR#XU6m!Q}Z7UpE`T=!<&14 zpZSfcZ()-D*7YdLtL)^duIu^YVadrYhn00NA1$X9ZL5F_$M4y!&CZ^EY39tJG3%oY zqXe--3>)nqVq*U*CIuB!++1@Y?GZJn__E2s^!M=0|F;T|AMusF5nq3{ASO@({J8197ywCF{0s2 z)WfTSshsj1!qz80i5{1p3V++NLN+Pic7FWa8ymaU-g*2kWYhBDvyLC$`sa=v$9L>F z(fhIW=XdpU2Oa7<)Vt`Tg*&Ewp78baQE^KjKmK#lflmdac7NQyHD&LwQznO=beGMK z7rB}eZyg9t{JRtdto_fH{GW~4kD7>zIh(xxL4AAQqdu-S@w)6nc~@}TlM~Orj%aMz zQ+2!PuOIhB4c-3xoyW1CGg{BEd22%5g!b{IMU}u(IHVbNLN$UgWaCPd=t5z4iRHOB zo75RI%uIRP6&`8tcd65pT~Wmy_rCLUhudy<7AVR!(n1wDA02SPD`!{&>7BXU&2y&Z zuuH>6gzKfd$svd-IFPnE$fc5{BmgL`Do+s6m@2n>^p<8@Hv*?B*`|0AQEP1BSSEv7 z!X?W2T}&PDHz5l^7<9#>IzonPNu)H4CX+Rewt4c@SlC;$#^qq6agdb_d&LH>Rd_sU#lqleXw+FjXbf(_Iu& z>DLM&d0|HMh(c^RDJ@eX)uLEdbw6sVBw!!VKN_Ww3PdlGsT?MH{!q~xfq>Rnnj2POFeiY>rAnd@yx#M@dHo!>}xSRB|vyb;-drJqSZ&^UhaNDPxKS zD{xn*C5-mR*g#u9*ijiqXJdY8;UWWQ08!QDtBcQJc4v?npp;{cofKUiiB?;PUxER5 z14I6kK$9cmq~?){BAt)+>}`ySP8WLxOv*w=MPC$l(PVq!4kv7 zK!wtlFe<@76@u;p!=kRJZY7W4VabQ)Nr3H8{ty)&*IP;rM|WjCZ6384GyC-B$ZN`6 zfQ0k$yn(tca2p+9^6HdXUeTe}vA|Z1-6mwU@bO$SkmV%{3~x3ldn^MzCOZuXoFY=D z9X9i&4!u3X(RNA`J1urVJ_z7ALGGUAKBzhJ0*$H@Alg<{oX%c72uqhw8q1ncO>-Y6 zys$KKdZjR;M~dqL)JgVQ{#`>@+T_bCYB!*TR9#ZfPw>X#G4(QTh8)&)-qGC@G`P*5 zlmYYAsz%DB0U^ufl`TA++gyp3;cP3^-#3pKU*S7~AYFPXD60qsnR7hH#!6~dOtid?1ADLeJI&@5$#QK)^4>I)lM2VxXY{4oD5)D-_Y4rQ1CW42Zpc z7k-}^z?KLt9Wjt>xhz%3F3w}bPx|cyWi{WaWeAq*YeSDr6>UPd%?1EpAY#!;-shoe ziZ&RamUrj}g(DOkq?r9ln-IB{J|DEe07knwd#|qfVRdvK`3Z-!g&Mt^2Laiy0$f)V z+l8llD5!VeMW)>4G3Sd^Sr$=+j;zC;pqRD}!w5?myIO$D%XRFkctpe)OLq#Pp3a00 zT`UM@Z56d5Qm0Hu_3xlAsD;NR8C4n+>Jnobb$YAo=$q#sj|1UhFq_*)M#pfGnd;WzHjU#$b z49>mpIoR-L;IU7+q8<6*nY~ke|2$A|V%zTT4#Qt$9DkE`Eq`zH5>x|^wK~o`KIoWm zZ?xa$(-k4-r@xY({yx3^x;+gPf#WusMuY_Xa4@kut9{wz@2-@byi>6AcxKvoXWY}u z?7vP&t_bk&3RDdQ&Y~>ovYsxWk7OOW_4r!idB(!^e23@t_FJNZL)~ZjSkD;cI=_wTCebTJSgSL%%1F%irx8 zy6MN8=eHjn4SvOKpI(0KZOs7hY1(0boL9fp9ee1k zy1;2p8oG4Hj7xq2iw5ueI|wRe^go|C>Kiom+hw%c{}sf&h2sa0r)cTpxmV{We|6`t zZ{MsQ|9h_kp8D~nUw^sU-MaiZ@N|04#3=#0+Zu>v$G)3&MW-3P?Qkyx9EyKcxa$kM z=kW&%yaOt}=#0ustJx&;{wOH2ZFP*_wZV6vg$e~q%Ugz-Huu+XaecztYlYkkOCJpU zGEhH@xw64Ea@4u#+%HcDo{N)w_xOix&o;Bt9(adxWhb4+Jw1Qp?b823x*^m6ws(nt z`Ulc&eh=w5OGi&4B#a_dqySX{CuUU=ByOMx zrK0q_{_d^M!CVmR24dSbiC@b285>^SDjbMRLl)@st7tvEgj2#+$2J!J}AuBks z&Jx_xgcDSe9xXPj_@GXu5`{zYqk8Fv%~M|YGEQ1mV=%6dIEf#?rKnD^0z5^D7qbaz z7nm9-HNGiiIg3|ZJ9B_x8a~w0#D!d--cnd>hH=!1ml&8BSJdLs3rtU`PPq}eI018p zo)K#*&A3_M5)>yfQK3B+R_TF^Chpc=)lsqk%ze)VE{rqJYqo?_qO3qoNen>gRuLYq z+7^_GGs@*p_-kSIFSer~qc7x-m`X`wlz*f&*3j>?y_t~Z`d3Fo+H$2DHdEvt)L`j{z&N&~iqZ2nwB79! z!FKM3R}Ru?>{AJ;&S#4FV&>{7h1@EBHlBrm1KzIGt^`J#s;|Wi7Q}b$V3;Xt45faH z7MNg+TZ1=X&BQ}=j}NR9M*;i+$>+kKYCU4Gn4K=kzJxkg_!19**G%uii`az&SyS}W z8T@Qa6&~HDR9=d^C?5_i(@%)wrO&+dEtv<;z7ilWmYe7S?3O@?K*9l|1c4^d1HrD& z1bMF*x874mayV`YLb-@cR6s2>OFvx&A*dmbaA7_pSY9Tqx@<$_7Ror-P_?GC zn-G;c#nbR<``nw*XRw80ewYQZN-NM465}bmfnM?KsMEFnPvRi?b`mZg$NyI*J zr_MeS?*LZQ4$&q!qxk&mGH0zyOVyK3C{rZJjzPFy;gv!JQj_+5Ln+w+^XW5`sTdzPFSZI)LOvQT%IX&me4EDvc+TLz zt^wi5H2M@vK~7!|sX`%Q5X;Dv7Xd_JhA~RtN^vuoBU$yXoaTb&pe>>$Dwa%zh|*Oc zyF~$6h(bA9sOZof@5{6%ofdN4GfSTDt@|ny90`JHI3n+#d9&86Te>k<-#W zfpDlfD$tuZ)^>5{LQHKZnuc@jwUAD0H{`7Fd>SJ#5j;RfF`uxsV8AgL%1%(Jm)YGW z(H7m^+i=w^_xo{v&j#^w5!JXnoeuQ+wIe&HSyLGhwG5)NKt#5HBTS&jY4H2;%+!># z4!+JG?cxXUs>OuegoNUJjSW;dSpKb9Y)d{=l7FL=FV^`q%JKxH*LL(O3uTfZFBE6E&~lAYDyT-S;#eq$U&JaTz#SO2E8(bexJ*{Os&G+t zi?UEYfKVAa79zkBKs71Q$&Ie5swZ1(uWF0Y(%vPj(Uv?HlTAvLxS8ULq3riwg=Ahq zLRsb#d74fZ+JI%MS3r40W^bq&w(_ZFU1)?GA1efX(AGS=C2-Gz%q@dsg%NEhg@ruA zn}_2WEFwxig3`dIUQ4G=>+`&~g5OnBzD+`7TgMWeMCOn!U`;%MNir$@pL(Gj#?=Z3 zy<-T&gq!r*;6JUS6oTQZ4ov}Dq=!%z+7`>fsgtaVb~ZoTSx|@;i~tXNg@`&=!}Hig6Pj_JN^wdMxUu` zJ@oweaK(d{&mJ`HJon+#lC}E&)BE2(e|{6N*e)H&R3x{j%svi{`Mv+6?%sP3&N?I3 zGD4s4y0KI-ZuH%A(NV!a)!aE+|HCkD+=R7bZy;X{9{=fIZPw?%G5>V(Ur1hzNdD=h zuKi+%q^EWKdAY}DY1YhD-zy#VQ_;=5x9&%86)t*yeu#ce@}aJ>@A)4vM*6=Z$EnEW z0y2`e@ZXV-IP>*alXjd#et$6aSDy)9?O*inbE`hW_VwS>9k<{hH)e1~(cmRRd%nt_ za;vlG`Q}~DxsP^C`fS46PzZFl7YU1@ zcYhRr2qW)bVW_Au7VwofXYK=) z@4Pcw-{pP!8E+Y!JdMLI1dFDNLEe*eQPrcuMv*PvVqZ{r_7)~G4dntG2Mi1v*+pmH6uiPP7np2krpZ_IV zRaOz7XaWBC^LIx$TEdmG4qv)H|=t8G4tKKdDmAxGJ4G?gCA52&mG-)Pwem0 zJ`EUT_!~j)_%DK_3IxE3r@JTpgCGyQN05GV6KQ|Ge)aVC`yMw#uPyEU;mp$8`;2dy zo)=uZ3U_LD$_NLW6JAG;-R;z#rkR=+Qo>j8Fp!6l@^WWEumDq`I2~6g(APotB)un= z06=k`dkXuMM7M5#CyY89TZ<-jmX`cl{_CJd8ix_+tKmc_uSRa8ZG&pBR<`~0f6?~t zQB9m}zyBlzNJ7BL5D*YG6DA=6)DS>Ht%f0BCIk!-5D+cFh~TM+XR9@VNk{-S2?qhu z0tN&;6jAWdY7x)^qN1V|4=pNMPi;N)pslU@?$hUepYHYk)^F{#*V_K$a%ti{!zA~0 zf3NTL`6doL_M*I8fu8_sc+fsco!vo=z@rS5;U%+*nX)tvN+WBP-Bm0ncXev237a?m zj*P2~L9BEiZ^UjY5}E)j2)QCtW5u`=tu`Ak!#x}pRyIzeCU`BcqcY)IT-*f?gyDA3 z-z&l`W(ai32!~i|q?P}BFt_!}gy$J439kN3cW*Y9EHjxoLQ)1=D7;CKmYygX=c;47 zdm1O-ODh{{7JB$&y=q!La1fBZ1c15b`ht-p%Lx8TeRM^H%<3)dXQ&&u47h=i0d6iy z%FFNsx*b#H`*PQm_A{o7Z;3m(5S0f949w|`DD{E?y{d>0+pTmv&ShAPaBr?a+ILZq z5GpYD)Q_)~?@-*#izG`erF4QSLQIwlQeF=QSx6p%KK9-P8=Pc9*=Nb8r1kD`ph-jN zLme!Z5vdWj^a5-GizKW10_1kCJtGxz=dU`4a6|X3mJ}bvJ6d>db{~nu#Ily4yMx; zeNTCj(=mM*9Wj!aO~M9FbBxEQp(qez{V}d5`8Y9D=@SxtTC+qw7c{HG2{#%>f}+~Y zMr3Iy#(x}(tXAolG%U*`Jv8IfZ3U7pZSO`iZxWp#Km?541WeRrco*EHeDnqL<&Bs` zs>Cb_()&T80Tj6EJI%6547i0S)PY^bfE}gF2U6sxT|9ryGhio3H|2#;*|n@}Go*rU z5}#5dg*HOSz%Wg);i$QKcgyFsENbW06OlO8Y0R0jJN#O$rG}Is3OuoF5?85nJk(~N zPduajrHe)|<+C-B4jz2eiSm6fs?tKbL!I1^Y-Af6&I)|V5 zv@!E+r+bAxZ(O@nrIAO|)8KO=a5TCq=Y`ig>L@C*{m;NtegQf%d@feHl%2qy=N>j6 z0sT6c-~&R{MFH%(4vswB211qmmi)h|#Nwy8U$s&9DN`~$S z2(qNA6xvgR*P~<8Z5Kl=MYeiN^jP3%-KXgkRNuhjbON{z4FclyE~5T-6tu+85TfTCzDnFJ#S}}J$MMnjWCptBnIP7U5$QlwI!8fIP4~>De z5C1&tA_tj!qZ-L!}PEIYL%Jc+Q>CMhHv$Y(9oGXBX=o%M>g{7Hn6DH)B-`y>PcbP>D zZ(flCj0N!NMz&1B!w9*a?rY4BPoOTk6PN3=>Vp7s2F4=9Og*rZ{Poc)La=B|#>?Y7 zMctV(Qs$N5K#?}rPPJU~a%Xu7kY)ag5#y`8 z_)JTYnb}JgWn0qgHs~CqJsr(94CDIU#AjMW=#VKL75FA(OxA( zYvf$Ka1Mpm-BBm$d}iC!3YrAT)q{~v;B`k*d2Dbpij&qfAxz43{(^muVbjGJvX40w z%j=zjdiI5^&~YHPb=g@*ltUouXo-X21y4}9C;AdqH)Rs*=|W`+-r-f7rSTB46Bt-3 zZ3dO8;MQ}@QksZK*ya|hSL~>ReWN%}ufm){5vvq6^AR4HCg5;`=s4{vsQKN*Tx1l`?Wwch*BCnR85YarwOy+ z{oY9)!wKG%K=Jc8?VnBSiFMDqmVvs|@dJEnlrp6cET zRe3;??VzJf{pHG3DQZLHaolX_75${u)gEVxJhaRqy=t_SBeM#R;g}6!%PqU~9J^G@ zxQg#kk4I#9Ov%}z9A0&qE?#TrQwjQG4q4}k%e8H(puIkUuA$Hhb|`+cc*c=kT4wyw5W!K2 zGzEJ8)o*hkjsXLFOOq?^+kyv%SDK2ckYJ^vXX;~S=xapVIZD=mQj=`#(hYw$9NaFa zp*G-hAIUUkn3+Ae8ak?wr3e(|`6JNnCKqY*AY4tFwdJ(65x)pBIieMI?H1?~VU3E_Gq# z#WT6AlGQH1M0J|Jek&h7S8(CepihDlk}^W?ogdl8*zwutNutZm2k$u*&zlih7q;z4 zk!8J4V@FEw(=fg4P57!G5}k&^SKq8VU(2`WJ~-|J-w6p(b$n~>S#>*Spa1jwwY1YW zpItml-12YzdFfX3t*OuFW;Iz9h7hlh0~D_#hd0Mu>1dtc)7cWS-&936mwBFqKKO?b zyWo-LQR{?#=j)a|-0b`&iFtXxi|6A|pFPp}Vf#N)9h}`f%@A>p@QNS0R!AF8ndWr=e?gMUeaY-k zw=ArA)n4=254C$di|(9h|2bfL-oA%lFZl4ekHb*HiZgM;5s!*8zPs|Ea^V?r@3Vc? zea*=1 zJvg(X9ZS};A6qw1?)xMmSvj2Ja7mvKofW(kzw`$Kph_{hHE}gAe@8@fk_)6qzyCk- zU#Fjp5Bf<}+>q=tfu~>xMr-nW{PNGL1^YR#Q!;I>1@yeoG+={(dCL3?i*oORpT3ExY2^s4C3 zbF3fy^7Y4G@3}UU=lIi+-9+)qi=VAnK748Gqrsv_rUu2GIQa z&1ZQ_4-r+?lG0g2i^n%iALlTB`L=m02ZBr19aQ!el-SO#Sp%TBqM6o)F!7Nv_kH^u z{gqAE{R6(9G%hmNupUY}uE(ajhd~ zV$%8*`zB7f#hf*3DE*V5-ZyXFy#JdB4(!!!nXfgqsJfK@1hZb} zZ(>)r1q2+u?BVe*do{Cve9y9sIoGDVij}M=OHx|}Q7)$1O%Xy{?TwL779$z=(u!9E zwPkd4wOg>mKd=0s?&=0%=FMN$06+C%uXcI6tTN#FmF2t>p7lTcPb~NzpIhbMFG$W> zDO>WveOLX#6Ankx&w6AI+-L<6Mt=m)v$);3?WS!@H(<=toJ|uK26Ti(O{MZ2W%p9%cN7s_N?Mp&i7>zx^6& zi}2g<@hPPvlQT5%_x@G;kNpb_TS5zdUimZSudIy!wh!foK4|`0^uvoyZu1RW_nz5_ zRXr6gICDmlU2S{PzmpO6?o`#hnCx#h#HOeHoPECMqp}a{o;-dv^l9NI37g1wj~Z3< z?v}T*pS*u>&G=Y8bwcr_H-2=tJ+2mb$Ee4Npl$DGGi$%Q`saa9pFTbD#nO{291mK% zSAO>7TKR_QJ6g9My?q`2d!eoWSZEwr=)m3>Z_1Is7y56@_s`^-o112ht$Ox-E8pF0 z$nid%lDs46%7I(I9(=QAWJY42up7zxm=@ z!C(13?YeV^iZjbgtVScl=TKGx>eV!^XR}9v$qV{_*b1 z{`UoAQr|0jwpl2u!Y1cAYOc|ReeFSC)k=TMimEP81m@XICRrUOBLP3yjL7Q8>KUTY zdPJm9WjMEQulv5@mY4Zz>z+=s&iKRz+RABR zC5=#q!^qewO}8^^%(!(Sr7zjur=Z0SOBq^y8qesBRf_|eI-c2FwQQH0lO-RNeX=wV zvqg%`0qcz6wGL-fsMW*t?OuUvFiAfxE5O*>Rl;vF9mC9sO8wZO2&uH~SKcD(tL2%M z5IE@}L_}GcpN}G#eBdKT2BA9_J8N?v6%O>==^0S!{VrwF?AU`AKgs7p(5g2z9Kvd| zdQIk(YKaoc4PQpv9u;9a(M*NhB@OG`6QKfoWU$pB9Z4p@VC-dy4uPC_6=~Y6EUn69 zL(Du-%3oS{Ntt+2In)bL=+NV=xn7$xANQ{*_Yd#%d31rA2e{sHe5gDaqNp5oZqk#w z7c=Fs$N2IqsaJ*}aL$ph=xaGggU@oropi^M4o(ki7S4sH-M;bU+zYG(5I?*bgrdyi zJ^`w*PMv=s_<&SSuo>Wgw^1!(c}h$|snz1}v@Fx6yp(h&KNFnyj`HhQZI@um5{?tw zV~|_K=3yjF0>De9?u_M2%_}Wlly^LeZc_3=0$ve_6*92<*3yNYgUmVnV7Ge~l3c{K z95mBJQ{})asg^@JXq_=6a!Ff3OlSR{MRI#Ruh$F)HBqdIhhicc%~FW~OkHhM`!3He zzq3LSX9xYVn^>U5(^RAtgz3WdwfVw&w$MhU&BS(xkn0OdY6l|&mkC$_I2LMy?Wm=$(Te7J6}rw_$ckoVT311*gHf-P#XNagF-(XNxZH&CVM`GX8Ku3cjOVVr3cJ%| zKVR9QB~rs$3t2*9iE>*$pd^quUs{CA>k)Yb<67^FqFc50+NNApz5J&u1W!$1Bo>8& zbYB>RS9s?i&bc|m3gdAXrvSqnTIF1c8 zOPZDRR?lOwY%&{~0LzNNkS!ZI3r^1mxKu*zov)~5?5qDN-Ghck+7ED}^Vks#`muZf zGMCA$G%Lns05#r*tmTg9)W~||hNN>hTAAF8Qq;@cikT7;!1M;wizg!(Jf1p|?!lO? z=xmt=!lv@+wj({5hQmNLVgZg5Hz*?xY{uNP*B4e*!kAuy(Cq$%BDk!MaULNvTf-qi zj#R1{9J?XmV>X*BYC|Ks3?iWd=tJ4houtk?rDhu?9)gG2?z|P|T$;X5fcYod_l4g` zrgYNIWuDDCF-JqsFQ6Wa@>O*t^FXhhScXfRd?IAS?{1wF#WJ$x_z_{5%>g64)KXcM zPB&p}e4b^fXXW{LU4wO=-R|#UwMJY|O>|{aOnkQhqr_b+TUknB_+!}f{Rs)v+(X&Y zF*|}wJSpupyGxg}(Mhuye3`~?++pf>g?1boZdlKN!Qt7~!x^Lmg$ixo)6R}hx<%jo zKr8HWatFiS-c*Dq13};n@RI`kYl0i=&oxiF9|5b@^ZFwD3#OFXY_YtRRa_xevRp%2 zVFKa9MYR||JZ`kv>pU{XVace?oU`D3Fuk(9`jR{DY=ikIwUSz=gWHze{XI@yWLvAY|HP(g7E{J3UY9x@w_QrpvN@DGdznNq>UG1!j7O>z*$-G(S+ zIzHliw(`Y}_WWC&N?BW_rFAEpF9Vix9)h8n2nnY4p%~A<>yFNU6j5Ma2)g~#hdj#Z ze^h&V|7-W2d?3B|V&cU2+lnWsKKHl$I^|X7o5#5ih z>1!U-FR%F{?Be?G0ymZ9`u?o^>fz|ffpnq1AZOnVpT93`@E;d87hITr#=@19HQ>Vj za-7e<`HsVwp7eF;{!lbs( z+%q+Ub8Lg*X8f7oQ*}tUrhQW0z_;7B&lf4#*VOixAHkYu8@g?|cQ35H^<~R`WA%)% zi{i}XZx_Y|rvJU1k$)^l1(xH!Zrnx6?*HvR-w4e4?y`@wrtjS^mlp2HjrZL5V9l3P zMrTjy_>efamFQG2ZTBbinD=yRtTc*}nU>va96EcF4)&iG9H>wlM}E`OD!UTc-YPL$&v z&0fUNQZ#QDK5Fm!sr+HV-tP{o((c8WJT@lO_a+5m$pPS&S3 z+iFUSntEFC!^c`rj7hIzkj?!e&QNOxhECo`kH; zmNu}F8!YBP%q=Yy?uBckD&3{x-TsHL3a%<_-LDHmpVAHv&00$&(UR!(ata*awcW1m z1#@&%BAn>On|V{>*1cBMu6XF!KP0EIS&79CxKU=RPeF6*VY!^ZLTr{$vdF7VCRZ0lhkEz;zBcBP zJ53_9#wAEr$F-{zkunj7cf{qOEcbMDXfYegr7jA%(eIvt(mRk5^%_jXjhEksbdH&< zKJ|*;V5SLtpY*82EU&ed6*7?5HDTeD6{QMS-53OcuZfhKWd(jew8C z6<0D0NMe(mm+6>%wK+qD%Sz=64P+WMGlEEyLdGK)nOH0u51~O9E%{yfBFrNcNkGph zFy@LlO{RG`mJCu%*zy2&rcft92(&jZDufeeD+!2}mmwUynD_R>o1LAfcuKqCNNw59Q zl6f*~2a#%_L-%1WdooII-4TQCDJ}K12Q(rGg%Ry zydHS+Y(C0sxumYwCJ=;&Tf;Z$k@e)%w zM_tz(YNHM4!{x9YlYxLA9xs7GVS(12%_O~cd?GBh@!pul8PfbEa&0*m56uUhCNkPW z*6|EH7#`%yP(xlT2lmxMG#DUdi_M z_VreyW*3n}#^vP66Ff+>OsG=OZSo=yAsu9z*i6dKvCMW^nG7wcGdGi@#tmUGFf@kE zPC#(bjLwurb4(`;Vn$bFga;osIPy6!)NqWKWQnoYrUoh4xCmtn%Xnr$`-1x{cFfyA zL23AW)jqD<4UXCgOlv3r$(t;O(+FA`I~!+#SzrNZU(mq|o7RKA0(Ugi$U`y)2xd1| z(~ETKyAYWj6rG9g-P-KDaw z>2^8l!$$gK>G=xsxEhY;I!m3)wNIzx!6o~`adhsTtK<11HC)!PJceUoLae66l&|1gCj7s*#tki(o2G%DK%R|m0?B$o(O`R6B1`Z;U`1>h`L5{J(?d) zhy6zD<9JA}A&5njPHMQ9w|tXPuC15Xa@e74OWgug650xQa{s|OZQFotk*RPHfqUY(`o+>CRyD>C3ip2WyDhcm)E ztw>V2i&Cc?9Xj*ni6XC&d@ruh?*>qGRln8?&RsKKtzPAIt7P82SCptajRX${kGf^W2dA z>;pF{me}fi4&A6(LJ>SK&FmOi+WBV9y7Z`R5o=%j$v*n=qi1gm+j{Va7uW-(y@P$q ztj!T81k>Wz*Hp72&s_KTx4sBDf-Cv=FE#l6_0#_M6x%;9O0egbpRaC?D!BJbH2&1Q zLt8Bge81V+Qt8Z`?U6r>U71?;gP(KP&Th-1bBL^O;a;Da57#O-d#b;!`KTcL!k&pU z)d?PsONRFE4aj-sw@Npgb1F;M#|$+1f9t+ytZLM83$e*D-*e)^+hd$Q?ns>f)tGnP zI}3{{r$h|8O<3o@%kM@;;ZJufy#D?X!2ek7Jh0k)BQ=Nm9tF#taq%66|LKeu%HhY) z=YKK3y_ELGJTSL*?zRf6h_~mV{Lcwd!(UX7ym~zLRnf0+Pk#G=Pb<&v9#gQP=eKj? zbZ1BCbJgFQXLnDrElwJ`e`*6XeskWoWApEi(_Gky%nvzww7e{~{yWzbzdm!U4y1qA z*ExpS^G9QU-?Bq1KdHa=UEAQwi*r89cKf(Heslg00gHZ5{$qA&I^6zk-;UDc(|^vN z6PKU(W9_R~BZ<@#v&VrHqBRqMIc}!%%cLl>;nykCHtk+OcK6O+X4*IB?WYCKizB9# zO)85C?+Na?xUGVyIU4eN2F#IcxwCB9*MY8=_6WC4n)Ojg@OO__Jl-&M=BkCQN6Y3W zJo>Wx+R3f9{m#_NSs5AYWP|H3+ztHUe(1inFaExH(9nO%+;QOMF;%Zm|Ca%)@2@J? zdrkcG;gE`;iP#q3qnyjE8??)hT5mq-;O*}iduGhCZw6^Wuvbo<|1@jZSdUgpPQslV=@0>Ehc;x4i>1))rA56s4P&0gm+(uK{Z3ma(4}!@?$=TiQ>D9GH|lMCltalH_?lI|1XR@}N=7-;GvB$42wv zJ{d`V7~s=P=S5kx(^Z{S;RGtznzM^c=4_kHi4e^72V`0QMd<v@jR){i8LZtF{r>^RN#E^;d zD;uj%&SC@mHI6uWhsXPJ6U!O`6o{c=PE>{i?at)L$Tw7?&O;Z#iU!C}+ zeQr`>Oa>C`>Y%JhYpA$*xYY?_`k8RH(-#d5u~0;n%^L_R-V(rW5Hm0aLYu54U6ss% zIJs99*Z5FK@|)u2`f!3Fl9FK1&{+?-fgEy#2(lrp>PXwAlN+gpldH^_D>|8A!gU;Z z8;{Qc@o2&*dP;7MF)xv8v)2kL3lZ$Grb%fJC+NAM`p!a>$GguHmLnp__a0wR!@W@D z-P6NZFRQZZ9qFlsk|;BuXRr$Gq@xj66SfT7HrE<%+l9&ZNM$+SR!ZfR8I|05R2tw? zpk<`KKwKEUWQ5@G>kgI$rVIKHCwUwShn1T<_>aN~%cES^03Z$yCL=r#YChcRC(vsmaA`M73=rKI zS4-70y3tzNK<^r_)$#4?mVv#^%aLlNM;#)sP zs;9Z$lEb`)e^Ky+{Y_AIdR<{O1Ep;h?oCJB)NUoJ) zy_On$eYI3SX6bB?BClGbSN#rXxYz5Ux1VEAICPW{Sp8*{Om|B-m|(ae^RG)2jml3sq%Ti0p zpAz!T&rj8=3X~ybNYpESSv6=!qC8(t%3+__q9JQW9+E=qhPmJqiG|}n1VqY} zRkpE=pyrFB$cn{&E_9Q#h-pAYLV#eflrk7}u3XvK8%5CL^R(trZa>B|%vCW*iT=G5 zzCm9~N_cG?o2&q$_vhpK&H#6ReYX{#=c$vMgM?(UrOki9Q0D-?OLC3UIx1n2a8S|& zmx82R1S?co2C<+aTC3(JNsJ&crhk7&)7<4~RGumxAsfT#DwhO@t5~ZhdbE{Y^ z{r)NN%-KKWIFvQ>5o-V{1}Va;TDbB+-R#^6D93o zSWNZxHlZ1$>mK1n^K3z8-FExmn>_a)6;dMDVlaP;XE z-6MPH&QjguS;dccq&3tBXznbS?z?3${ZEha=?9~mY;O)m1^;y zeDL2dm;U4B$>8PuKM!sFd$a*me}6$sHm-T_>)0Z zF5R$U!|RsQ>wXjzU806@r(}KRnfLO+1?dk{lJY6Soc@i5`pdJ&hFzNPxiXi`om}tc zV4TyM>ixZacihpWpI)qdHRtIcZ{9t&7{af=|Eld@moM(U|#8$2nBlkGipK+S?GHmKuRV`tnK*_TqS9K!SAZG7L{fN_tr)M-BsA@W?^A24q>WXhX zw90jx5>v zeSxh3@X;zp7iev$Vk2dkiNL|wrJ5b=GUzBiSZ-s&yem_!>&8T)sz2F809(YPH^RV- z&a>lx@v1#R-bc5R3msWIE+w~mnf52OlC}F zj}8P-ed1VUJw+TTI0tlRkDJSUM*z#922?2AOj#Wp0phHXip1kvUNnA1BO?Qu4`o*y z^%@i2MjI_+1FE|mAFR_0P|Oa7eVwtvo?RBsxtCY-rDuow)bY;L6a>6eww0Eg@7Mzn z{XL$s@P0;M&!J86FIFSFvVCgHJV;LixZ92b_b|Fj z+y2548rNO%%XKX!tcY`kfjFhe^Eh0sfKJJdDj$xB81_!&35!H7{QL6zyp~nd?@xv- zPX1B~g+p&UB``j;)sgn6r|LiELA4WmQTe@49uIte)of>~6Nd5Z_WMs)L9NuO-<#&lPt}sp*nhfiwy+|+`?ZOqA z(IjpYbK;y3N8md^Xr3@Le$)Nq?*&ukTDpi+3O%&1~(`g19<&^OF`8t@eZJ|)^Mts^023S!x)P(NhwLc1#;+9YD+wdf}wkFl<^Siq^!`yKmVqs!*be+6`nEpU1@@66r;?|qDc-ku_}%H$_RnYalk=0(v^6j|%l`CZ}T z#>hKiw@ZM>2FHRVQ7ke+#p~l1p}~JOs}=Yptd_LfOYHUe3D&a)M^}o~ih1K|7$E>1 zi2&vTN1)PkNE^pw(?$pFg4yv8rVlDGQ~ALr;7F0@W#J;A^@0Hc1;XITZB|6hkmNk7 zGzz0z)PD4UOuNv=5O#L(MuAh~2wUR`?jf6}Lot>y$`DTD)-G?h>vUEWRz&1mX_NZ~ zf!6Pmz(zpF2#etGtQ^v^58`@2gJsv6+~q~-%cV}ufA99Hf7B`S!EWDHH){Q_-QEeJ zu2~SY5hg>%Upk!-v#ZCJe+pUQ^lazJ?e!buw@uepXJ&n(Zr404sO`(OH8U9(dD{4JATz2BGCx40&IH2UIav^ z3{?lxF~8#6bhsbj%2x!Ql?{7;YO5N2u{byV)>7DZ{@(GqvW=$w$|e0%PP}dK{%Yw& zCH=?FKkt2NH5Xpq_Oga;?tV0pY0BHfp4>f>`@p#*_Wh3+s@Dc~zL=li{PH`6*3yvLs^>%e&ViVTBvs5!!Db?LLd#=MH3)WB_qZ8-6_-#ytw7>=S z`8xK4r4}7YXUxdf&QCA_v%EuMbmm>hpi`=^|8M>BGt)}GRE=?&dE>y=q+QB`VIA`$ zYiE90`?>$jD@xzrX0CBLK3IBp?dqUNnQh&C7EuLg<@ya1R(bjHxA&Zhd?`Kue`%Lr zs3zwM@Bie4l-Q4{Vu<0y7b$P-1EmKK3nOO<##GKaOIB3*`uLj+V{0P zeDm?Q`UNXLQ7I`rNh$ZugGpms z{g&R(F4(fe`Il{Le}g`Ma3FL}#C7`ojpiTyyyDMSK7EK?UgsEAGn{%jx<)z1hFy{c zIwn@hOG6e+S7QWO{;5x-o$yqxJLEKK^?%eiA@KjhPG8D7?AtbV@o)Wu3A*{-ebKBn zi=IYCj|t8`cX{)Piyb{X^9MqcW4bqsY(cHXj-URAU_NI2x4)cUU2=YPdVI#(mzl#) z4mD(1@1T#`e%(K{?Bx&FcwBSYf{rTpZ&z+Oz3Fa97x%+Cr&Bk++Bg1aaM&RcZN4|h z5RiO-1uHk;s|8IYiuLnq4O#8Sf`@VVJeuM8FMN6Xv4sA~HfQnP7S5bygWu1aT$Irk zx=wCK6Fgl_i;tL=WPGkZwadw*>!19~Dj5FI>G-m@%yaME|A?v@_x_!7?#qHI>+CCE zb~Q8RQxc{I_w@91`y(yOtJWiR$w%0A2m7yRqQ_nS@nN}nLUP!`1NpmtZgHEInVp#_ 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 diff --git a/test/wpt/tests/fetch/api/request/request-bad-port.any.js b/test/wpt/tests/fetch/api/request/request-bad-port.any.js index 7d192452fda..915063bab56 100644 --- a/test/wpt/tests/fetch/api/request/request-bad-port.any.js +++ b/test/wpt/tests/fetch/api/request/request-bad-port.any.js @@ -3,92 +3,92 @@ // list of bad ports according to // https://fetch.spec.whatwg.org/#port-blocking var BLOCKED_PORTS_LIST = [ - 1, // tcpmux - 7, // echo - 9, // discard - 11, // systat - 13, // daytime - 15, // netstat - 17, // qotd - 19, // chargen - 20, // ftp-data - 21, // ftp - 22, // ssh - 23, // telnet - 25, // smtp - 37, // time - 42, // name - 43, // nicname - 53, // domain - 69, // tftp - 77, // priv-rjs - 79, // finger - 87, // ttylink - 95, // supdup - 101, // hostriame - 102, // iso-tsap - 103, // gppitnp - 104, // acr-nema - 109, // pop2 - 110, // pop3 - 111, // sunrpc - 113, // auth - 115, // sftp - 117, // uucp-path - 119, // nntp - 123, // ntp - 135, // loc-srv / epmap - 137, // netbios-ns - 139, // netbios-ssn - 143, // imap2 - 161, // snmp - 179, // bgp - 389, // ldap - 427, // afp (alternate) - 465, // smtp (alternate) - 512, // print / exec - 513, // login - 514, // shell - 515, // printer - 526, // tempo - 530, // courier - 531, // chat - 532, // netnews - 540, // uucp - 548, // afp - 554, // rtsp - 556, // remotefs - 563, // nntp+ssl - 587, // smtp (outgoing) - 601, // syslog-conn - 636, // ldap+ssl - 989, // ftps-data - 990, // ftps - 993, // ldap+ssl - 995, // pop3+ssl - 1719, // h323gatestat - 1720, // h323hostcall - 1723, // pptp - 2049, // nfs - 3659, // apple-sasl - 4045, // lockd - 4190, // sieve - 5060, // sip - 5061, // sips - 6000, // x11 - 6566, // sane-port - 6665, // irc (alternate) - 6666, // irc (alternate) - 6667, // irc (default) - 6668, // irc (alternate) - 6669, // irc (alternate) - 6679, // osaut - 6697, // irc+tls - 10080, // amanda + 1, // tcpmux + 7, // echo + 9, // discard + 11, // systat + 13, // daytime + 15, // netstat + 17, // qotd + 19, // chargen + 20, // ftp-data + 21, // ftp + 22, // ssh + 23, // telnet + 25, // smtp + 37, // time + 42, // name + 43, // nicname + 53, // domain + 69, // tftp + 77, // priv-rjs + 79, // finger + 87, // ttylink + 95, // supdup + 101, // hostriame + 102, // iso-tsap + 103, // gppitnp + 104, // acr-nema + 109, // pop2 + 110, // pop3 + 111, // sunrpc + 113, // auth + 115, // sftp + 117, // uucp-path + 119, // nntp + 123, // ntp + 135, // loc-srv / epmap + 137, // netbios-ns + 139, // netbios-ssn + 143, // imap2 + 161, // snmp + 179, // bgp + 389, // ldap + 427, // afp (alternate) + 465, // smtp (alternate) + 512, // print / exec + 513, // login + 514, // shell + 515, // printer + 526, // tempo + 530, // courier + 531, // chat + 532, // netnews + 540, // uucp + 548, // afp + 554, // rtsp + 556, // remotefs + 563, // nntp+ssl + 587, // smtp (outgoing) + 601, // syslog-conn + 636, // ldap+ssl + 989, // ftps-data + 990, // ftps + 993, // ldap+ssl + 995, // pop3+ssl + 1719, // h323gatestat + 1720, // h323hostcall + 1723, // pptp + 2049, // nfs + 3659, // apple-sasl + 4045, // lockd + 4190, // sieve + 5060, // sip + 5061, // sips + 6000, // x11 + 6566, // sane-port + 6665, // irc (alternate) + 6666, // irc (alternate) + 6667, // irc (default) + 6668, // irc (alternate) + 6669, // irc (alternate) + 6679, // osaut + 6697, // irc+tls + 10080, // amanda ]; BLOCKED_PORTS_LIST.map(function(a){ - promise_test(function(t){ - return promise_rejects_js(t, TypeError, fetch("http://example.com:" + a)) - }, 'Request on bad port ' + a + ' should throw TypeError.'); + promise_test(function(t){ + return promise_rejects_js(t, TypeError, fetch(`${location.origin}:${a}`)) + }, 'Request on bad port ' + a + ' should throw TypeError.'); }); diff --git a/test/wpt/tests/fetch/api/request/request-constructor-init-body-override.any.js b/test/wpt/tests/fetch/api/request/request-constructor-init-body-override.any.js new file mode 100644 index 00000000000..27bb991871a --- /dev/null +++ b/test/wpt/tests/fetch/api/request/request-constructor-init-body-override.any.js @@ -0,0 +1,21 @@ +promise_test(async function () { + const req1 = new Request("https://example.com/", { + body: "req1", + method: "POST", + }); + + const text1 = await req1.text(); + assert_equals( + text1, + "req1", + "The body of the first request should be 'req1'." + ); + + const req2 = new Request(req1, { body: "req2" }); + const bodyText = await req2.text(); + assert_equals( + bodyText, + "req2", + "The body of the second request should be overridden to 'req2'." + ); +}, "Check that the body of a new request can be overridden when created from an existing Request object"); diff --git a/test/wpt/tests/fetch/api/request/request-consume-empty.any.js b/test/wpt/tests/fetch/api/request/request-consume-empty.any.js index 034a86041a7..0bf9672a795 100644 --- a/test/wpt/tests/fetch/api/request/request-consume-empty.any.js +++ b/test/wpt/tests/fetch/api/request/request-consume-empty.any.js @@ -8,23 +8,11 @@ function checkBodyText(test, request) { }); } -function checkBodyBlob(test, request) { - return request.blob().then(function(bodyAsBlob) { - var promise = new Promise(function(resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, "", "Resolved value should be empty"); - assert_false(request.bodyUsed); - }); - }); +async function checkBodyBlob(test, request) { + const bodyAsBlob = await request.blob(); + const body = await bodyAsBlob.text(); + assert_equals(body, "", "Resolved value should be empty"); + assert_false(request.bodyUsed); } function checkBodyArrayBuffer(test, request) { diff --git a/test/wpt/tests/fetch/api/request/request-consume.any.js b/test/wpt/tests/fetch/api/request/request-consume.any.js index aff5d65244a..3db9e8f2657 100644 --- a/test/wpt/tests/fetch/api/request/request-consume.any.js +++ b/test/wpt/tests/fetch/api/request/request-consume.any.js @@ -9,26 +9,15 @@ function checkBodyText(request, expectedBody) { }); } -function checkBodyBlob(request, expectedBody, checkContentType) { - return request.blob().then(function(bodyAsBlob) { - if (checkContentType) - assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type"); - - var promise = new Promise(function (resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, expectedBody, "Retrieve and verify request's body"); - assert_true(request.bodyUsed, "body as blob: bodyUsed turned true"); - }); - }); +async function checkBodyBlob(request, expectedBody, checkContentType) { + const bodyAsBlob = await request.blob(); + + if (checkContentType) + assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type"); + + const body = await bodyAsBlob.text(); + assert_equals(body, expectedBody, "Retrieve and verify request's body"); + assert_true(request.bodyUsed, "body as blob: bodyUsed turned true"); } function checkBodyArrayBuffer(request, expectedBody) { diff --git a/test/wpt/tests/fetch/api/resources/dump-authorization-header.py b/test/wpt/tests/fetch/api/resources/dump-authorization-header.py index a651aeb4e8b..0d82809f59d 100644 --- a/test/wpt/tests/fetch/api/resources/dump-authorization-header.py +++ b/test/wpt/tests/fetch/api/resources/dump-authorization-header.py @@ -2,6 +2,11 @@ def main(request, response): headers = [(b"Content-Type", "text/html"), (b"Cache-Control", b"no-cache")] + if (request.GET.first(b"strip_auth_header", False) and request.method == "OPTIONS" and + b"authorization" in request.headers.get(b"Access-Control-Request-Headers", b"").lower()): + # Auth header should not be sent for preflight after cross-origin redirect. + return 500, headers, "fail" + if b"Origin" in request.headers: headers.append((b"Access-Control-Allow-Origin", request.headers.get(b"Origin", b""))) headers.append((b"Access-Control-Allow-Credentials", b"true")) diff --git a/test/wpt/tests/fetch/api/resources/huge-response.py b/test/wpt/tests/fetch/api/resources/huge-response.py new file mode 100644 index 00000000000..16a60078e53 --- /dev/null +++ b/test/wpt/tests/fetch/api/resources/huge-response.py @@ -0,0 +1,22 @@ +# A Python script that generates a huge response. Implemented as a script to +# avoid needing to add a huge file to the repository. + +TOTAL_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB +CHUNK_SIZE = 1024 * 1024 # 1 MB + +assert TOTAL_SIZE % CHUNK_SIZE == 0 + + +def main(request, response): + response.headers.set(b"Content-type", b"text/plain") + response.headers.set(b"Content-Length", str(TOTAL_SIZE).encode()) + response.headers.set(b"Cache-Control", b"max-age=86400") + response.write_status_headers() + + chunk = bytes(CHUNK_SIZE) + total_sent = 0 + + while total_sent < TOTAL_SIZE: + if not response.writer.write(chunk): + break + total_sent += CHUNK_SIZE diff --git a/test/wpt/tests/fetch/api/response/response-arraybuffer-realm.window.js b/test/wpt/tests/fetch/api/response/response-arraybuffer-realm.window.js new file mode 100644 index 00000000000..19a5dfa5ff6 --- /dev/null +++ b/test/wpt/tests/fetch/api/response/response-arraybuffer-realm.window.js @@ -0,0 +1,23 @@ +// META: title=realm of Response arrayBuffer() + +'use strict'; + +promise_test(async () => { + await new Promise(resolve => { + onload = resolve; + }); + + let iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + iframe.srcdoc = ''; + await new Promise(resolve => { + iframe.onload = resolve; + }); + + let otherRealm = iframe.contentWindow; + + let ab = await window.Response.prototype.arrayBuffer.call(new otherRealm.Response('')); + + assert_true(ab instanceof otherRealm.ArrayBuffer, "ArrayBuffer should be created in receiver's realm"); + assert_false(ab instanceof ArrayBuffer, "ArrayBuffer should not be created in the arrayBuffer() methods's realm"); +}, 'realm of the ArrayBuffer from Response arrayBuffer()'); diff --git a/test/wpt/tests/fetch/api/response/response-clone.any.js b/test/wpt/tests/fetch/api/response/response-clone.any.js index f5cda75149e..c0c844948db 100644 --- a/test/wpt/tests/fetch/api/response/response-clone.any.js +++ b/test/wpt/tests/fetch/api/response/response-clone.any.js @@ -135,6 +135,7 @@ testReadableStreamClone(new Uint16Array(arrayBuffer, 2), "Uint16Array"); testReadableStreamClone(new Uint32Array(arrayBuffer), "Uint32Array"); testReadableStreamClone(typeof BigInt64Array === "function" ? new BigInt64Array(arrayBuffer) : undefined, "BigInt64Array"); testReadableStreamClone(typeof BigUint64Array === "function" ? new BigUint64Array(arrayBuffer) : undefined, "BigUint64Array"); +testReadableStreamClone(typeof Float16Array === "function" ? new Float16Array(arrayBuffer) : undefined, "Float16Array"); testReadableStreamClone(new Float32Array(arrayBuffer), "Float32Array"); testReadableStreamClone(new Float64Array(arrayBuffer), "Float64Array"); testReadableStreamClone(new DataView(arrayBuffer, 2, 8), "DataView"); diff --git a/test/wpt/tests/fetch/api/response/response-consume-empty.any.js b/test/wpt/tests/fetch/api/response/response-consume-empty.any.js index 0fa85ecbcb2..a5df3562586 100644 --- a/test/wpt/tests/fetch/api/response/response-consume-empty.any.js +++ b/test/wpt/tests/fetch/api/response/response-consume-empty.any.js @@ -8,23 +8,12 @@ function checkBodyText(test, response) { }); } -function checkBodyBlob(test, response) { - return response.blob().then(function(bodyAsBlob) { - var promise = new Promise(function(resolve, reject) { - var reader = new FileReader(); - reader.onload = function(evt) { - resolve(reader.result) - }; - reader.onerror = function() { - reject("Blob's reader failed"); - }; - reader.readAsText(bodyAsBlob); - }); - return promise.then(function(body) { - assert_equals(body, "", "Resolved value should be empty"); - assert_false(response.bodyUsed); - }); - }); +async function checkBodyBlob(test, response) { + const bodyAsBlob = await response.blob(); + const body = await bodyAsBlob.text(); + + assert_equals(body, "", "Resolved value should be empty"); + assert_false(response.bodyUsed); } function checkBodyArrayBuffer(test, response) { diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cache.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cache.tentative.https.html new file mode 100644 index 00000000000..c8bcf7fdf12 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cache.tentative.https.html @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cookies.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cookies.tentative.https.html new file mode 100644 index 00000000000..aa1673e88c8 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-cookies.tentative.https.html @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-storage.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-storage.tentative.https.html new file mode 100644 index 00000000000..22747eb6565 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data-storage.tentative.https.html @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html new file mode 100644 index 00000000000..c7b3b7c3a5a --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html new file mode 100644 index 00000000000..23a271d4818 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html new file mode 100644 index 00000000000..6f6a792ade6 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html b/test/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html new file mode 100644 index 00000000000..f0782aff3bd --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html @@ -0,0 +1,61 @@ + + + + + + + + + + + diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py b/test/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py new file mode 100644 index 00000000000..0db51bf797f --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py @@ -0,0 +1,4 @@ +def main(request, response): + directive = request.GET.first(b"directive") + response.headers.set(b"Clear-Site-Data", b"\"" + directive + b"\"") + return b"OK" diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py b/test/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py new file mode 100644 index 00000000000..4be4b555640 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py @@ -0,0 +1,28 @@ +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + response.headers.set(b"Content-Type", b"text/plain") + response.headers.set( + b"Content-Dictionary", + b":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:") + + # `br_d_data` and `zstd_d_data` are generated using the following commands: + # + # $ echo "This is a test dictionary." > /tmp/dict + # $ echo -n "This is compressed test data using a test dictionary" \ + # > /tmp/data + # $ brotli -o /tmp/out.brd -D /tmp/dict /tmp/data + # $ xxd -p /tmp/out.brd | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + br_d_data = b"\xa1\x98\x01\x80\x62\xa4\x4c\x1d\xdf\x12\x84\x8c\xae\xc2\xca\x60\x22\x07\x6e\x81\x05\x14\xc9\xb7\xc3\x44\x8e\xbc\x16\xe0\x15\x0e\xec\xc1\xee\x34\x33\x3e\x0d" + # $ zstd -o /tmp/out.zstdd -D /tmp/dict /tmp/data + # $ xxd -p /tmp/out.zstdd | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + zstd_d_data = b"\x28\xb5\x2f\xfd\x24\x34\xf5\x00\x00\x98\x63\x6f\x6d\x70\x72\x65\x73\x73\x65\x64\x61\x74\x61\x20\x75\x73\x69\x6e\x67\x03\x00\x59\xf9\x73\x54\x46\x27\x26\x10\x9e\x99\xf2\xbc" + + if b'content_encoding' in request.GET: + content_encoding = request.GET.first(b"content_encoding") + response.headers.set(b"Content-Encoding", content_encoding) + if content_encoding == b"br-d": + # Send the pre compressed file + response.content = br_d_data + if content_encoding == b"zstd-d": + # Send the pre compressed file + response.content = zstd_d_data diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js b/test/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js new file mode 100644 index 00000000000..7d86f594a80 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js @@ -0,0 +1,120 @@ + +const kDefaultDictionaryContent = 'This is a test dictionary.\n'; +const kDefaultDictionaryHashBase64 = + ':U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:'; +const kRegisterDictionaryPath = './resources/register-dictionary.py'; +const kCompressedDataPath = './resources/compressed-data.py'; +const kExpectedCompressedData = + `This is compressed test data using a test dictionary`; +const kCheckAvailableDictionaryHeaderMaxRetry = 10; +const kCheckAvailableDictionaryHeaderRetryTimeout = 200; +const kCheckPreviousRequestHeadersMaxRetry = 5; +const kCheckPreviousRequestHeadersRetryTimeout = 250; + +// Gets the remote URL corresponding to `relative_path`. +function getRemoteHostUrl(relative_path) { + const remote_origin = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); + let result = new URL(relative_path, location.href); + result.protocol = remote_origin.protocol; + result.hostname = remote_origin.hostname; + result.port = remote_origin.port; + return result.href; +} + +// Calculates the Structured Field Byte Sequence containing the SHA-256 hash of +// the contents of the dictionary text. +async function calculateDictionaryHash(dictionary_text) { + const encoded = (new TextEncoder()).encode(dictionary_text); + const digest = await crypto.subtle.digest('SHA-256', encoded) + return ':' + btoa(String.fromCharCode(...new Uint8Array(digest))) + ':'; +} + +// Checks the HTTP request headers which is sent to the server. +async function checkHeaders(check_remote = false) { + let url = './resources/echo-headers.py'; + if (check_remote) { + url = getRemoteHostUrl(url); + } + return await (await fetch(url)).json(); +} + +// Checks the "available-dictionary" header in the HTTP request headers. +async function checkAvailableDictionaryHeader(check_remote = false) { + return (await checkHeaders(check_remote))['available-dictionary']; +} + +// Waits until the "available-dictionary" header is available in the HTTP +// request headers, and returns the header. If the header is not available after +// the specified number of retries, returns an error message. If the +// `expected_header` is specified, this method waits until the header is +// available and matches the `expected_header`. +async function waitUntilAvailableDictionaryHeader(test, { + max_retry = kCheckAvailableDictionaryHeaderMaxRetry, + expected_header = undefined, + check_remote = false +}) { + for (let retry_count = 0; retry_count <= max_retry; retry_count++) { + const header = await checkAvailableDictionaryHeader(check_remote); + if (header) { + if (expected_header === undefined || header == expected_header) { + return header; + } + } + await new Promise( + (resolve) => test.step_timeout( + resolve, kCheckAvailableDictionaryHeaderRetryTimeout)); + } + return '"available-dictionary" header is not available'; +} + +// Checks the HTTP request headers which was sent to the server with `token` +// to register a dictionary. +async function checkPreviousRequestHeaders(token, check_remote = false) { + let url = `./resources/register-dictionary.py?get_previous_header=${token}`; + if (check_remote) { + url = getRemoteHostUrl(url); + } + return await (await fetch(url)).json(); +} + +// Waits until the HTTP request headers which was sent to the server with +// `token` to register a dictionary is available, and returns the header. If the +// header is not available after the specified number of retries, returns +// `undefined`. +async function waitUntilPreviousRequestHeaders( + test, token, check_remote = false) { + for (let retry_count = 0; retry_count <= kCheckPreviousRequestHeadersMaxRetry; + retry_count++) { + const header = + (await checkPreviousRequestHeaders(token, check_remote))['headers']; + if (header) { + return header; + } + await new Promise( + (resolve) => test.step_timeout( + resolve, kCheckPreviousRequestHeadersRetryTimeout)); + } + return undefined; +} + +// Clears the site data for the specified directive by sending a request to +// `./resources/clear-site-data.py` which returns `Clear-Site-Data` response +// header. +// Note: When `directive` is 'cache' or 'cookies' is specified, registered +// compression dictionaries should be also cleared. +async function clearSiteData(directive = 'cache') { + return await (await fetch( + `./resources/clear-site-data.py?directive=${directive}`)) + .text(); +} + +// A utility test method that adds the `clearSiteData()` method to the +// testharness cleanup function. This is intended to ensure that registered +// dictionaries are cleared in tests and that registered dictionaries do not +// interfere with subsequent tests. +function compression_dictionary_promise_test(func, name, properties) { + promise_test(async (test) => { + test.add_cleanup(clearSiteData); + await func(test); + }, name, properties); +} diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py b/test/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py new file mode 100644 index 00000000000..aabd99eb101 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py @@ -0,0 +1,10 @@ +import json + +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + headers = {} + for header in request.headers: + key = header.decode('utf-8') + value = request.headers.get(header).decode('utf-8') + headers[key] = value + return json.dumps(headers) diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/empty.html b/test/wpt/tests/fetch/compression-dictionary/resources/empty.html new file mode 100644 index 00000000000..0e76edd65b7 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/empty.html @@ -0,0 +1 @@ + diff --git a/test/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py b/test/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py new file mode 100644 index 00000000000..0bd57225ef1 --- /dev/null +++ b/test/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py @@ -0,0 +1,37 @@ +import json + +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + match = b"/fetch/compression-dictionary/resources/*" + content = b"This is a test dictionary.\n" + if b"match" in request.GET: + match = request.GET.first(b"match") + if b"content" in request.GET: + content = request.GET.first(b"content") + + token = request.GET.first(b"save_header", None) + if token is not None: + headers = {} + for header in request.headers: + key = header.decode('utf-8') + value = request.headers.get(header).decode('utf-8') + headers[key] = value + with request.server.stash.lock: + request.server.stash.put(token, json.dumps(headers)) + + previous_token = request.GET.first(b"get_previous_header", None) + if previous_token is not None: + result = {} + with request.server.stash.lock: + store = request.server.stash.take(previous_token) + if store is not None: + headers = json.loads(store) + result["headers"] = headers + return json.dumps(result) + + options = b"match=\"" + match + b"\"" + if b"id" in request.GET: + options += b", id=\"" + request.GET.first(b"id") + b"\"" + response.headers.set(b"Use-As-Dictionary", options) + response.headers.set(b"Cache-Control", b"max-age=3600") + return content diff --git a/test/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js b/test/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js new file mode 100644 index 00000000000..43ea90a336c --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js @@ -0,0 +1,12 @@ +// META: global=window + +[ + "arrayBuffer", +].forEach(method => { + promise_test(t => { + return fetch("resources/bad-br-body.py").then(res => { + assert_equals(res.status, 200); + return promise_rejects_js(t, TypeError, res[method]()); + }); + }, "Consuming the body of a resource with bad br content with " + method + "() should reject"); +}); diff --git a/test/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js b/test/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js new file mode 100644 index 00000000000..1427dd73026 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js @@ -0,0 +1,55 @@ +// META: global=window,worker + +const EXPECTED_SIZE = 27000000; +const EXPECTED_SHA256 = [ + 74, 100, 37, 243, 147, 61, 116, 60, 241, 221, 126, + 18, 24, 71, 204, 28, 50, 62, 201, 130, 152, 225, + 217, 183, 10, 201, 143, 214, 102, 155, 212, 248, + ]; + +promise_test(async () => { + const response = await fetch('resources/big.text.br'); + assert_true(response.ok); + const arrayBuffer = await response.arrayBuffer(); + assert_equals(arrayBuffer.byteLength, EXPECTED_SIZE, + 'uncompressed size should match'); + const sha256 = await crypto.subtle.digest('SHA-256', arrayBuffer); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large br data should be decompressed successfully'); + +promise_test(async () => { + const response = await fetch('resources/big.text.br'); + assert_true(response.ok); + const reader = response.body.getReader({mode: 'byob'}); + let offset = 0; + // Pre-allocate space for the output. The response body will be read + // chunk-by-chunk into this array. + let ab = new ArrayBuffer(EXPECTED_SIZE); + while (offset < EXPECTED_SIZE) { + // To stress the data pipe, we want to use a different size read each + // time. Unfortunately, JavaScript doesn't have a seeded random number + // generator, so this creates the possibility of making this test flaky if + // it doesn't work for some edge cases. + let size = Math.floor(Math.random() * 65535 + 1); + if (size + offset > EXPECTED_SIZE) { + size = EXPECTED_SIZE - offset; + } + const u8 = new Uint8Array(ab, offset, size); + const { value, done } = await reader.read(u8); + ab = value.buffer; + // Check that we got our original array back. + assert_equals(ab.byteLength, EXPECTED_SIZE, + 'backing array should be the same size'); + assert_equals(offset, value.byteOffset, 'offset should match'); + assert_less_than_equal(value.byteLength, size, + 'we should not have got more than we asked for'); + offset = value.byteOffset + value.byteLength; + if (done) break; + } + assert_equals(offset, EXPECTED_SIZE, + 'we should have read the whole thing'); + const sha256 = await crypto.subtle.digest('SHA-256', new Uint8Array(ab)); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large br data should be decompressed successfully with byte stream'); diff --git a/test/wpt/tests/fetch/content-encoding/br/br-body.https.any.js b/test/wpt/tests/fetch/content-encoding/br/br-body.https.any.js new file mode 100644 index 00000000000..2c2dbb5d293 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/br-body.https.any.js @@ -0,0 +1,15 @@ +// META: global=window,worker + +const expectedDecompressedSize = 10500; +[ + "text", + "octetstream" +].forEach(contentType => { + promise_test(async t => { + let response = await fetch(`resources/foo.${contentType}.br`); + assert_true(response.ok); + let arrayBuffer = await response.arrayBuffer() + let u8 = new Uint8Array(arrayBuffer); + assert_equals(u8.length, expectedDecompressedSize); + }, `fetched br data with content type ${contentType} should be decompressed.`); +}); diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py b/test/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py new file mode 100644 index 00000000000..0710e7ffdee --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py @@ -0,0 +1,3 @@ +def main(request, response): + headers = [(b"Content-Encoding", b"br")] + return headers, b"not actually br" diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/big.text.br b/test/wpt/tests/fetch/content-encoding/br/resources/big.text.br new file mode 100644 index 0000000000000000000000000000000000000000..b3a530d757de6b5453ddc38411d65522a9e372de GIT binary patch literal 49 zcmV-10M7r<|NnpZW$~B~#Oj^#VW^PL`+p=BrHyqy#FR^IeayAbeIN3de-pn1KpYkT H=+SQi?%x`6 literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers b/test/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers new file mode 100644 index 00000000000..aba00bd5d4a --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers @@ -0,0 +1,3 @@ +Content-type: text/plain +Content-Encoding: br +Cache-Control: no-store diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br b/test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br new file mode 100644 index 0000000000000000000000000000000000000000..30cb2f7095e15aca510a4a8cbae00f5d35f0918b GIT binary patch literal 15 WcmaDT;c-wu^_$pyR>s{53~T^0T?Hus literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers b/test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers new file mode 100644 index 00000000000..c0c19bc82af --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers @@ -0,0 +1,2 @@ +Content-type: application/octet-stream +Content-Encoding: br diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br b/test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br new file mode 100644 index 0000000000000000000000000000000000000000..30cb2f7095e15aca510a4a8cbae00f5d35f0918b GIT binary patch literal 15 WcmaDT;c-wu^_$pyR>s{53~T^0T?Hus literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers b/test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers new file mode 100644 index 00000000000..8c03b823e09 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers @@ -0,0 +1,2 @@ +Content-type: text/plain +Content-Encoding: br diff --git a/test/wpt/tests/fetch/content-encoding/bad-gzip-body.any.js b/test/wpt/tests/fetch/content-encoding/gzip/bad-gzip-body.any.js similarity index 100% rename from test/wpt/tests/fetch/content-encoding/bad-gzip-body.any.js rename to test/wpt/tests/fetch/content-encoding/gzip/bad-gzip-body.any.js diff --git a/test/wpt/tests/fetch/content-encoding/big-gzip-body.https.any.js b/test/wpt/tests/fetch/content-encoding/gzip/big-gzip-body.https.any.js similarity index 100% rename from test/wpt/tests/fetch/content-encoding/big-gzip-body.https.any.js rename to test/wpt/tests/fetch/content-encoding/gzip/big-gzip-body.https.any.js diff --git a/test/wpt/tests/fetch/content-encoding/gzip-body.any.js b/test/wpt/tests/fetch/content-encoding/gzip/gzip-body.any.js similarity index 100% rename from test/wpt/tests/fetch/content-encoding/gzip-body.any.js rename to test/wpt/tests/fetch/content-encoding/gzip/gzip-body.any.js diff --git a/test/wpt/tests/fetch/content-encoding/resources/bad-gzip-body.py b/test/wpt/tests/fetch/content-encoding/gzip/resources/bad-gzip-body.py similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/bad-gzip-body.py rename to test/wpt/tests/fetch/content-encoding/gzip/resources/bad-gzip-body.py diff --git a/test/wpt/tests/fetch/content-encoding/resources/big.text.gz b/test/wpt/tests/fetch/content-encoding/gzip/resources/big.text.gz similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/big.text.gz rename to test/wpt/tests/fetch/content-encoding/gzip/resources/big.text.gz diff --git a/test/wpt/tests/fetch/content-encoding/resources/big.text.gz.headers b/test/wpt/tests/fetch/content-encoding/gzip/resources/big.text.gz.headers similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/big.text.gz.headers rename to test/wpt/tests/fetch/content-encoding/gzip/resources/big.text.gz.headers diff --git a/test/wpt/tests/fetch/content-encoding/resources/foo.octetstream.gz b/test/wpt/tests/fetch/content-encoding/gzip/resources/foo.octetstream.gz similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/foo.octetstream.gz rename to test/wpt/tests/fetch/content-encoding/gzip/resources/foo.octetstream.gz diff --git a/test/wpt/tests/fetch/content-encoding/resources/foo.octetstream.gz.headers b/test/wpt/tests/fetch/content-encoding/gzip/resources/foo.octetstream.gz.headers similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/foo.octetstream.gz.headers rename to test/wpt/tests/fetch/content-encoding/gzip/resources/foo.octetstream.gz.headers diff --git a/test/wpt/tests/fetch/content-encoding/resources/foo.text.gz b/test/wpt/tests/fetch/content-encoding/gzip/resources/foo.text.gz similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/foo.text.gz rename to test/wpt/tests/fetch/content-encoding/gzip/resources/foo.text.gz diff --git a/test/wpt/tests/fetch/content-encoding/resources/foo.text.gz.headers b/test/wpt/tests/fetch/content-encoding/gzip/resources/foo.text.gz.headers similarity index 100% rename from test/wpt/tests/fetch/content-encoding/resources/foo.text.gz.headers rename to test/wpt/tests/fetch/content-encoding/gzip/resources/foo.text.gz.headers diff --git a/test/wpt/tests/fetch/content-encoding/zstd/bad-zstd-body.https.any.js b/test/wpt/tests/fetch/content-encoding/zstd/bad-zstd-body.https.any.js new file mode 100644 index 00000000000..3f32e4dfba7 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/bad-zstd-body.https.any.js @@ -0,0 +1,22 @@ +// META: global=window,worker + +promise_test((test) => { + return fetch("resources/bad-zstd-body.py").then(res => { + assert_equals(res.status, 200); + }); +}, "Fetching a resource with bad zstd content should still resolve"); + +[ + "arrayBuffer", + "blob", + "formData", + "json", + "text" +].forEach(method => { + promise_test(t => { + return fetch("resources/bad-zstd-body.py").then(res => { + assert_equals(res.status, 200); + return promise_rejects_js(t, TypeError, res[method]()); + }); + }, "Consuming the body of a resource with bad zstd content with " + method + "() should reject"); +}); diff --git a/test/wpt/tests/fetch/content-encoding/zstd/big-window-zstd-body.tentative.https.any.js b/test/wpt/tests/fetch/content-encoding/zstd/big-window-zstd-body.tentative.https.any.js new file mode 100644 index 00000000000..c1dc9449567 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/big-window-zstd-body.tentative.https.any.js @@ -0,0 +1,9 @@ +// META: global=window,worker +// See https://github.com/facebook/zstd/issues/2713 for discussion about +// standardizing window size limits. + +promise_test(async t => { + const response = await fetch('resources/big.window.zst'); + assert_true(response.ok); + await promise_rejects_js(t, TypeError, response.text()); +}, 'Consuming the body of a resource with too large of a zstd window size should reject'); diff --git a/test/wpt/tests/fetch/content-encoding/zstd/big-zstd-body.https.any.js b/test/wpt/tests/fetch/content-encoding/zstd/big-zstd-body.https.any.js new file mode 100644 index 00000000000..6835f6e4255 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/big-zstd-body.https.any.js @@ -0,0 +1,55 @@ +// META: global=window,worker + +const EXPECTED_SIZE = 27000000; +const EXPECTED_SHA256 = [ + 74, 100, 37, 243, 147, 61, 116, 60, 241, 221, 126, + 18, 24, 71, 204, 28, 50, 62, 201, 130, 152, 225, + 217, 183, 10, 201, 143, 214, 102, 155, 212, 248, + ]; + +promise_test(async () => { + const response = await fetch('resources/big.text.zst'); + assert_true(response.ok); + const arrayBuffer = await response.arrayBuffer(); + assert_equals(arrayBuffer.byteLength, EXPECTED_SIZE, + 'uncompressed size should match'); + const sha256 = await crypto.subtle.digest('SHA-256', arrayBuffer); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large zstd data should be decompressed successfully'); + +promise_test(async () => { + const response = await fetch('resources/big.text.zst'); + assert_true(response.ok); + const reader = response.body.getReader({mode: 'byob'}); + let offset = 0; + // Pre-allocate space for the output. The response body will be read + // chunk-by-chunk into this array. + let ab = new ArrayBuffer(EXPECTED_SIZE); + while (offset < EXPECTED_SIZE) { + // To stress the data pipe, we want to use a different size read each + // time. Unfortunately, JavaScript doesn't have a seeded random number + // generator, so this creates the possibility of making this test flaky if + // it doesn't work for some edge cases. + let size = Math.floor(Math.random() * 65535 + 1); + if (size + offset > EXPECTED_SIZE) { + size = EXPECTED_SIZE - offset; + } + const u8 = new Uint8Array(ab, offset, size); + const { value, done } = await reader.read(u8); + ab = value.buffer; + // Check that we got our original array back. + assert_equals(ab.byteLength, EXPECTED_SIZE, + 'backing array should be the same size'); + assert_equals(offset, value.byteOffset, 'offset should match'); + assert_less_than_equal(value.byteLength, size, + 'we should not have got more than we asked for'); + offset = value.byteOffset + value.byteLength; + if (done) break; + } + assert_equals(offset, EXPECTED_SIZE, + 'we should have read the whole thing'); + const sha256 = await crypto.subtle.digest('SHA-256', new Uint8Array(ab)); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large zstd data should be decompressed successfully with byte stream'); diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/bad-zstd-body.py b/test/wpt/tests/fetch/content-encoding/zstd/resources/bad-zstd-body.py new file mode 100644 index 00000000000..496f26881dc --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/resources/bad-zstd-body.py @@ -0,0 +1,3 @@ +def main(request, response): + headers = [(b"Content-Encoding", b"zstd")] + return headers, b"not actually zstd" diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/big.text.zst b/test/wpt/tests/fetch/content-encoding/zstd/resources/big.text.zst new file mode 100644 index 0000000000000000000000000000000000000000..30eda2443f338240772315f18ca29f607cc6e395 GIT binary patch literal 2509 zcmdPcs{gko;=rHTj53T2HxiSQQ&Q8?GcvQXb8_?Y3kr*hOG?YiD=MqF7#X_%SKA%; zVPN3MXJq*E-%@}H#7;tD7a*~dk=TVu>=Yz+5fVEUiCv7uPD5gsAhFYt*riD93?z0L z5<3%#U5>=gLSk1Sv9pobl}PLyBz6@NI~R!!3@do}2*^B!zyFyT6x1EaH3Xy&VUUAf kBXtZ)pq4=s%oV8PA_HoLQ2@2PRzSW%%)nUVzVRpy0RJy1#Q*>R literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/big.window.zst.headers b/test/wpt/tests/fetch/content-encoding/zstd/resources/big.window.zst.headers new file mode 100644 index 00000000000..c5974e126a3 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/resources/big.window.zst.headers @@ -0,0 +1,2 @@ +Content-type: text/plain +Content-Encoding: zstd diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst new file mode 100644 index 0000000000000000000000000000000000000000..a73bbdd22458db57635bf88e97a4e47b4886e722 GIT binary patch literal 25 hcmdPcs{c2IMI)AhK_V?bpON98$30%g!~5pX2LNk|2}J+^ literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst.headers b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst.headers new file mode 100644 index 00000000000..e397816f545 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.octetstream.zst.headers @@ -0,0 +1,2 @@ +Content-type: application/octet-stream +Content-Encoding: zstd diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst new file mode 100644 index 0000000000000000000000000000000000000000..a73bbdd22458db57635bf88e97a4e47b4886e722 GIT binary patch literal 25 hcmdPcs{c2IMI)AhK_V?bpON98$30%g!~5pX2LNk|2}J+^ literal 0 HcmV?d00001 diff --git a/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst.headers b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst.headers new file mode 100644 index 00000000000..c5974e126a3 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/resources/foo.text.zst.headers @@ -0,0 +1,2 @@ +Content-type: text/plain +Content-Encoding: zstd diff --git a/test/wpt/tests/fetch/content-encoding/zstd/zstd-body.https.any.js b/test/wpt/tests/fetch/content-encoding/zstd/zstd-body.https.any.js new file mode 100644 index 00000000000..86923857432 --- /dev/null +++ b/test/wpt/tests/fetch/content-encoding/zstd/zstd-body.https.any.js @@ -0,0 +1,15 @@ +// META: global=window,worker + +const expectedDecompressedSize = 10500; +[ + "text", + "octetstream" +].forEach(contentType => { + promise_test(async t => { + let response = await fetch(`resources/foo.${contentType}.zst`); + assert_true(response.ok); + let arrayBuffer = await response.arrayBuffer() + let u8 = new Uint8Array(arrayBuffer); + assert_equals(u8.length, expectedDecompressedSize); + }, `fetched zstd data with content type ${contentType} should be decompressed.`); +}); diff --git a/test/wpt/tests/fetch/fetch-later/activate-after.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/activate-after.tentative.https.window.js index 08750dd3fe4..e62da0508a0 100644 --- a/test/wpt/tests/fetch/fetch-later/activate-after.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/activate-after.tentative.https.window.js @@ -1,11 +1,9 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/dispatcher/dispatcher.js // META: script=/common/get-host-info.sub.js // META: script=/common/utils.js // META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js // META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/basic.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/basic.tentative.https.window.js index bf92716681e..37f72ab89e5 100644 --- a/test/wpt/tests/fetch/fetch-later/basic.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/basic.tentative.https.window.js @@ -1,6 +1,3 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js - 'use strict'; test(() => { diff --git a/test/wpt/tests/fetch/fetch-later/iframe.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/iframe.tentative.https.window.js index 62505bc81d9..305272af419 100644 --- a/test/wpt/tests/fetch/fetch-later/iframe.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/iframe.tentative.https.window.js @@ -1,8 +1,6 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/new-window.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/new-window.tentative.https.window.js index 37b38d7f1dc..27922f46266 100644 --- a/test/wpt/tests/fetch/fetch-later/new-window.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/new-window.tentative.https.window.js @@ -1,8 +1,6 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/non-secure.window.js b/test/wpt/tests/fetch/fetch-later/non-secure.window.js index 2f2c3ea8d34..c13932e353c 100644 --- a/test/wpt/tests/fetch/fetch-later/non-secure.window.js +++ b/test/wpt/tests/fetch/fetch-later/non-secure.window.js @@ -1,6 +1,3 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js - 'use strict'; test(() => { diff --git a/test/wpt/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js index 5aa759c2346..32a3e106a0c 100644 --- a/test/wpt/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js @@ -1,9 +1,7 @@ // META: title=FetchLater: allowed by CSP -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; const { diff --git a/test/wpt/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js index 88490950d3a..ca9d881e8cd 100644 --- a/test/wpt/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js @@ -1,9 +1,7 @@ // META: title=FetchLater: blocked by CSP -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; const { diff --git a/test/wpt/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js index db6b4234b97..584f476b456 100644 --- a/test/wpt/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js @@ -1,9 +1,7 @@ // META: title=FetchLater: redirect blocked by CSP -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/quota.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/quota.tentative.https.window.js index 4fc5979374c..9d0ae4287df 100644 --- a/test/wpt/tests/fetch/fetch-later/quota.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/quota.tentative.https.window.js @@ -1,8 +1,6 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/get-host-info.sub.js // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/resources/fetch-later-helper.js b/test/wpt/tests/fetch/fetch-later/resources/fetch-later-helper.js new file mode 100644 index 00000000000..566b3e0a1a4 --- /dev/null +++ b/test/wpt/tests/fetch/fetch-later/resources/fetch-later-helper.js @@ -0,0 +1,206 @@ +'use strict'; + +const ROOT_NAME = 'fetch/fetch-later'; + +function parallelPromiseTest(func, description) { + async_test((t) => { + Promise.resolve(func(t)).then(() => t.done()).catch(t.step_func((e) => { + throw e; + })); + }, description); +} + +/** @enum {string} */ +const BeaconDataType = { + String: 'String', + ArrayBuffer: 'ArrayBuffer', + FormData: 'FormData', + URLSearchParams: 'URLSearchParams', + Blob: 'Blob', + File: 'File', +}; + +/** @enum {string} */ +const BeaconDataTypeToSkipCharset = { + String: '', + ArrayBuffer: '', + FormData: '\n\r', // CRLF characters will be normalized by FormData + URLSearchParams: ';,/?:@&=+$', // reserved URI characters + Blob: '', + File: '', +}; + +const BEACON_PAYLOAD_KEY = 'payload'; + +// Creates beacon data of the given `dataType` from `data`. +// @param {string} data - A string representation of the beacon data. Note that +// it cannot contain UTF-16 surrogates for all `BeaconDataType` except BLOB. +// @param {BeaconDataType} dataType - must be one of `BeaconDataType`. +// @param {string} contentType - Request Content-Type. +function makeBeaconData(data, dataType, contentType) { + switch (dataType) { + case BeaconDataType.String: + return data; + case BeaconDataType.ArrayBuffer: + return new TextEncoder().encode(data).buffer; + case BeaconDataType.FormData: + const formData = new FormData(); + if (data.length > 0) { + formData.append(BEACON_PAYLOAD_KEY, data); + } + return formData; + case BeaconDataType.URLSearchParams: + if (data.length > 0) { + return new URLSearchParams(`${BEACON_PAYLOAD_KEY}=${data}`); + } + return new URLSearchParams(); + case BeaconDataType.Blob: { + const options = {type: contentType || undefined}; + return new Blob([data], options); + } + case BeaconDataType.File: { + const options = {type: contentType || 'text/plain'}; + return new File([data], 'file.txt', options); + } + default: + throw Error(`Unsupported beacon dataType: ${dataType}`); + } +} + +// Create a string of `end`-`begin` characters, with characters starting from +// UTF-16 code unit `begin` to `end`-1. +function generateSequentialData(begin, end, skip) { + const codeUnits = Array(end - begin).fill().map((el, i) => i + begin); + if (skip) { + return String.fromCharCode( + ...codeUnits.filter(c => !skip.includes(String.fromCharCode(c)))); + } + return String.fromCharCode(...codeUnits); +} + +function generatePayload(size) { + if (size == 0) { + return ''; + } + const prefix = String(size) + ':'; + if (size < prefix.length) { + return Array(size).fill('*').join(''); + } + if (size == prefix.length) { + return prefix; + } + + return prefix + Array(size - prefix.length).fill('*').join(''); +} + +function generateSetBeaconURL(uuid, options) { + const host = (options && options.host) || ''; + let url = `${host}/${ROOT_NAME}/resources/set_beacon.py?uuid=${uuid}`; + if (options) { + if (options.expectOrigin !== undefined) { + url = `${url}&expectOrigin=${options.expectOrigin}`; + } + if (options.expectPreflight !== undefined) { + url = `${url}&expectPreflight=${options.expectPreflight}`; + } + if (options.expectCredentials !== undefined) { + url = `${url}&expectCredentials=${options.expectCredentials}`; + } + + if (options.useRedirectHandler) { + const redirect = `${host}/common/redirect.py` + + `?location=${encodeURIComponent(url)}`; + url = redirect; + } + } + return url; +} + +async function poll(asyncFunc, expected) { + const maxRetries = 30; + const waitInterval = 100; // milliseconds. + const delay = ms => new Promise(res => setTimeout(res, ms)); + + let result = {data: []}; + for (let i = 0; i < maxRetries; i++) { + result = await asyncFunc(); + if (!expected(result)) { + await delay(waitInterval); + continue; + } + return result; + } + return result; +} + +// Waits until the `options.count` number of beacon data available from the +// server. Defaults to 1. +// If `options.data` is set, it will be used to compare with the data from the +// response. +async function expectBeacon(uuid, options) { + const expectedCount = + (options && options.count !== undefined) ? options.count : 1; + + const res = await poll( + async () => { + const res = await fetch( + `/${ROOT_NAME}/resources/get_beacon.py?uuid=${uuid}`, + {cache: 'no-store'}); + return await res.json(); + }, + (res) => { + if (expectedCount == 0) { + // If expecting no beacon, we should try to wait as long as possible. + // So always returning false here until `poll()` decides to terminate + // itself. + return false; + } + return res.data.length == expectedCount; + }); + if (!options || !options.data) { + assert_equals( + res.data.length, expectedCount, + 'Number of sent beacons does not match expected count:'); + return; + } + + if (expectedCount == 0) { + assert_equals( + res.data.length, 0, + 'Number of sent beacons does not match expected count:'); + return; + } + + const decoder = options && options.percentDecoded ? (s) => { + // application/x-www-form-urlencoded serializer encodes space as '+' + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + s = s.replace(/\+/g, '%20'); + return decodeURIComponent(s); + } : (s) => s; + + assert_equals( + res.data.length, options.data.length, + `The size of beacon data ${ + res.data.length} from server does not match expected value ${ + options.data.length}.`); + for (let i = 0; i < options.data.length; i++) { + assert_equals( + decoder(res.data[i]), options.data[i], + 'The beacon data does not match expected value.'); + } +} + +function generateHTML(script) { + return ``; +} + +// Loads `script` into an iframe and appends it to the current document. +// Returns the loaded iframe element. +async function loadScriptAsIframe(script) { + const iframe = document.createElement('iframe'); + iframe.srcdoc = generateHTML(script); + const iframeLoaded = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + await iframeLoaded; + return iframe; +} diff --git a/test/wpt/tests/fetch/fetch-later/resources/get_beacon.py b/test/wpt/tests/fetch/fetch-later/resources/get_beacon.py new file mode 100644 index 00000000000..32cb9a9ba30 --- /dev/null +++ b/test/wpt/tests/fetch/fetch-later/resources/get_beacon.py @@ -0,0 +1,30 @@ +"""An HTTP request handler for WPT that handles /get_beacon.py requests.""" + +import json + +_BEACON_ID_KEY = b"uuid" +_BEACON_DATA_PATH = "beacon_data" + + +def main(request, response): + """Retrieves the beacon data keyed by the given uuid from server storage. + + The response content is a JSON string in one of the following formats: + - "{'data': ['abc', null, '123',...]}" + - "{'data': []}" indicates that no data has been set for this uuid. + """ + if _BEACON_ID_KEY not in request.GET: + response.status = 400 + return "Must provide a UUID to store beacon data" + uuid = request.GET.first(_BEACON_ID_KEY) + + with request.server.stash.lock: + body = {'data': []} + data = request.server.stash.take(key=uuid, path=_BEACON_DATA_PATH) + if data: + body['data'] = data + # The stash is read-once/write-once, so it has to be put back after + # reading if `data` is not None. + request.server.stash.put( + key=uuid, value=data, path=_BEACON_DATA_PATH) + return [(b'Content-Type', b'text/plain')], json.dumps(body) diff --git a/test/wpt/tests/fetch/fetch-later/resources/set_beacon.py b/test/wpt/tests/fetch/fetch-later/resources/set_beacon.py new file mode 100644 index 00000000000..1c71f23e578 --- /dev/null +++ b/test/wpt/tests/fetch/fetch-later/resources/set_beacon.py @@ -0,0 +1,83 @@ +"""An HTTP request handler for WPT that handles /set_beacon.py requests.""" + +_BEACON_ID_KEY = b"uuid" +_BEACON_DATA_PATH = "beacon_data" +_BEACON_FORM_PAYLOAD_KEY = b"payload" +_BEACON_BODY_PAYLOAD_KEY = "payload=" +_BEACON_EXPECT_ORIGIN_KEY = b"expectOrigin" +_BEACON_EXPECT_PREFLIGHT_KEY = b"expectPreflight" +_BEACON_EXPECT_CREDS_KEY = b"expectCredentials" + + +def main(request, response): + """Stores the given beacon's data keyed by uuid in the server. + + For GET request, this handler assumes no data. + For POST request, this handler extracts data from request body: + - Content-Type=multipart/form-data: data keyed by 'payload'. + - the entire request body. + + Multiple data can be added for the same uuid. + + The data is stored as UTF-8 format. + """ + if _BEACON_ID_KEY not in request.GET: + response.status = 400 + return "Must provide a UUID to store beacon data" + uuid = request.GET.first(_BEACON_ID_KEY) + + expected_origin = request.GET.get(_BEACON_EXPECT_ORIGIN_KEY) + if b"origin" in request.headers: + origin = request.headers.get(b"origin") + if expected_origin: + assert origin == expected_origin, f"expected {expected_origin}, got {origin}" + response.headers.set(b"Access-Control-Allow-Origin", origin) + else: + assert expected_origin is None, f"expected None, got {expected_origin}" + + # Handles preflight request first. + if request.method == u"OPTIONS": + assert request.GET.get( + _BEACON_EXPECT_PREFLIGHT_KEY) == b"true", "Preflight not expected." + + # preflight must not have cookies. + assert b"Cookie" not in request.headers + + requested_headers = request.headers.get( + b"Access-Control-Request-Headers") + assert b"content-type" in requested_headers, f"expected content-type, got {requested_headers}" + response.headers.set(b"Access-Control-Allow-Headers", b"content-type") + + requested_method = request.headers.get(b"Access-Control-Request-Method") + assert requested_method == b"POST", f"expected POST, got {requested_method}" + response.headers.set(b"Access-Control-Allow-Methods", b"POST") + + return response + + expect_creds = request.GET.get(_BEACON_EXPECT_CREDS_KEY) == b"true" + if expect_creds: + assert b"Cookie" in request.headers + else: + assert b"Cookie" not in request.headers + + data = None + if request.method == u"POST": + if b"multipart/form-data" in request.headers.get(b"Content-Type", b""): + if _BEACON_FORM_PAYLOAD_KEY in request.POST: + data = request.POST.first(_BEACON_FORM_PAYLOAD_KEY).decode( + 'utf-8') + elif request.body: + data = request.body.decode('utf-8') + if data.startswith(_BEACON_BODY_PAYLOAD_KEY): + data = data.split(_BEACON_BODY_PAYLOAD_KEY)[1] + + with request.server.stash.lock: + saved_data = request.server.stash.take(key=uuid, path=_BEACON_DATA_PATH) + if not saved_data: + saved_data = [data] + else: + saved_data.append(data) + request.server.stash.put( + key=uuid, value=saved_data, path=_BEACON_DATA_PATH) + + response.status = 200 diff --git a/test/wpt/tests/fetch/fetch-later/send-on-deactivate-with-background-sync.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/send-on-deactivate-with-background-sync.tentative.https.window.js new file mode 100644 index 00000000000..881bdd23f93 --- /dev/null +++ b/test/wpt/tests/fetch/fetch-later/send-on-deactivate-with-background-sync.tentative.https.window.js @@ -0,0 +1,128 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js +// META: timeout=long + +'use strict'; + +async function setBackgroundSyncEnabled(enabled) { + const status = enabled ? 'granted' : 'denied'; + await test_driver.set_permission({name: 'background-sync'}, status); +} + +parallelPromiseTest(async t => { + // Enables BackgroundSync permission such that deferred request won't be + // immediately sent out on entering BFCache. + await setBackgroundSyncEnabled(true); + + const uuid = token(); + const url = generateSetBeaconURL(uuid); + // Sets no option to test the default behavior when a document enters BFCache. + const helper = new RemoteContextHelper(); + // Opens a window with noopener so that BFCache will work. + const rc1 = await helper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + + // Creates a fetchLater request with default config in remote, which should + // only be sent on page discarded (not on entering BFCache). + await rc1.executeScript(url => { + fetchLater(url); + // Add a pageshow listener to stash the BFCache event. + window.addEventListener('pageshow', e => { + window.pageshowEvent = e; + }); + }, [url]); + // Navigates away to let page enter BFCache. + const rc2 = await rc1.navigateToNew(); + // Navigates back. + await rc2.historyBack(); + // Verifies the page was BFCached. + assert_true(await rc1.executeScript(() => { + return window.pageshowEvent.persisted; + })); + + // By default, pending requests are all flushed on BFCache no matter + // BackgroundSync is on or not. See http://b/310541607#comment28. + await expectBeacon(uuid, {count: 1}); +}, `fetchLater() does send on page entering BFCache even if BackgroundSync is on.`); + +parallelPromiseTest(async t => { + // Enables BackgroundSync permission such that deferred request won't be + // immediately sent out on entering BFCache. + await setBackgroundSyncEnabled(true); + + const uuid = token(); + const url = generateSetBeaconURL(uuid); + // activateAfter = 0s means the request should be sent out right on + // document becoming deactivated (BFCached or frozen) after navigating away. + const options = {activateAfter: 0}; + const helper = new RemoteContextHelper(); + // Opens a window with noopener so that BFCache will work. + const rc1 = await helper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + + // Creates a fetchLater request in remote which should only be sent on + // navigating away. + await rc1.executeScript((url, options) => { + fetchLater(url, options); + + // Add a pageshow listener to stash the BFCache event. + window.addEventListener('pageshow', e => { + window.pageshowEvent = e; + }); + }, [url, options]); + // Navigates away to trigger request sending. + const rc2 = await rc1.navigateToNew(); + // Navigates back. + await rc2.historyBack(); + // Verifies the page was BFCached. + assert_true(await rc1.executeScript(() => { + return window.pageshowEvent.persisted; + })); + + await expectBeacon(uuid, {count: 1}); +}, `fetchLater() with activateAfter=0 sends on page entering BFCache if BackgroundSync is on.`); + +parallelPromiseTest(async t => { + // Enables BackgroundSync permission such that deferred request won't be + // immediately sent out on entering BFCache. + await setBackgroundSyncEnabled(true); + + const uuid = token(); + const url = generateSetBeaconURL(uuid); + // activateAfter = 1m means the request should NOT be sent out on + // document becoming deactivated (BFCached or frozen) until after 1 minute. + const options = {activateAfter: 60000}; + const helper = new RemoteContextHelper(); + // Opens a window with noopener so that BFCache will work. + const rc1 = await helper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + + // Creates a fetchLater request in remote which should only be sent on + // navigating away. + await rc1.executeScript((url, options) => { + fetchLater(url, options); + + // Adds a pageshow listener to stash the BFCache event. + window.addEventListener('pageshow', e => { + window.pageshowEvent = e; + }); + }, [url, options]); + // Navigates away to trigger request sending. + const rc2 = await rc1.navigateToNew(); + // Navigates back. + await rc2.historyBack(); + // Verifies the page was BFCached. + assert_true(await rc1.executeScript(() => { + return window.pageshowEvent.persisted; + })); + + // By default, pending requests are all flushed on BFCache no matter + // BackgroundSync is on or not. See http://b/310541607#comment28. + await expectBeacon(uuid, {count: 1}); +}, `fetchLater() with activateAfter=1m does send on page entering BFCache even if BackgroundSync is on.`); diff --git a/test/wpt/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js index 94877e8321a..3bcf07483ad 100644 --- a/test/wpt/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js @@ -1,11 +1,9 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/dispatcher/dispatcher.js // META: script=/common/get-host-info.sub.js // META: script=/common/utils.js // META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js // META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js index c49e0bde87b..6ddafd78131 100644 --- a/test/wpt/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js @@ -1,7 +1,5 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js index 03078b2b516..0bbe94c39f0 100644 --- a/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js @@ -1,7 +1,5 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js b/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js index 25ce98d446e..05bb2dc1149 100644 --- a/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js +++ b/test/wpt/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js @@ -1,7 +1,5 @@ -// META: script=/resources/testharness.js -// META: script=/resources/testharnessreport.js // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/test/wpt/tests/fetch/h1-parsing/resources-with-0x00-in-header.window.js b/test/wpt/tests/fetch/h1-parsing/resources-with-0x00-in-header.window.js index 37a61c12b56..b617911105a 100644 --- a/test/wpt/tests/fetch/h1-parsing/resources-with-0x00-in-header.window.js +++ b/test/wpt/tests/fetch/h1-parsing/resources-with-0x00-in-header.window.js @@ -1,3 +1,5 @@ +// META: script=/common/get-host-info.sub.js + async_test(t => { const script = document.createElement("script"); t.add_cleanup(() => script.remove()); @@ -29,3 +31,7 @@ async_test(t => { img.onload = t.unreached_func(); document.body.append(img); }, "Expect network error for image with 0x00 in a header"); + +promise_test(async t => { + return promise_rejects_js(t, TypeError, fetch(get_host_info().HTTP_REMOTE_ORIGIN + "/fetch/h1-parsing/resources/blue-with-0x00-in-a-header.asis", {mode:"no-cors"})); +}, "Expect network error for fetch with 0x00 in a header"); diff --git a/test/wpt/tests/fetch/http-cache/freshness.any.js b/test/wpt/tests/fetch/http-cache/freshness.any.js index 6b97c8244f6..86c2620aa66 100644 --- a/test/wpt/tests/fetch/http-cache/freshness.any.js +++ b/test/wpt/tests/fetch/http-cache/freshness.any.js @@ -59,6 +59,34 @@ var tests = [ } ] }, + { + name: "HTTP cache does not reuse a response with an invalid Expires with Last-Modified now", + requests: [ + { + response_headers: [ + ["Expires", "0"], + ['Last-Modified', 0] + ] + }, + { + expected_type: "not_cached" + } + ] + }, + { + name: "HTTP cache does not reuse a response with an invalid Expires with past Last-Modified", + requests: [ + { + response_headers: [ + ["Expires", "0"], + ['Last-Modified', -100000] + ] + }, + { + expected_type: "not_cached" + } + ] + }, { name: "HTTP cache reuses a response with positive Cache-Control: max-age", requests: [ diff --git a/test/wpt/tests/fetch/metadata/WEB_FEATURES.yml b/test/wpt/tests/fetch/metadata/WEB_FEATURES.yml new file mode 100644 index 00000000000..fb48eaa3112 --- /dev/null +++ b/test/wpt/tests/fetch/metadata/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: fetch-metadata + files: "**" diff --git a/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html b/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html deleted file mode 100644 index cf322fd34bc..00000000000 --- a/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - HTTP headers on request for Appcache manifest - - - - - - - diff --git a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html index 69ac7682a5c..65b1837c636 100644 --- a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html +++ b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html @@ -36,36 +36,6 @@ }); }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no options'); - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpSameSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-site'); - }); - }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no options'); - - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpCrossSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-site'); - }); - }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no options'); - promise_test(() => { const key = '{{uuid()}}'; const url = makeRequestURL( @@ -81,36 +51,6 @@ }); }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no options'); - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpSameSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-mode'); - }); - }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no options'); - - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpCrossSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-mode'); - }); - }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no options'); - promise_test(() => { const key = '{{uuid()}}'; const url = makeRequestURL( @@ -126,36 +66,6 @@ }); }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no options'); - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpSameSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-dest'); - }); - }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no options'); - - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpCrossSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-dest'); - }); - }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no options'); - promise_test(() => { const key = '{{uuid()}}'; const url = makeRequestURL( @@ -170,35 +80,5 @@ assert_not_own_property(headers, 'sec-fetch-user'); }); }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no options'); - - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpSameSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-user'); - }); - }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no options'); - - promise_test(() => { - const key = '{{uuid()}}'; - const url = makeRequestURL( - key, - ['httpCrossSite'], - { mime: 'application/javascript', body: 'postMessage("")' } - ); - - return induceRequest(url) - .then(() => retrieve(key)) - .then((headers) => { - assert_not_own_property(headers, 'sec-fetch-user'); - }); - }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no options'); diff --git a/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml b/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml index b277bcb7b53..b96bd2fd7b4 100644 --- a/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml +++ b/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml @@ -43,10 +43,8 @@ cases: origins: [httpCrossSite] description: Not sent to non-trustworthy cross-site destination template_axes: - # Unused - appcache-manifest.sub.https.html: [] - # The `audioWorklet` interface is only available in secure contexts - # https://webaudio.github.io/web-audio-api/#BaseAudioContext + # The `AudioWorklet` interface is only available in secure contexts + # https://webaudio.github.io/web-audio-api/#AudioWorklet audioworklet.https.sub.html: [] # Service workers are only available in secure context fetch-via-serviceworker.https.sub.html: [] @@ -91,6 +89,62 @@ cases: svg-image.sub.html: [{}] window-history.sub.html: [{}] worker-dedicated-importscripts.sub.html: [{}] + # `new Worker()` only makes same-origin requests, therefore we split it + # out into the next block. + worker-dedicated-constructor.sub.html: [] + + - all_subtests: + expected: NULL + filename_flags: [] + common_axis: + - headerName: sec-fetch-site + origins: [httpOrigin] + description: Not sent to non-trustworthy same-origin destination + - headerName: sec-fetch-mode + origins: [httpOrigin] + description: Not sent to non-trustworthy same-origin destination + - headerName: sec-fetch-dest + origins: [httpOrigin] + description: Not sent to non-trustworthy same-origin destination + - headerName: sec-fetch-user + origins: [httpOrigin] + description: Not sent to non-trustworthy same-origin destination + template_axes: + # All the templates in this block are unused with the exception of + # `worker-dedicated-constructor` + audioworklet.https.sub.html: [] + fetch-via-serviceworker.https.sub.html: [] + serviceworker.https.sub.html: [] + css-images.sub.html: [] + css-font-face.sub.html: [] + element-a.sub.html: [] + element-area.sub.html: [] + element-audio.sub.html: [] + element-embed.sub.html: [] + element-frame.sub.html: [] + element-iframe.sub.html: [] + element-img.sub.html: [] + element-img-environment-change.sub.html: [] + element-input-image.sub.html: [] + element-link-icon.sub.html: [] + element-link-prefetch.optional.sub.html: [] + element-meta-refresh.optional.sub.html: [] + element-picture.sub.html: [] + element-script.sub.html: [] + element-video.sub.html: [] + element-video-poster.sub.html: [] + fetch.sub.html: [] + form-submission.sub.html: [] + header-link.sub.html: [] + header-refresh.optional.sub.html: [] + window-location.sub.html: [] + script-module-import-dynamic.sub.html: [] + script-module-import-static.sub.html: [] + svg-image.sub.html: [] + window-history.sub.html: [] + worker-dedicated-importscripts.sub.html: [] + # `new Worker()` only makes same-origin requests, so we populate its + # generated tests here. worker-dedicated-constructor.sub.html: [{}] # Sec-Fetch-Site - direct requests @@ -117,7 +171,6 @@ cases: # https://html.spec.whatwg.org/#fetch-a-single-module-script worker-dedicated-constructor.sub.html: [] - appcache-manifest.sub.https.html: [{}] audioworklet.https.sub.html: [{}] css-images.sub.html: - filename_flags: [tentative] @@ -176,8 +229,8 @@ cases: expected: cross-site template_axes: # Unused - # The `audioWorklet` interface is only available in secure contexts - # https://webaudio.github.io/web-audio-api/#BaseAudioContext + # The `AudioWorklet` interface is only available in secure contexts + # https://webaudio.github.io/web-audio-api/#AudioWorklet audioworklet.https.sub.html: [] # Service workers are only available in secure context fetch-via-serviceworker.https.sub.html: [] @@ -196,7 +249,6 @@ cases: # https://html.spec.whatwg.org/#fetch-a-single-module-script worker-dedicated-constructor.sub.html: [] - appcache-manifest.sub.https.html: [{}] css-images.sub.html: - filename_flags: [tentative] css-font-face.sub.html: @@ -289,7 +341,6 @@ cases: # https://html.spec.whatwg.org/#fetch-a-single-module-script worker-dedicated-constructor.sub.html: [] - appcache-manifest.sub.https.html: [{}] audioworklet.https.sub.html: [{}] css-images.sub.html: - filename_flags: [tentative] @@ -377,7 +428,6 @@ cases: worker-dedicated-constructor.sub.html: [] worker-dedicated-importscripts.sub.html: [] # Avoid duplicate subtest for 'sec-fetch-site - HTTPS downgrade-upgrade' - appcache-manifest.sub.https.html: [] css-images.sub.html: - filename_flags: [tentative] element-a.sub.html: [{}] @@ -409,8 +459,6 @@ cases: filename_flags: [https] origins: [] template_axes: - appcache-manifest.sub.https.html: - - expected: no-cors audioworklet.https.sub.html: # https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script - expected: cors @@ -586,8 +634,6 @@ cases: filename_flags: [https] origins: [] template_axes: - appcache-manifest.sub.https.html: - - expected: empty audioworklet.https.sub.html: # https://github.com/WebAudio/web-audio-api/issues/2203 - expected: audioworklet @@ -709,8 +755,6 @@ cases: filename_flags: [https] origins: [] template_axes: - appcache-manifest.sub.https.html: - - expected: NULL audioworklet.https.sub.html: - expected: NULL css-images.sub.html: diff --git a/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html b/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html deleted file mode 100644 index 0dfc084f2e3..00000000000 --- a/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - HTTP headers on request for Appcache manifest - - - - - - - diff --git a/test/wpt/tests/fetch/private-network-access/anchor.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/anchor.tentative.https.window.js new file mode 100644 index 00000000000..4e860ad381d --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/anchor.tentative.https.window.js @@ -0,0 +1,191 @@ +// 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-local +// META: variant=?include=from-private +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public +// +// These tests verify that secure contexts can navigate to less-public address +// spaces via an anchor link iff the target server responds affirmatively to +// preflight requests. + +setup(() => { + assert_true(window.isSecureContext); +}); + +// Source: secure local context. +// +// All fetches unaffected by Private Network Access. + +subsetTestByKey("from-local", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_LOCAL }, + expected: NavigationTestResult.SUCCESS, +}), "local to local: no preflight required."); + +subsetTestByKey("from-local", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "local to private: no preflight required."); + +subsetTestByKey("from-local", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "local to public: no preflight required."); + +// Generates tests of preflight behavior for a single (source, target) pair. +// +// Scenarios: +// +// - preflight response has non-2xx HTTP code +// - preflight response is missing CORS headers +// - preflight response is missing the PNA-specific `Access-Control` header +// - success +// +function makePreflightTests({ + key, + sourceName, + sourceServer, + sourceTreatAsPublic, + targetName, + targetServer, +}) { + const prefix = + `${sourceName} to ${targetName}: `; + + const source = { + server: sourceServer, + treatAsPublic: sourceTreatAsPublic, + }; + + promise_test_parallel(t => anchorTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.failure() }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "failed preflight."); + + promise_test_parallel(t => anchorTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.noCorsHeader(token()) }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "missing CORS headers."); + + promise_test_parallel(t => anchorTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.noPnaHeader(token()) }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "missing PNA header."); + + promise_test_parallel(t => anchorTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.navigation(token()) }, + }, + expected: NavigationTestResult.SUCCESS, + }), prefix + "success."); +} + +// Source: private secure context. +// +// Fetches to the local address space require a successful preflight response +// carrying a PNA-specific header. + +subsetTestByKey('from-private', makePreflightTests, { + sourceServer: Server.HTTPS_PRIVATE, + sourceName: 'private', + targetServer: Server.HTTPS_LOCAL, + targetName: 'local', +}); + +subsetTestByKey("from-private", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { server: Server.HTTPS_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "private to private: no preflight required."); + +subsetTestByKey("from-private", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { server: Server.HTTPS_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "private to public: no preflight required."); + +// Source: public secure context. +// +// Fetches to the local and private address spaces require a successful +// preflight response carrying a PNA-specific header. + +subsetTestByKey('from-public', makePreflightTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: "public", + targetServer: Server.HTTPS_LOCAL, + targetName: "local", +}); + +subsetTestByKey('from-public', makePreflightTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: "public", + targetServer: Server.HTTPS_PRIVATE, + targetName: "private", +}); + +subsetTestByKey("from-public", promise_test_parallel, t => anchorTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { server: Server.HTTPS_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "public to public: no preflight 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', makePreflightTests, { + sourceServer: Server.HTTPS_LOCAL, + sourceTreatAsPublic: true, + sourceName: "treat-as-public-address", + targetServer: Server.OTHER_HTTPS_LOCAL, + targetName: "local", +}); + +subsetTestByKey("from-treat-as-public", promise_test_parallel, + t => anchorTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: {server: Server.HTTPS_LOCAL}, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to local (same-origin): no preflight required.'); + +subsetTestByKey('from-treat-as-public', makePreflightTests, { + sourceServer: Server.HTTPS_LOCAL, + sourceTreatAsPublic: true, + sourceName: 'treat-as-public-address', + targetServer: Server.HTTPS_PRIVATE, + targetName: 'private', +}); + +subsetTestByKey("from-treat-as-public", promise_test_parallel, + t => anchorTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: {server: Server.HTTPS_PUBLIC}, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to public: no preflight required.'); diff --git a/test/wpt/tests/fetch/private-network-access/anchor.tentative.window.js b/test/wpt/tests/fetch/private-network-access/anchor.tentative.window.js new file mode 100644 index 00000000000..cb538658081 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/anchor.tentative.window.js @@ -0,0 +1,95 @@ +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// META: timeout=long +// +// Spec: https://wicg.github.io/private-network-access/ +// +// These tests verify that non-secure contexts cannot open a new window via an +// anchor link to less-public address spaces. + +setup(() => { + // Making sure we are in a non secure context, as expected. + assert_false(window.isSecureContext); +}); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.SUCCESS, +}), "local to local: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "local to private: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "local to public: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "private to local: failure."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "private to private: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "private to public: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "public to local: failure."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.FAILURE, +}), "public to private: failure."); + +promise_test_parallel(t => anchorTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "public to public: no preflight required."); + +promise_test_parallel(t => anchorTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "treat-as-public-address to local: failure."); + +promise_test_parallel(t => anchorTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.FAILURE, +}), "treat-as-public-address to private: failure."); + +promise_test_parallel(t => anchorTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "treat-as-public-address to public: no preflight required."); diff --git a/test/wpt/tests/fetch/private-network-access/fetch.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/fetch.tentative.https.window.js index dbc4f23f677..606443dc14d 100644 --- a/test/wpt/tests/fetch/private-network-access/fetch.tentative.https.window.js +++ b/test/wpt/tests/fetch/private-network-access/fetch.tentative.https.window.js @@ -236,7 +236,7 @@ subsetTestByKey("from-private", promise_test, t => fetchTest(t, { subsetTestByKey("from-private", promise_test, t => fetchTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { - server: Server.HTTPS_PRIVATE, + server: Server.HTTPS_PUBLIC, behavior: { response: ResponseBehavior.allowCrossOrigin() }, }, expected: FetchTestResult.SUCCESS, diff --git a/test/wpt/tests/fetch/private-network-access/mixed-content-fetch.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/mixed-content-fetch.tentative.https.window.js index 54485dc7047..dbae5193b5c 100644 --- a/test/wpt/tests/fetch/private-network-access/mixed-content-fetch.tentative.https.window.js +++ b/test/wpt/tests/fetch/private-network-access/mixed-content-fetch.tentative.https.window.js @@ -1,5 +1,6 @@ // META: script=/common/utils.js // META: script=resources/support.sub.js +// META: timeout=long // // Spec: https://wicg.github.io/private-network-access // diff --git a/test/wpt/tests/fetch/private-network-access/resources/anchor.html b/test/wpt/tests/fetch/private-network-access/resources/anchor.html new file mode 100644 index 00000000000..0780b3fa502 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/anchor.html @@ -0,0 +1,16 @@ + + +Anchor + + diff --git a/test/wpt/tests/fetch/private-network-access/resources/iframed-no-preflight-received.html b/test/wpt/tests/fetch/private-network-access/resources/iframed-no-preflight-received.html new file mode 100644 index 00000000000..20b5150d44a --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/iframed-no-preflight-received.html @@ -0,0 +1,7 @@ + + +Iframed + diff --git a/test/wpt/tests/fetch/private-network-access/resources/no-preflight-received.html b/test/wpt/tests/fetch/private-network-access/resources/no-preflight-received.html new file mode 100644 index 00000000000..5ee533e1820 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/no-preflight-received.html @@ -0,0 +1,6 @@ + + +No preflight received + diff --git a/test/wpt/tests/fetch/private-network-access/resources/open-to-existing-window.html b/test/wpt/tests/fetch/private-network-access/resources/open-to-existing-window.html new file mode 100644 index 00000000000..6460024bc80 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/open-to-existing-window.html @@ -0,0 +1,12 @@ + + +Opener to an existing window + + diff --git a/test/wpt/tests/fetch/private-network-access/resources/openee.html b/test/wpt/tests/fetch/private-network-access/resources/openee.html new file mode 100644 index 00000000000..8f0a859cb32 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/openee.html @@ -0,0 +1,8 @@ + + +Openee + diff --git a/test/wpt/tests/fetch/private-network-access/resources/opener.html b/test/wpt/tests/fetch/private-network-access/resources/opener.html index 40e3b60bb52..78b66c6db7b 100644 --- a/test/wpt/tests/fetch/private-network-access/resources/opener.html +++ b/test/wpt/tests/fetch/private-network-access/resources/opener.html @@ -4,8 +4,8 @@ diff --git a/test/wpt/tests/fetch/private-network-access/resources/preflight.py b/test/wpt/tests/fetch/private-network-access/resources/preflight.py index 255bd56a33e..44676632394 100644 --- a/test/wpt/tests/fetch/private-network-access/resources/preflight.py +++ b/test/wpt/tests/fetch/private-network-access/resources/preflight.py @@ -78,9 +78,8 @@ def _get_response_headers(method, mode, origin): return [ acam, ("Access-Control-Allow-Origin", origin), - _ACAPN, ("Access-Control-Allow-Credentials", "true"), - ("Access-Control-Allow-Headers", "Upgrade-Insecure-Requests") + _ACAPN, ] return [] @@ -89,7 +88,8 @@ def _get_expect_single_preflight(request): return request.GET.get(b"expect-single-preflight") def _is_preflight_optional(request): - return request.GET.get(b"is-preflight-optional") + return request.GET.get(b"is-preflight-optional") or \ + request.GET.get(b"file-if-no-preflight-received") def _get_preflight_uuid(request): return request.GET.get(b"preflight-uuid") @@ -129,8 +129,12 @@ def _handle_preflight_request(request, response): return (headers, "preflight") -def _final_response_body(request): - file_name = request.GET.get(b"file") +def _final_response_body(request, missing_preflight): + file_name = None + if missing_preflight and not request.GET.get(b"is-preflight-optional"): + file_name = request.GET.get(b"file-if-no-preflight-received") + if file_name is None: + file_name = request.GET.get(b"file") if file_name is None: return request.GET.get(b"body") or "success" @@ -146,13 +150,14 @@ def _final_response_body(request): return prefix + contents def _handle_final_request(request, response): + missing_preflight = False if _should_treat_as_public_once(request): headers = [("Content-Security-Policy", "treat-as-public-address"),] else: uuid = _get_preflight_uuid(request) if uuid is not None: - if (request.server.stash.take(uuid) is None and - not _is_preflight_optional(request)): + missing_preflight = request.server.stash.take(uuid) is None + if missing_preflight and not _is_preflight_optional(request): return (405, [], "no preflight received") request.server.stash.put(uuid, "final") @@ -172,7 +177,7 @@ def _handle_final_request(request, response): if _is_loaded_in_fenced_frame(request): headers.append(("Supports-Loading-Mode", "fenced-frame")) - body = _final_response_body(request) + body = _final_response_body(request, missing_preflight) return (headers, body) def main(request, response): diff --git a/test/wpt/tests/fetch/private-network-access/resources/service-worker-fetch-all.js b/test/wpt/tests/fetch/private-network-access/resources/service-worker-fetch-all.js new file mode 100644 index 00000000000..78ac8d1576b --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/resources/service-worker-fetch-all.js @@ -0,0 +1,20 @@ +self.addEventListener("install", () => { + // Skip waiting before replacing the previously-active service worker, if any. + // This allows the bridge script to notice the controller change and query + // the install time via fetch. + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + // Claim all clients so that the bridge script notices the activation. + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url).searchParams.get("proxied-url"); + if (url) { + event.respondWith(fetch(url)); + } else { + event.respondWith(fetch(event.request)); + } +}); diff --git a/test/wpt/tests/fetch/private-network-access/resources/support.sub.js b/test/wpt/tests/fetch/private-network-access/resources/support.sub.js index c3bdb8139ba..7d133b02889 100644 --- a/test/wpt/tests/fetch/private-network-access/resources/support.sub.js +++ b/test/wpt/tests/fetch/private-network-access/resources/support.sub.js @@ -446,6 +446,10 @@ async function iframeTest(t, { source, target, expected }) { const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "iframed.html"); targetUrl.searchParams.set("iframe-uuid", uuid); + targetUrl.searchParams.set( + "file-if-no-preflight-received", + "iframed-no-preflight-received.html", + ); const sourceUrl = resolveUrl("resources/iframer.html", sourceResolveOptions(source)); @@ -470,13 +474,18 @@ async function iframeTest(t, { source, target, expected }) { assert_equals(result, expected); } -const WindowOpenTestResult = { +const NavigationTestResult = { SUCCESS: "success", - FAILURE: "failure", + FAILURE: "timeout", }; async function windowOpenTest(t, { source, target, expected }) { const targetUrl = preflightUrl(target); + targetUrl.searchParams.set("file", "openee.html"); + targetUrl.searchParams.set( + "file-if-no-preflight-received", + "no-preflight-received.html", + ); const sourceUrl = resolveUrl("resources/opener.html", sourceResolveOptions(source)); @@ -487,7 +496,69 @@ async function windowOpenTest(t, { source, target, expected }) { iframe.contentWindow.postMessage({ url: targetUrl.href }, "*"); - assert_equals(await reply, expected); + const result = await Promise.race([ + reply, + new Promise((resolve) => { + t.step_timeout(() => resolve("timeout"), 10000 /* ms */); + }), + ]); + + assert_equals(result, expected); +} + +async function windowOpenExistingTest(t, { source, target, expected }) { + const targetUrl = preflightUrl(target); + targetUrl.searchParams.set("file", "openee.html"); + targetUrl.searchParams.set( + "file-if-no-preflight-received", + "no-preflight-received.html", + ); + + const sourceUrl = resolveUrl( + 'resources/open-to-existing-window.html', sourceResolveOptions(source)); + sourceUrl.searchParams.set("url", targetUrl); + sourceUrl.searchParams.set("token", token()); + + const iframe = await appendIframe(t, document, sourceUrl); + const reply = futureMessage({ source: iframe.contentWindow }); + + iframe.contentWindow.postMessage({ url: targetUrl.href }, "*"); + + const result = await Promise.race([ + reply, + new Promise((resolve) => { + t.step_timeout(() => resolve("timeout"), 10000 /* ms */); + }), + ]); + + assert_equals(result, expected); +} + +async function anchorTest(t, { source, target, expected }) { + const targetUrl = preflightUrl(target); + targetUrl.searchParams.set("file", "openee.html"); + targetUrl.searchParams.set( + "file-if-no-preflight-received", + "no-preflight-received.html", + ); + + const sourceUrl = + resolveUrl("resources/anchor.html", sourceResolveOptions(source)); + sourceUrl.searchParams.set("url", targetUrl); + + const iframe = await appendIframe(t, document, sourceUrl); + const reply = futureMessage({ source: iframe.contentWindow }); + + iframe.contentWindow.postMessage({ url: targetUrl.href }, "*"); + + const result = await Promise.race([ + reply, + new Promise((resolve) => { + t.step_timeout(() => resolve("timeout"), 10000 /* ms */); + }), + ]); + + assert_equals(result, expected); } // Similar to `iframeTest`, but replaced iframes with fenced frames. @@ -784,3 +855,66 @@ async function sharedWorkerBlobFetchTest(t, { source, target, expected }) { assert_equals(status, expected.status, "response status"); assert_equals(body, expected.body, "response body"); } + +async function makeServiceWorkerTest(t, { source, target, expected, fetch_document=false }) { + const bridgeUrl = resolveUrl( + "resources/service-worker-bridge.html", + sourceResolveOptions({ server: source.server })); + + const scriptUrl = fetch_document? + resolveUrl("resources/service-worker-fetch-all.js", sourceResolveOptions(source)): + resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); + + const realTargetUrl = preflightUrl(target); + + // Fetch a URL within the service worker's scope, but tell it which URL to + // really fetch. + const targetUrl = new URL("service-worker-proxy", scriptUrl); + targetUrl.searchParams.append("proxied-url", realTargetUrl.href); + + const iframe = await appendIframe(t, document, bridgeUrl); + + const request = (message) => { + const reply = futureMessage(); + iframe.contentWindow.postMessage(message, "*"); + return reply; + }; + + { + const { error, loaded } = await request({ + action: "register", + url: scriptUrl.href, + }); + + assert_equals(error, undefined, "register error"); + assert_true(loaded, "response loaded"); + } + + try { + const { controlled, numControllerChanges } = await request({ + action: "wait", + numControllerChanges: 1, + }); + + assert_equals(numControllerChanges, 1, "controller change"); + assert_true(controlled, "bridge script is controlled"); + + const { error, ok, body } = await request({ + action: "fetch", + url: targetUrl.href, + }); + + assert_equals(error, expected.error, "fetch error"); + assert_equals(ok, expected.ok, "response ok"); + assert_equals(body, expected.body, "response body"); + } finally { + // Always unregister the service worker. + const { error, unregistered } = await request({ + action: "unregister", + scope: new URL("./", scriptUrl).href, + }); + + assert_equals(error, undefined, "unregister error"); + assert_true(unregistered, "unregistered"); + } +} diff --git a/test/wpt/tests/fetch/private-network-access/service-worker-background-fetch.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/service-worker-background-fetch.tentative.https.window.js index 6369b166e21..8d1028cc5ee 100644 --- a/test/wpt/tests/fetch/private-network-access/service-worker-background-fetch.tentative.https.window.js +++ b/test/wpt/tests/fetch/private-network-access/service-worker-background-fetch.tentative.https.window.js @@ -1,5 +1,6 @@ // META: script=/common/utils.js // META: script=resources/support.sub.js +// META: timeout=long // // Spec: https://wicg.github.io/private-network-access/#integration-fetch // Spec: https://wicg.github.io/background-fetch/ diff --git a/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js new file mode 100644 index 00000000000..6fc29ce4725 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document-treat-as-public.tentative.https.window.js @@ -0,0 +1,101 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.OTHER_HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to local (same-origin): no preflight required."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "treat-as-public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: { + server: Server.HTTPS_PUBLIC, + behavior: { response: ResponseBehavior.allowCrossOrigin() }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "treat-as-public to public: success."); diff --git a/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js new file mode 100644 index 00000000000..ec380555a80 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/service-worker-fetch-document.tentative.https.window.js @@ -0,0 +1,114 @@ +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// +// Spec: https://wicg.github.io/private-network-access/#integration-fetch +// +// These tests check that fetches from within `ServiceWorker` scripts are +// subject to Private Network Access checks, just like fetches from within +// documents. + +// Results that may be expected in tests. +const TestResult = { + SUCCESS: { ok: true, body: "success" }, + FAILURE: { error: "TypeError" }, +}; + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_LOCAL }, + target: { server: Server.HTTPS_LOCAL }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "local to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "private to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PRIVATE }, + target: { server: Server.HTTPS_PRIVATE }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "private to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to local: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_LOCAL, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to local: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, + }, + expected: TestResult.FAILURE, + fetch_document: true, +}), "public to private: failed preflight."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { + server: Server.HTTPS_PRIVATE, + behavior: { + preflight: PreflightBehavior.success(token()), + response: ResponseBehavior.allowCrossOrigin(), + }, + }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to private: success."); + +promise_test(t => makeServiceWorkerTest(t, { + source: { server: Server.HTTPS_PUBLIC }, + target: { server: Server.HTTPS_PUBLIC }, + expected: TestResult.SUCCESS, + fetch_document: true, +}), "public to public: success."); + diff --git a/test/wpt/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js index cb6d1f79b01..5fc5800ba04 100644 --- a/test/wpt/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js +++ b/test/wpt/tests/fetch/private-network-access/service-worker-fetch.tentative.https.window.js @@ -16,84 +16,25 @@ const TestResult = { FAILURE: { error: "TypeError" }, }; -async function makeTest(t, { source, target, expected }) { - const bridgeUrl = resolveUrl( - "resources/service-worker-bridge.html", - sourceResolveOptions({ server: source.server })); - - const scriptUrl = - resolveUrl("resources/service-worker.js", sourceResolveOptions(source)); - - const realTargetUrl = preflightUrl(target); - - // Fetch a URL within the service worker's scope, but tell it which URL to - // really fetch. - const targetUrl = new URL("service-worker-proxy", scriptUrl); - targetUrl.searchParams.append("proxied-url", realTargetUrl.href); - - const iframe = await appendIframe(t, document, bridgeUrl); - - const request = (message) => { - const reply = futureMessage(); - iframe.contentWindow.postMessage(message, "*"); - return reply; - }; - - { - const { error, loaded } = await request({ - action: "register", - url: scriptUrl.href, - }); - - assert_equals(error, undefined, "register error"); - assert_true(loaded, "response loaded"); - } - - try { - const { controlled, numControllerChanges } = await request({ - action: "wait", - numControllerChanges: 1, - }); - - assert_equals(numControllerChanges, 1, "controller change"); - assert_true(controlled, "bridge script is controlled"); - - const { error, ok, body } = await request({ - action: "fetch", - url: targetUrl.href, - }); - - assert_equals(error, expected.error, "fetch error"); - assert_equals(ok, expected.ok, "response ok"); - assert_equals(body, expected.body, "response body"); - } finally { - // Always unregister the service worker. - const { error, unregistered } = await request({ - action: "unregister", - scope: new URL("./", scriptUrl).href, - }); - - assert_equals(error, undefined, "unregister error"); - assert_true(unregistered, "unregistered"); - } -} - -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_LOCAL }, expected: TestResult.SUCCESS, }), "local to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "private to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_LOCAL, @@ -105,22 +46,25 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "private to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_PRIVATE }, expected: TestResult.SUCCESS, }), "private to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_LOCAL, @@ -132,16 +76,19 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PRIVATE, @@ -153,25 +100,28 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PUBLIC }, expected: TestResult.SUCCESS, }), "public to public: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.OTHER_HTTPS_LOCAL, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to local: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -186,7 +136,7 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to local: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -195,19 +145,22 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to local (same-origin): no preflight required."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: { server: Server.HTTPS_PRIVATE, - behavior: { response: ResponseBehavior.allowCrossOrigin() }, + behavior: { + preflight: PreflightBehavior.failure(), + response: ResponseBehavior.allowCrossOrigin() + }, }, expected: TestResult.FAILURE, }), "treat-as-public to private: failed preflight."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, @@ -222,7 +175,7 @@ subsetTest(promise_test, t => makeTest(t, { expected: TestResult.SUCCESS, }), "treat-as-public to private: success."); -subsetTest(promise_test, t => makeTest(t, { +subsetTest(promise_test, t => makeServiceWorkerTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, diff --git a/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js new file mode 100644 index 00000000000..6a2a624fc80 --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js @@ -0,0 +1,209 @@ +// 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-local +// META: variant=?include=from-private +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public +// +// These tests verify that secure contexts can navigate to less-public address +// spaces via window.open to an existing window iff the target server responds +// affirmatively to preflight requests. + +setup(() => { + assert_true(window.isSecureContext); +}); + +// Source: secure local context. +// +// All fetches unaffected by Private Network Access. + +subsetTestByKey( + 'from-local', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_LOCAL}, + target: {server: Server.HTTPS_LOCAL}, + expected: NavigationTestResult.SUCCESS, + }), + 'local to local: no preflight required.'); + +subsetTestByKey( + 'from-local', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_LOCAL}, + target: {server: Server.HTTPS_PRIVATE}, + expected: NavigationTestResult.SUCCESS, + }), + 'local to private: no preflight required.'); + +subsetTestByKey( + 'from-local', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_LOCAL}, + target: {server: Server.HTTPS_PUBLIC}, + expected: NavigationTestResult.SUCCESS, + }), + 'local to public: no preflight required.'); + +// Generates tests of preflight behavior for a single (source, target) pair. +// +// Scenarios: +// +// - preflight response has non-2xx HTTP code +// - preflight response is missing CORS headers +// - preflight response is missing the PNA-specific `Access-Control` header +// - success +// +function makePreflightTests({ + key, + sourceName, + sourceServer, + sourceTreatAsPublic, + targetName, + targetServer, +}) { + const prefix = + `${sourceName} to ${targetName}: `; + + const source = { + server: sourceServer, + treatAsPublic: sourceTreatAsPublic, + }; + + promise_test_parallel(t => windowOpenExistingTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.failure() }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "failed preflight."); + + promise_test_parallel(t => windowOpenExistingTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.noCorsHeader(token()) }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "missing CORS headers."); + + promise_test_parallel(t => windowOpenExistingTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.noPnaHeader(token()) }, + }, + expected: NavigationTestResult.FAILURE, + }), prefix + "missing PNA header."); + + promise_test_parallel(t => windowOpenExistingTest(t, { + source, + target: { + server: targetServer, + behavior: { preflight: PreflightBehavior.navigation(token()) }, + }, + expected: NavigationTestResult.SUCCESS, + }), prefix + "success."); +} + +// Source: private secure context. +// +// Navigating to the local address space require a successful preflight response +// carrying a PNA-specific header. + +subsetTestByKey('from-private', makePreflightTests, { + sourceServer: Server.HTTPS_PRIVATE, + sourceName: 'private', + targetServer: Server.HTTPS_LOCAL, + targetName: 'local', +}); + +subsetTestByKey( + 'from-private', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PRIVATE}, + target: {server: Server.HTTPS_PRIVATE}, + expected: NavigationTestResult.SUCCESS, + }), + 'private to private: no preflight required.'); + +subsetTestByKey( + 'from-private', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PRIVATE}, + target: {server: Server.HTTPS_PUBLIC}, + expected: NavigationTestResult.SUCCESS, + }), + 'private to public: no preflight required.'); + +// Source: public secure context. +// +// Navigating to the local and private address spaces require a successful +// preflight response carrying a PNA-specific header. + +subsetTestByKey('from-public', makePreflightTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: "public", + targetServer: Server.HTTPS_LOCAL, + targetName: "local", +}); + +subsetTestByKey('from-public', makePreflightTests, { + sourceServer: Server.HTTPS_PUBLIC, + sourceName: "public", + targetServer: Server.HTTPS_PRIVATE, + targetName: "private", +}); + +subsetTestByKey( + 'from-public', promise_test_parallel, + t => windowOpenExistingTest(t, { + source: {server: Server.HTTPS_PUBLIC}, + target: {server: Server.HTTPS_PUBLIC}, + expected: NavigationTestResult.SUCCESS, + }), + 'public to public: no preflight 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', makePreflightTests, { + sourceServer: Server.HTTPS_LOCAL, + sourceTreatAsPublic: true, + sourceName: "treat-as-public-address", + targetServer: Server.OTHER_HTTPS_LOCAL, + targetName: "local", +}); + +subsetTestByKey("from-treat-as-public", promise_test_parallel, + t => windowOpenExistingTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: {server: Server.HTTPS_LOCAL}, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to local (same-origin): no preflight required.'); + +subsetTestByKey('from-treat-as-public', makePreflightTests, { + sourceServer: Server.HTTPS_LOCAL, + sourceTreatAsPublic: true, + sourceName: 'treat-as-public-address', + targetServer: Server.HTTPS_PRIVATE, + targetName: 'private', +}); + +subsetTestByKey("from-treat-as-public", promise_test_parallel, + t => windowOpenExistingTest(t, { + source: { + server: Server.HTTPS_LOCAL, + treatAsPublic: true, + }, + target: {server: Server.HTTPS_PUBLIC}, + expected: NavigationTestResult.SUCCESS, + }), + 'treat-as-public-address to public: no preflight required.'); diff --git a/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.window.js b/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.window.js new file mode 100644 index 00000000000..5a6cd4c5cfd --- /dev/null +++ b/test/wpt/tests/fetch/private-network-access/window-open-existing.tentative.window.js @@ -0,0 +1,95 @@ +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=resources/support.sub.js +// META: timeout=long +// +// Spec: https://wicg.github.io/private-network-access/ +// +// These tests verify that non-secure contexts cannot navigate to less-public +// address spaces via window.open to an existing window. + +setup(() => { + // Making sure we are in a non secure context, as expected. + assert_false(window.isSecureContext); +}); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.SUCCESS, +}), "local to local: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "local to private: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_LOCAL }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "local to public: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "private to local: failure."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.SUCCESS, +}), "private to private: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PRIVATE }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "private to public: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "public to local: failure."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.FAILURE, +}), "public to private: failure."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { server: Server.HTTP_PUBLIC }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "public to public: no preflight required."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_LOCAL }, + expected: NavigationTestResult.FAILURE, +}), "treat-as-public-address to local: failure."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_PRIVATE }, + expected: NavigationTestResult.FAILURE, +}), "treat-as-public-address to private: failure."); + +promise_test_parallel(t => windowOpenExistingTest(t, { + source: { + server: Server.HTTP_LOCAL, + treatAsPublic: true, + }, + target: { server: Server.HTTP_PUBLIC }, + expected: NavigationTestResult.SUCCESS, +}), "treat-as-public-address to public: no preflight required."); diff --git a/test/wpt/tests/fetch/private-network-access/window-open.tentative.https.window.js b/test/wpt/tests/fetch/private-network-access/window-open.tentative.https.window.js index 85b929f34d4..6793d1f3b4d 100644 --- a/test/wpt/tests/fetch/private-network-access/window-open.tentative.https.window.js +++ b/test/wpt/tests/fetch/private-network-access/window-open.tentative.https.window.js @@ -1,10 +1,16 @@ +// 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-local +// META: variant=?include=from-private +// META: variant=?include=from-public +// META: variant=?include=from-treat-as-public // -// These tests verify that secure contexts can navigate iframes to less-public -// address spaces iff the target server responds affirmatively to preflight -// requests. +// These tests verify that secure contexts can navigate to less-public address +// spaces via window.open iff the target server responds affirmatively to +// preflight requests. setup(() => { assert_true(window.isSecureContext); @@ -14,29 +20,28 @@ setup(() => { // // All fetches unaffected by Private Network Access. -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-local", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_LOCAL }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to local: no preflight required."); -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-local", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_PRIVATE }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to private: no preflight required."); -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-local", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_LOCAL }, target: { server: Server.HTTPS_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to public: no preflight required."); // Generates tests of preflight behavior for a single (source, target) pair. // // Scenarios: // -// - parent navigates child: // - preflight response has non-2xx HTTP code // - preflight response is missing CORS headers // - preflight response is missing the PNA-specific `Access-Control` header @@ -64,7 +69,7 @@ function makePreflightTests({ server: targetServer, behavior: { preflight: PreflightBehavior.failure() }, }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), prefix + "failed preflight."); promise_test_parallel(t => windowOpenTest(t, { @@ -73,7 +78,7 @@ function makePreflightTests({ server: targetServer, behavior: { preflight: PreflightBehavior.noCorsHeader(token()) }, }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), prefix + "missing CORS headers."); promise_test_parallel(t => windowOpenTest(t, { @@ -82,16 +87,16 @@ function makePreflightTests({ server: targetServer, behavior: { preflight: PreflightBehavior.noPnaHeader(token()) }, }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), prefix + "missing PNA header."); promise_test_parallel(t => windowOpenTest(t, { source, target: { server: targetServer, - behavior: { preflight: PreflightBehavior.success(token()) }, + behavior: { preflight: PreflightBehavior.navigation(token()) }, }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), prefix + "success."); } @@ -100,23 +105,23 @@ function makePreflightTests({ // Fetches to the local address space require a successful preflight response // carrying a PNA-specific header. -makePreflightTests({ +subsetTestByKey('from-private', makePreflightTests, { sourceServer: Server.HTTPS_PRIVATE, sourceName: 'private', targetServer: Server.HTTPS_LOCAL, targetName: 'local', }); -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-private", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_PRIVATE }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "private to private: no preflight required."); -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-private", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_PRIVATE }, target: { server: Server.HTTPS_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "private to public: no preflight required."); // Source: public secure context. @@ -124,30 +129,30 @@ promise_test_parallel(t => windowOpenTest(t, { // Fetches to the local and private address spaces require a successful // preflight response carrying a PNA-specific header. -makePreflightTests({ +subsetTestByKey('from-public', makePreflightTests, { sourceServer: Server.HTTPS_PUBLIC, sourceName: "public", targetServer: Server.HTTPS_LOCAL, targetName: "local", }); -makePreflightTests({ +subsetTestByKey('from-public', makePreflightTests, { sourceServer: Server.HTTPS_PUBLIC, sourceName: "public", targetServer: Server.HTTPS_PRIVATE, targetName: "private", }); -promise_test_parallel(t => windowOpenTest(t, { +subsetTestByKey("from-public", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_PUBLIC }, target: { server: Server.HTTPS_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight 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. -makePreflightTests({ +subsetTestByKey('from-treat-as-public', makePreflightTests, { sourceServer: Server.HTTPS_LOCAL, sourceTreatAsPublic: true, sourceName: "treat-as-public-address", @@ -155,18 +160,18 @@ makePreflightTests({ targetName: "local", }); -promise_test_parallel( +subsetTestByKey("from-treat-as-public", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: {server: Server.HTTPS_LOCAL}, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), 'treat-as-public-address to local (same-origin): no preflight required.'); -makePreflightTests({ +subsetTestByKey('from-treat-as-public', makePreflightTests, { sourceServer: Server.HTTPS_LOCAL, sourceTreatAsPublic: true, sourceName: 'treat-as-public-address', @@ -174,14 +179,14 @@ makePreflightTests({ targetName: 'private', }); -promise_test_parallel( +subsetTestByKey("from-treat-as-public", promise_test_parallel, t => windowOpenTest(t, { source: { server: Server.HTTPS_LOCAL, treatAsPublic: true, }, target: {server: Server.HTTPS_PUBLIC}, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), 'treat-as-public-address to public: no preflight required.'); @@ -195,6 +200,6 @@ promise_test_parallel( server: Server.HTTPS_PUBLIC, behavior: {preflight: PreflightBehavior.optionalSuccess(token())} }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), 'treat-as-public-address to local: optional preflight'); diff --git a/test/wpt/tests/fetch/private-network-access/window-open.tentative.window.js b/test/wpt/tests/fetch/private-network-access/window-open.tentative.window.js index 8d023775226..5e2313d60a2 100644 --- a/test/wpt/tests/fetch/private-network-access/window-open.tentative.window.js +++ b/test/wpt/tests/fetch/private-network-access/window-open.tentative.window.js @@ -1,6 +1,7 @@ // META: script=/common/dispatcher/dispatcher.js // META: script=/common/utils.js // META: script=resources/support.sub.js +// META: timeout=long // // Spec: https://wicg.github.io/private-network-access/ // @@ -12,58 +13,58 @@ setup(() => { assert_false(window.isSecureContext); }); -promise_test(t => windowOpenTest(t, { +promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_LOCAL }, target: { server: Server.HTTP_LOCAL }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to local: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_LOCAL }, target: { server: Server.HTTP_PRIVATE }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to private: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_LOCAL }, target: { server: Server.HTTP_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "local to public: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PRIVATE }, target: { server: Server.HTTP_LOCAL }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), "private to local: failure."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PRIVATE }, target: { server: Server.HTTP_PRIVATE }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "private to private: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PRIVATE }, target: { server: Server.HTTP_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "private to public: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PUBLIC }, target: { server: Server.HTTP_LOCAL }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), "public to local: failure."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PUBLIC }, target: { server: Server.HTTP_PRIVATE }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), "public to private: failure."); promise_test_parallel(t => windowOpenTest(t, { source: { server: Server.HTTP_PUBLIC }, target: { server: Server.HTTP_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); promise_test_parallel(t => windowOpenTest(t, { @@ -72,7 +73,7 @@ promise_test_parallel(t => windowOpenTest(t, { treatAsPublic: true, }, target: { server: Server.HTTP_LOCAL }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), "treat-as-public-address to local: failure."); promise_test_parallel(t => windowOpenTest(t, { @@ -81,7 +82,7 @@ promise_test_parallel(t => windowOpenTest(t, { treatAsPublic: true, }, target: { server: Server.HTTP_PRIVATE }, - expected: WindowOpenTestResult.FAILURE, + expected: NavigationTestResult.FAILURE, }), "treat-as-public-address to private: failure."); promise_test_parallel(t => windowOpenTest(t, { @@ -90,5 +91,5 @@ promise_test_parallel(t => windowOpenTest(t, { treatAsPublic: true, }, target: { server: Server.HTTP_PUBLIC }, - expected: WindowOpenTestResult.SUCCESS, + expected: NavigationTestResult.SUCCESS, }), "treat-as-public-address to public: no preflight required."); diff --git a/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html new file mode 100644 index 00000000000..428decfc583 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html @@ -0,0 +1,80 @@ + + + + + + diff --git a/test/wpt/tests/fetch/security/dangling-markup-mitigation-data-url.tentative.sub.html b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html similarity index 100% rename from test/wpt/tests/fetch/security/dangling-markup-mitigation-data-url.tentative.sub.html rename to test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html diff --git a/test/wpt/tests/fetch/security/dangling-markup-mitigation.tentative.html b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html similarity index 100% rename from test/wpt/tests/fetch/security/dangling-markup-mitigation.tentative.html rename to test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html diff --git a/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html new file mode 100644 index 00000000000..3f038cbb7be --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html @@ -0,0 +1,61 @@ + + + + + + diff --git a/test/wpt/tests/fetch/security/dangling-markup/media.html b/test/wpt/tests/fetch/security/dangling-markup/media.html new file mode 100644 index 00000000000..2649edcf320 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/media.html @@ -0,0 +1,27 @@ + + + + + diff --git a/test/wpt/tests/fetch/security/dangling-markup/option.html b/test/wpt/tests/fetch/security/dangling-markup/option.html new file mode 100644 index 00000000000..f528bed999b --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/option.html @@ -0,0 +1,51 @@ + + + + + + diff --git a/test/wpt/tests/fetch/security/dangling-markup/resources/empty.html b/test/wpt/tests/fetch/security/dangling-markup/resources/empty.html new file mode 100644 index 00000000000..0e76edd65b7 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/resources/empty.html @@ -0,0 +1 @@ + diff --git a/test/wpt/tests/fetch/security/dangling-markup/resources/helper.js b/test/wpt/tests/fetch/security/dangling-markup/resources/helper.js new file mode 100644 index 00000000000..100bcba7b51 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/resources/helper.js @@ -0,0 +1,63 @@ +function assert_no_message_from_frame(test, frame) { + window.addEventListener("message", test.step_func(e => { + assert_not_equals(e.source, frame.contentWindow); + })); +} + +function appendFrameAndGetElement(test, frame) { + return new Promise((resolve, reject) => { + frame.onload = test.step_func(_ => { + frame.onload = null; + resolve(frame.contentDocument.querySelector('#dangling')); + }); + document.body.appendChild(frame); + }); +} + +function appendAndSubmit(test, frame) { + return new Promise((resolve, reject) => { + frame.onload = test.step_func(_ => { + frame.onload = null; + frame.contentDocument.querySelector('form').addEventListener("error", _ => { + resolve("error"); + }); + frame.contentDocument.querySelector('form').addEventListener("submit", _ => { + resolve("submit"); + }); + frame.contentDocument.querySelector('[type=submit]').click(); + }); + document.body.appendChild(frame); + }); +} + +function assert_no_submission(test, frame) { + assert_no_message_from_frame(test, frame); + + appendAndSubmit(test, frame) + .then(test.step_func_done(result => { + assert_equals(result, "error"); + frame.remove(); + })); +} + +function assert_img_loaded(test, frame) { + appendFrameAndGetElement(test, frame) + .then(test.step_func_done(img => { + assert_equals(img.naturalHeight, 103, "Height"); + assert_equals(img.naturalWidth, 76, "Width"); + })); +} + +function assert_img_not_loaded(test, frame) { + appendFrameAndGetElement(test, frame) + .then(test.step_func_done(img => { + assert_equals(img.naturalHeight, 0, "Height"); + assert_equals(img.naturalWidth, 0, "Width"); + })); +} + +function createFrame(markup) { + var i = document.createElement('iframe'); + i.srcdoc = `${markup}sekrit`; + return i; +} diff --git a/test/wpt/tests/fetch/security/dangling-markup/service-worker.js b/test/wpt/tests/fetch/security/dangling-markup/service-worker.js new file mode 100644 index 00000000000..99d5456a874 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/service-worker.js @@ -0,0 +1,41 @@ +const requests = new Set(); + +addEventListener('install', evt => { + evt.waitUntil(self.skipWaiting()); +}); + +addEventListener('activate', evt => { + evt.waitUntil(self.clients.claim()); +}); + +addEventListener('message', evt => { + evt.source.postMessage(requests); +}); + +addEventListener('fetch', evt => { + const url = new URL(evt.request.url); + const path = url.pathname; + const search = url.search || "?"; + const params = new URLSearchParams(search); + const type = params.get('type'); + if (path.includes('404')) { + const dir = path.split('/'); + const request = dir[dir.length-1] + search; + if (!requests.has(request)) { + requests.add(request); + } + evt.respondWith(new Response("", { + headers: { + "Content-Type": type || "text/plain" + } + })); + } else if (path.endsWith('resources.html')) { + const html = params.get('html') || ""; + evt.respondWith(new Response(html, { + headers: { + "Content-Type": type || "text/html" + } + })); + } + return; +}); diff --git a/test/wpt/tests/fetch/security/dangling-markup/textarea.html b/test/wpt/tests/fetch/security/dangling-markup/textarea.html new file mode 100644 index 00000000000..c4b334edc99 --- /dev/null +++ b/test/wpt/tests/fetch/security/dangling-markup/textarea.html @@ -0,0 +1,34 @@ + + + + + + diff --git a/test/wpt/tests/interfaces/CSP.idl b/test/wpt/tests/interfaces/CSP.idl index ac0a6ff5638..d4a6377ebb3 100644 --- a/test/wpt/tests/interfaces/CSP.idl +++ b/test/wpt/tests/interfaces/CSP.idl @@ -41,16 +41,16 @@ interface SecurityPolicyViolationEvent : Event { }; dictionary SecurityPolicyViolationEventInit : EventInit { - required USVString documentURI; - USVString referrer = ""; - USVString blockedURI = ""; - required DOMString violatedDirective; - required DOMString effectiveDirective; - required DOMString originalPolicy; - USVString sourceFile = ""; - DOMString sample = ""; - required SecurityPolicyViolationEventDisposition disposition; - required unsigned short statusCode; - unsigned long lineNumber = 0; - unsigned long columnNumber = 0; + USVString documentURI = ""; + USVString referrer = ""; + USVString blockedURI = ""; + DOMString violatedDirective = ""; + DOMString effectiveDirective = ""; + DOMString originalPolicy = ""; + USVString sourceFile = ""; + DOMString sample = ""; + SecurityPolicyViolationEventDisposition disposition = "enforce"; + unsigned short statusCode = 0; + unsigned long lineNumber = 0; + unsigned long columnNumber = 0; }; diff --git a/test/wpt/tests/interfaces/DOM-Parsing.idl b/test/wpt/tests/interfaces/DOM-Parsing.idl index d0d84ab6972..af262607936 100644 --- a/test/wpt/tests/interfaces/DOM-Parsing.idl +++ b/test/wpt/tests/interfaces/DOM-Parsing.idl @@ -8,19 +8,3 @@ interface XMLSerializer { constructor(); DOMString serializeToString(Node root); }; - -interface mixin InnerHTML { - [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerHTML; -}; - -Element includes InnerHTML; -ShadowRoot includes InnerHTML; - -partial interface Element { - [CEReactions] attribute [LegacyNullToEmptyString] DOMString outerHTML; - [CEReactions] undefined insertAdjacentHTML(DOMString position, DOMString text); -}; - -partial interface Range { - [CEReactions, NewObject] DocumentFragment createContextualFragment(DOMString fragment); -}; diff --git a/test/wpt/tests/interfaces/FedCM.idl b/test/wpt/tests/interfaces/FedCM.idl index 9b18d951f83..4070d50fcd7 100644 --- a/test/wpt/tests/interfaces/FedCM.idl +++ b/test/wpt/tests/interfaces/FedCM.idl @@ -18,9 +18,19 @@ partial interface Navigator { [SecureContext] readonly attribute NavigatorLogin login; }; +dictionary IdentityCredentialDisconnectOptions : IdentityProviderConfig { + required USVString accountHint; +}; + [Exposed=Window, SecureContext] interface IdentityCredential : Credential { + static Promise disconnect(optional IdentityCredentialDisconnectOptions options = {}); readonly attribute USVString? token; + readonly attribute boolean isAutoSelected; +}; + +dictionary DisconnectedAccount { + required USVString account_id; }; partial dictionary CredentialRequestOptions { @@ -35,15 +45,19 @@ enum IdentityCredentialRequestOptionsContext { }; dictionary IdentityCredentialRequestOptions { - required sequence providers; + required sequence providers; IdentityCredentialRequestOptionsContext context = "signin"; }; dictionary IdentityProviderConfig { required USVString configURL; required USVString clientId; +}; + +dictionary IdentityProviderRequestOptions : IdentityProviderConfig { USVString nonce; DOMString loginHint; + DOMString domainHint; }; dictionary IdentityProviderWellKnown { @@ -67,6 +81,7 @@ dictionary IdentityProviderAPIConfig { required USVString client_metadata_endpoint; required USVString id_assertion_endpoint; required USVString login_url; + USVString disconnect_endpoint; IdentityProviderBranding branding; }; @@ -78,6 +93,7 @@ dictionary IdentityProviderAccount { USVString picture; sequence approved_clients; sequence login_hints; + sequence domain_hints; }; dictionary IdentityProviderAccountList { sequence accounts; diff --git a/test/wpt/tests/interfaces/anonymous-iframe.idl b/test/wpt/tests/interfaces/anonymous-iframe.idl new file mode 100644 index 00000000000..11d19e320a8 --- /dev/null +++ b/test/wpt/tests/interfaces/anonymous-iframe.idl @@ -0,0 +1,12 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Iframe credentialless (https://wicg.github.io/anonymous-iframe/) + +partial interface HTMLIFrameElement { + attribute boolean credentialless; +}; + +partial interface Window { + readonly attribute boolean credentialless; +}; diff --git a/test/wpt/tests/interfaces/compute-pressure.idl b/test/wpt/tests/interfaces/compute-pressure.idl index 3e35dc4ee2d..a90febffc3b 100644 --- a/test/wpt/tests/interfaces/compute-pressure.idl +++ b/test/wpt/tests/interfaces/compute-pressure.idl @@ -14,9 +14,9 @@ callback PressureUpdateCallback = undefined ( [Exposed=(DedicatedWorker,SharedWorker,Window), SecureContext] interface PressureObserver { - constructor(PressureUpdateCallback callback, optional PressureObserverOptions options = {}); + constructor(PressureUpdateCallback callback); - Promise observe(PressureSource source); + Promise observe(PressureSource source, optional PressureObserverOptions options = {}); undefined unobserve(PressureSource source); undefined disconnect(); sequence takeRecords(); @@ -33,5 +33,5 @@ interface PressureRecord { }; dictionary PressureObserverOptions { - double sampleRate = 1.0; + [EnforceRange] unsigned long sampleInterval = 0; }; diff --git a/test/wpt/tests/interfaces/contact-picker.idl b/test/wpt/tests/interfaces/contact-picker.idl index 0119d0e2ce6..fc589fa0669 100644 --- a/test/wpt/tests/interfaces/contact-picker.idl +++ b/test/wpt/tests/interfaces/contact-picker.idl @@ -37,7 +37,7 @@ dictionary ContactsSelectOptions { boolean multiple = false; }; -[Exposed=Window,SecureContext] +[Exposed=Window, SecureContext] interface ContactsManager { Promise> getProperties(); Promise> select(sequence properties, optional ContactsSelectOptions options = {}); diff --git a/test/wpt/tests/interfaces/css-anchor-position.idl b/test/wpt/tests/interfaces/css-anchor-position.idl index 5d3973eff3d..5eeaa030b85 100644 --- a/test/wpt/tests/interfaces/css-anchor-position.idl +++ b/test/wpt/tests/interfaces/css-anchor-position.idl @@ -4,11 +4,81 @@ // Source: CSS Anchor Positioning (https://drafts.csswg.org/css-anchor-position-1/) [Exposed=Window] -interface CSSPositionFallbackRule : CSSGroupingRule { +interface CSSPositionTryRule : CSSRule { readonly attribute CSSOMString name; + [SameObject, PutForwards=cssText] readonly attribute CSSPositionTryDescriptors style; }; [Exposed=Window] -interface CSSTryRule : CSSRule { - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; +interface CSSPositionTryDescriptors : CSSStyleDeclaration { + attribute CSSOMString margin; + attribute CSSOMString marginTop; + attribute CSSOMString marginRight; + attribute CSSOMString marginBottom; + attribute CSSOMString marginLeft; + attribute CSSOMString marginBlock; + attribute CSSOMString marginBlockStart; + attribute CSSOMString marginBlockEnd; + attribute CSSOMString marginInline; + attribute CSSOMString marginInlineStart; + attribute CSSOMString marginInlineEnd; + attribute CSSOMString margin-top; + attribute CSSOMString margin-right; + attribute CSSOMString margin-bottom; + attribute CSSOMString margin-left; + attribute CSSOMString margin-block; + attribute CSSOMString margin-block-start; + attribute CSSOMString margin-block-end; + attribute CSSOMString margin-inline; + attribute CSSOMString margin-inline-start; + attribute CSSOMString margin-inline-end; + attribute CSSOMString inset; + attribute CSSOMString insetBlock; + attribute CSSOMString insetBlockStart; + attribute CSSOMString insetBlockEnd; + attribute CSSOMString insetInline; + attribute CSSOMString insetInlineStart; + attribute CSSOMString insetInlineEnd; + attribute CSSOMString top; + attribute CSSOMString left; + attribute CSSOMString right; + attribute CSSOMString bottom; + attribute CSSOMString inset-block; + attribute CSSOMString inset-block-start; + attribute CSSOMString inset-block-end; + attribute CSSOMString inset-inline; + attribute CSSOMString inset-inline-start; + attribute CSSOMString inset-inline-end; + attribute CSSOMString width; + attribute CSSOMString minWidth; + attribute CSSOMString maxWidth; + attribute CSSOMString height; + attribute CSSOMString minHeight; + attribute CSSOMString maxHeight; + attribute CSSOMString blockSize; + attribute CSSOMString minBlockSize; + attribute CSSOMString maxBlockSize; + attribute CSSOMString inlineSize; + attribute CSSOMString minInlineSize; + attribute CSSOMString maxInlineSize; + attribute CSSOMString min-width; + attribute CSSOMString max-width; + attribute CSSOMString min-height; + attribute CSSOMString max-height; + attribute CSSOMString block-size; + attribute CSSOMString min-block-size; + attribute CSSOMString max-block-size; + attribute CSSOMString inline-size; + attribute CSSOMString min-inline-size; + attribute CSSOMString max-inline-size; + attribute CSSOMString placeSelf; + attribute CSSOMString alignSelf; + attribute CSSOMString justifySelf; + attribute CSSOMString place-self; + attribute CSSOMString align-self; + attribute CSSOMString justify-self; + attribute CSSOMString positionAnchor; + attribute CSSOMString position-anchor; + attribute CSSOMString insetArea; + attribute CSSOMString inset-area; }; diff --git a/test/wpt/tests/interfaces/css-animations.idl b/test/wpt/tests/interfaces/css-animations.idl index 6620e0156dc..14a99980690 100644 --- a/test/wpt/tests/interfaces/css-animations.idl +++ b/test/wpt/tests/interfaces/css-animations.idl @@ -24,7 +24,7 @@ partial interface CSSRule { [Exposed=Window] interface CSSKeyframeRule : CSSRule { attribute CSSOMString keyText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style; }; [Exposed=Window] diff --git a/test/wpt/tests/interfaces/css-fonts.idl b/test/wpt/tests/interfaces/css-fonts.idl index 070cbc58f5d..678f31323b8 100644 --- a/test/wpt/tests/interfaces/css-fonts.idl +++ b/test/wpt/tests/interfaces/css-fonts.idl @@ -3,9 +3,42 @@ // (https://github.com/w3c/webref) // Source: CSS Fonts Module Level 4 (https://drafts.csswg.org/css-fonts-4/) +[Exposed=Window] +interface CSSFontFaceDescriptors : CSSStyleDeclaration { + attribute [LegacyNullToEmptyString] CSSOMString src; + attribute [LegacyNullToEmptyString] CSSOMString fontFamily; + attribute [LegacyNullToEmptyString] CSSOMString font-family; + attribute [LegacyNullToEmptyString] CSSOMString fontStyle; + attribute [LegacyNullToEmptyString] CSSOMString font-style; + attribute [LegacyNullToEmptyString] CSSOMString fontWeight; + attribute [LegacyNullToEmptyString] CSSOMString font-weight; + attribute [LegacyNullToEmptyString] CSSOMString fontStretch; + attribute [LegacyNullToEmptyString] CSSOMString font-stretch; + attribute [LegacyNullToEmptyString] CSSOMString fontWidth; + attribute [LegacyNullToEmptyString] CSSOMString font-width; + attribute [LegacyNullToEmptyString] CSSOMString unicodeRange; + attribute [LegacyNullToEmptyString] CSSOMString unicode-range; + attribute [LegacyNullToEmptyString] CSSOMString fontFeatureSettings; + attribute [LegacyNullToEmptyString] CSSOMString font-feature-settings; + attribute [LegacyNullToEmptyString] CSSOMString fontVariationSettings; + attribute [LegacyNullToEmptyString] CSSOMString font-variation-settings; + attribute [LegacyNullToEmptyString] CSSOMString fontNamedInstance; + attribute [LegacyNullToEmptyString] CSSOMString font-named-instance; + attribute [LegacyNullToEmptyString] CSSOMString fontDisplay; + attribute [LegacyNullToEmptyString] CSSOMString font-display; + attribute [LegacyNullToEmptyString] CSSOMString fontLanguageOverride; + attribute [LegacyNullToEmptyString] CSSOMString font-language-override; + attribute [LegacyNullToEmptyString] CSSOMString ascentOverride; + attribute [LegacyNullToEmptyString] CSSOMString ascent-override; + attribute [LegacyNullToEmptyString] CSSOMString descentOverride; + attribute [LegacyNullToEmptyString] CSSOMString descent-override; + attribute [LegacyNullToEmptyString] CSSOMString lineGapOverride; + attribute [LegacyNullToEmptyString] CSSOMString line-gap-override; +}; + [Exposed=Window] interface CSSFontFaceRule : CSSRule { - readonly attribute CSSStyleDeclaration style; + readonly attribute CSSFontFaceDescriptors style; }; partial interface CSSRule { const unsigned short FONT_FEATURE_VALUES_RULE = 14; @@ -19,6 +52,7 @@ interface CSSFontFeatureValuesRule : CSSRule { readonly attribute CSSFontFeatureValuesMap swash; readonly attribute CSSFontFeatureValuesMap characterVariant; readonly attribute CSSFontFeatureValuesMap styleset; + readonly attribute CSSFontFeatureValuesMap historicalForms; }; [Exposed=Window] diff --git a/test/wpt/tests/interfaces/css-nesting.idl b/test/wpt/tests/interfaces/css-nesting.idl new file mode 100644 index 00000000000..58d3247f904 --- /dev/null +++ b/test/wpt/tests/interfaces/css-nesting.idl @@ -0,0 +1,9 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: CSS Nesting Module (https://drafts.csswg.org/css-nesting-1/) + +[Exposed=Window] +interface CSSNestRule : CSSGroupingRule { + [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style; +}; diff --git a/test/wpt/tests/interfaces/css-properties-values-api.idl b/test/wpt/tests/interfaces/css-properties-values-api.idl index eb7d7b027e7..418e78375be 100644 --- a/test/wpt/tests/interfaces/css-properties-values-api.idl +++ b/test/wpt/tests/interfaces/css-properties-values-api.idl @@ -16,8 +16,8 @@ partial namespace CSS { [Exposed=Window] interface CSSPropertyRule : CSSRule { - readonly attribute CSSOMString name; - readonly attribute CSSOMString syntax; - readonly attribute boolean inherits; - readonly attribute CSSOMString? initialValue; + readonly attribute CSSOMString name; + readonly attribute CSSOMString syntax; + readonly attribute boolean inherits; + readonly attribute CSSOMString? initialValue; }; diff --git a/test/wpt/tests/interfaces/css-scroll-snap-2.idl b/test/wpt/tests/interfaces/css-scroll-snap-2.idl new file mode 100644 index 00000000000..a346969c56a --- /dev/null +++ b/test/wpt/tests/interfaces/css-scroll-snap-2.idl @@ -0,0 +1,16 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: CSS Scroll Snap Module Level 2 (https://drafts.csswg.org/css-scroll-snap-2/) + +dictionary SnapEventInit : EventInit { + Node? snapTargetBlock; + Node? snapTargetInline; +}; + +[Exposed=Window] +interface SnapEvent : Event { + constructor(DOMString type, optional SnapEventInit eventInitDict = {}); + readonly attribute Node? snapTargetBlock; + readonly attribute Node? snapTargetInline; +}; diff --git a/test/wpt/tests/interfaces/css-transitions.idl b/test/wpt/tests/interfaces/css-transitions.idl index 0f00b2c014c..d4ff45e4862 100644 --- a/test/wpt/tests/interfaces/css-transitions.idl +++ b/test/wpt/tests/interfaces/css-transitions.idl @@ -1,7 +1,7 @@ // GENERATED CONTENT - DO NOT EDIT // Content was automatically extracted by Reffy into webref // (https://github.com/w3c/webref) -// Source: CSS Transitions (https://drafts.csswg.org/css-transitions-1/) +// Source: CSS Transitions Level 1 (https://drafts.csswg.org/css-transitions-1/) [Exposed=Window] interface TransitionEvent : Event { diff --git a/test/wpt/tests/interfaces/css-view-transitions-2.idl b/test/wpt/tests/interfaces/css-view-transitions-2.idl index fe1f0e30e7d..559870751a2 100644 --- a/test/wpt/tests/interfaces/css-view-transitions-2.idl +++ b/test/wpt/tests/interfaces/css-view-transitions-2.idl @@ -3,28 +3,34 @@ // (https://github.com/w3c/webref) // Source: CSS View Transitions Module Level 2 (https://drafts.csswg.org/css-view-transitions-2/) +partial interface CSSRule { + const unsigned short VIEW_TRANSITION_RULE = 15; +}; + +enum ViewTransitionNavigation { "auto", "none" }; + +[Exposed=Window] +interface CSSViewTransitionRule : CSSRule { + readonly attribute ViewTransitionNavigation navigation; + [SameObject] readonly attribute FrozenArray types; +}; + +[Exposed=Window] +interface ViewTransitionTypeSet { + setlike; +}; + [Exposed=Window] -interface PageRevealEvent : Event { - readonly attribute ViewTransition? viewTransition; +partial interface ViewTransition { + attribute ViewTransitionTypeSet types; }; dictionary StartViewTransitionOptions { UpdateCallback? update = null; - sequence? type = null; + sequence? types = null; }; partial interface Document { ViewTransition startViewTransition(optional (UpdateCallback or StartViewTransitionOptions) callbackOptions = {}); }; - -partial interface CSSRule { - const unsigned short VIEW_TRANSITION_RULE = 15; -}; - -enum ViewTransitionNavigation { "auto", "none" }; -[Exposed=Window] -interface CSSViewTransitionRule : CSSRule { - attribute ViewTransitionNavigation navigation; - attribute DOMTokenList type; -}; diff --git a/test/wpt/tests/interfaces/cssom-view.idl b/test/wpt/tests/interfaces/cssom-view.idl index 4e531a26824..57e559e7f12 100644 --- a/test/wpt/tests/interfaces/cssom-view.idl +++ b/test/wpt/tests/interfaces/cssom-view.idl @@ -104,6 +104,9 @@ dictionary ScrollIntoViewOptions : ScrollOptions { dictionary CheckVisibilityOptions { boolean checkOpacity = false; boolean checkVisibilityCSS = false; + boolean contentVisibilityAuto = false; + boolean opacityProperty = false; + boolean visibilityProperty = false; }; partial interface Element { @@ -127,6 +130,7 @@ partial interface Element { readonly attribute long clientLeft; readonly attribute long clientWidth; readonly attribute long clientHeight; + readonly attribute double currentCSSZoom; }; partial interface HTMLElement { diff --git a/test/wpt/tests/interfaces/cssom.idl b/test/wpt/tests/interfaces/cssom.idl index 94cd1912b91..005496e7ede 100644 --- a/test/wpt/tests/interfaces/cssom.idl +++ b/test/wpt/tests/interfaces/cssom.idl @@ -99,7 +99,7 @@ interface CSSRule { [Exposed=Window] interface CSSStyleRule : CSSGroupingRule { attribute CSSOMString selectorText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style; }; [Exposed=Window] @@ -118,10 +118,26 @@ interface CSSGroupingRule : CSSRule { undefined deleteRule(unsigned long index); }; +[Exposed=Window] +interface CSSPageDescriptors : CSSStyleDeclaration { + attribute [LegacyNullToEmptyString] CSSOMString margin; + attribute [LegacyNullToEmptyString] CSSOMString marginTop; + attribute [LegacyNullToEmptyString] CSSOMString marginRight; + attribute [LegacyNullToEmptyString] CSSOMString marginBottom; + attribute [LegacyNullToEmptyString] CSSOMString marginLeft; + attribute [LegacyNullToEmptyString] CSSOMString margin-top; + attribute [LegacyNullToEmptyString] CSSOMString margin-right; + attribute [LegacyNullToEmptyString] CSSOMString margin-bottom; + attribute [LegacyNullToEmptyString] CSSOMString margin-left; + attribute [LegacyNullToEmptyString] CSSOMString size; + attribute [LegacyNullToEmptyString] CSSOMString marks; + attribute [LegacyNullToEmptyString] CSSOMString bleed; +}; + [Exposed=Window] interface CSSPageRule : CSSGroupingRule { attribute CSSOMString selectorText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSPageDescriptors style; }; [Exposed=Window] @@ -146,6 +162,10 @@ interface CSSStyleDeclaration { [CEReactions] undefined setProperty(CSSOMString property, [LegacyNullToEmptyString] CSSOMString value, optional [LegacyNullToEmptyString] CSSOMString priority = ""); [CEReactions] CSSOMString removeProperty(CSSOMString property); readonly attribute CSSRule? parentRule; +}; + +[Exposed=Window] +interface CSSStyleProperties : CSSStyleDeclaration { [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString cssFloat; }; diff --git a/test/wpt/tests/interfaces/custom-state-pseudo-class.idl b/test/wpt/tests/interfaces/custom-state-pseudo-class.idl deleted file mode 100644 index 342f1ede0b0..00000000000 --- a/test/wpt/tests/interfaces/custom-state-pseudo-class.idl +++ /dev/null @@ -1,14 +0,0 @@ -// GENERATED CONTENT - DO NOT EDIT -// Content was automatically extracted by Reffy into webref -// (https://github.com/w3c/webref) -// Source: Custom State Pseudo Class (https://wicg.github.io/custom-state-pseudo-class/) - -partial interface ElementInternals { - readonly attribute CustomStateSet states; -}; - -[Exposed=Window] -interface CustomStateSet { - setlike; - undefined add(DOMString value); -}; diff --git a/test/wpt/tests/interfaces/device-attributes.idl b/test/wpt/tests/interfaces/device-attributes.idl new file mode 100644 index 00000000000..cf62523ad82 --- /dev/null +++ b/test/wpt/tests/interfaces/device-attributes.idl @@ -0,0 +1,13 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Device Attributes API (https://wicg.github.io/WebApiDevice/device_attributes/) + +partial interface NavigatorManagedData { + // Device Attributes API. + Promise getAnnotatedAssetId(); + Promise getAnnotatedLocation(); + Promise getDirectoryId(); + Promise getHostname(); + Promise getSerialNumber(); +}; diff --git a/test/wpt/tests/interfaces/digital-identities.idl b/test/wpt/tests/interfaces/digital-identities.idl new file mode 100644 index 00000000000..2d1b7208502 --- /dev/null +++ b/test/wpt/tests/interfaces/digital-identities.idl @@ -0,0 +1,27 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Digital Credentials (https://wicg.github.io/digital-identities/) + +partial interface Navigator { + [SecureContext, SameObject] readonly attribute CredentialsContainer identity; +}; + +partial dictionary CredentialRequestOptions { + DigitalCredentialRequestOptions digital; +}; + +dictionary DigitalCredentialRequestOptions { + sequence providers; +}; + +dictionary IdentityRequestProvider { + required DOMString protocol; + required object request; +}; + +[Exposed=Window, SecureContext] +interface DigitalCredential : Credential { + readonly attribute DOMString protocol; + [SameObject] readonly attribute Uint8Array data; +}; diff --git a/test/wpt/tests/interfaces/document-picture-in-picture.idl b/test/wpt/tests/interfaces/document-picture-in-picture.idl index f54f437a936..ed34b3c2160 100644 --- a/test/wpt/tests/interfaces/document-picture-in-picture.idl +++ b/test/wpt/tests/interfaces/document-picture-in-picture.idl @@ -20,6 +20,7 @@ interface DocumentPictureInPicture : EventTarget { dictionary DocumentPictureInPictureOptions { [EnforceRange] unsigned long long width = 0; [EnforceRange] unsigned long long height = 0; + boolean disallowReturnToOpener = false; }; [Exposed=Window, SecureContext] diff --git a/test/wpt/tests/interfaces/dom.idl b/test/wpt/tests/interfaces/dom.idl index 929fa8d03b3..72d61f5cfd8 100644 --- a/test/wpt/tests/interfaces/dom.idl +++ b/test/wpt/tests/interfaces/dom.idl @@ -120,9 +120,9 @@ interface mixin ParentNode { readonly attribute Element? lastElementChild; readonly attribute unsigned long childElementCount; - [CEReactions, Unscopable] undefined prepend((Node or DOMString)... nodes); - [CEReactions, Unscopable] undefined append((Node or DOMString)... nodes); - [CEReactions, Unscopable] undefined replaceChildren((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined prepend((Node or TrustedScript or DOMString)... nodes); + [CEReactions, Unscopable] undefined append((Node or TrustedScript or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceChildren((Node or TrustedScript or DOMString)... nodes); Element? querySelector(DOMString selectors); [NewObject] NodeList querySelectorAll(DOMString selectors); @@ -139,9 +139,9 @@ Element includes NonDocumentTypeChildNode; CharacterData includes NonDocumentTypeChildNode; interface mixin ChildNode { - [CEReactions, Unscopable] undefined before((Node or DOMString)... nodes); - [CEReactions, Unscopable] undefined after((Node or DOMString)... nodes); - [CEReactions, Unscopable] undefined replaceWith((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined before((Node or TrustedScript or DOMString)... nodes); + [CEReactions, Unscopable] undefined after((Node or TrustedScript or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceWith((Node or TrustedScript or DOMString)... nodes); [CEReactions, Unscopable] undefined remove(); }; DocumentType includes ChildNode; @@ -339,6 +339,8 @@ interface ShadowRoot : DocumentFragment { readonly attribute ShadowRootMode mode; readonly attribute boolean delegatesFocus; readonly attribute SlotAssignmentMode slotAssignment; + readonly attribute boolean clonable; + readonly attribute boolean serializable; readonly attribute Element host; attribute EventHandler onslotchange; }; @@ -397,6 +399,7 @@ dictionary ShadowRootInit { boolean delegatesFocus = false; SlotAssignmentMode slotAssignment = "named"; boolean clonable = false; + boolean serializable = false; }; [Exposed=Window, diff --git a/test/wpt/tests/interfaces/edit-context.idl b/test/wpt/tests/interfaces/edit-context.idl index eb3174e25f5..60ddbafe758 100644 --- a/test/wpt/tests/interfaces/edit-context.idl +++ b/test/wpt/tests/interfaces/edit-context.idl @@ -59,7 +59,7 @@ interface TextUpdateEvent : Event { readonly attribute unsigned long selectionEnd; }; -enum UnderlineStyle { "none", "solid", "double", "dotted", "dashed", "wavy" }; +enum UnderlineStyle { "none", "solid", "dotted", "dashed", "wavy" }; enum UnderlineThickness { "none", "thin", "thick" }; dictionary TextFormatInit { diff --git a/test/wpt/tests/interfaces/fenced-frame.idl b/test/wpt/tests/interfaces/fenced-frame.idl index 0eed6a6b4ba..2107655aa92 100644 --- a/test/wpt/tests/interfaces/fenced-frame.idl +++ b/test/wpt/tests/interfaces/fenced-frame.idl @@ -52,7 +52,11 @@ dictionary FenceEvent { DOMString eventType; DOMString eventData; sequence destination; + + // When setting event data to be used later in an automatic beacon, the + // following properties are used: boolean once = false; + boolean crossOriginExposed = false; // When reporting to a custom destination URL (with substitution of macros defined by // the Protected Audience buyer), the following property is used: diff --git a/test/wpt/tests/interfaces/gamepad-extensions.idl b/test/wpt/tests/interfaces/gamepad-extensions.idl index d7d750654fd..81776a46ec9 100644 --- a/test/wpt/tests/interfaces/gamepad-extensions.idl +++ b/test/wpt/tests/interfaces/gamepad-extensions.idl @@ -9,38 +9,6 @@ enum GamepadHand { "right" }; -[Exposed=Window] -interface GamepadHapticActuator { - readonly attribute GamepadHapticActuatorType type; - boolean canPlayEffectType(GamepadHapticEffectType type); - Promise playEffect( - GamepadHapticEffectType type, - optional GamepadEffectParameters params = {}); - Promise pulse(double value, double duration); - Promise reset(); -}; - -enum GamepadHapticsResult { - "complete", - "preempted" -}; - -enum GamepadHapticActuatorType { - "vibration", - "dual-rumble" -}; - -enum GamepadHapticEffectType { - "dual-rumble" -}; - -dictionary GamepadEffectParameters { - double duration = 0.0; - double startDelay = 0.0; - double strongMagnitude = 0.0; - double weakMagnitude = 0.0; -}; - [Exposed=Window] interface GamepadPose { readonly attribute boolean hasOrientation; @@ -67,5 +35,9 @@ partial interface Gamepad { readonly attribute FrozenArray hapticActuators; readonly attribute GamepadPose? pose; readonly attribute FrozenArray? touchEvents; - [SameObject] readonly attribute GamepadHapticActuator? vibrationActuator; +}; + +[Exposed=Window] +partial interface GamepadHapticActuator { + Promise pulse(double value, double duration); }; diff --git a/test/wpt/tests/interfaces/gamepad.idl b/test/wpt/tests/interfaces/gamepad.idl index bbc62da3c44..d922d7b80b0 100644 --- a/test/wpt/tests/interfaces/gamepad.idl +++ b/test/wpt/tests/interfaces/gamepad.idl @@ -3,7 +3,7 @@ // (https://github.com/w3c/webref) // Source: Gamepad (https://w3c.github.io/gamepad/) -[Exposed=Window, SecureContext] +[Exposed=Window] interface Gamepad { readonly attribute DOMString id; readonly attribute long index; @@ -12,9 +12,10 @@ interface Gamepad { readonly attribute GamepadMappingType mapping; readonly attribute FrozenArray axes; readonly attribute FrozenArray buttons; + [SameObject] readonly attribute GamepadHapticActuator vibrationActuator; }; -[Exposed=Window, SecureContext] +[Exposed=Window] interface GamepadButton { readonly attribute boolean pressed; readonly attribute boolean touched; @@ -27,12 +28,41 @@ enum GamepadMappingType { "xr-standard", }; +[Exposed=Window] +interface GamepadHapticActuator { + [SameObject] readonly attribute FrozenArray effects; + Promise playEffect( + GamepadHapticEffectType type, + optional GamepadEffectParameters params = {} + ); + Promise reset(); +}; + +enum GamepadHapticsResult { + "complete", + "preempted" +}; + +enum GamepadHapticEffectType { + "dual-rumble", + "trigger-rumble" +}; + +dictionary GamepadEffectParameters { + unsigned long long duration = 0; + unsigned long long startDelay = 0; + double strongMagnitude = 0.0; + double weakMagnitude = 0.0; + double leftTrigger = 0.0; + double rightTrigger = 0.0; +}; + [Exposed=Window] partial interface Navigator { sequence getGamepads(); }; -[Exposed=Window, SecureContext] +[Exposed=Window] interface GamepadEvent: Event { constructor(DOMString type, GamepadEventInit eventInitDict); diff --git a/test/wpt/tests/interfaces/geolocation.idl b/test/wpt/tests/interfaces/geolocation.idl index 4b971f097ba..8c0acfc6cc1 100644 --- a/test/wpt/tests/interfaces/geolocation.idl +++ b/test/wpt/tests/interfaces/geolocation.idl @@ -42,6 +42,7 @@ dictionary PositionOptions { interface GeolocationPosition { readonly attribute GeolocationCoordinates coords; readonly attribute EpochTimeStamp timestamp; + [Default] object toJSON(); }; [Exposed=Window, SecureContext] @@ -53,6 +54,7 @@ interface GeolocationCoordinates { readonly attribute double? altitudeAccuracy; readonly attribute double? heading; readonly attribute double? speed; + [Default] object toJSON(); }; [Exposed=Window] diff --git a/test/wpt/tests/interfaces/html.idl b/test/wpt/tests/interfaces/html.idl index b7501feea91..aad8994b87d 100644 --- a/test/wpt/tests/interfaces/html.idl +++ b/test/wpt/tests/interfaces/html.idl @@ -48,7 +48,7 @@ typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement; [LegacyOverrideBuiltIns] partial interface Document { - static Document parseHTMLUnsafe(DOMString html); + static Document parseHTMLUnsafe(HTMLString html); // resource metadata management [PutForwards=href, LegacyUnforgeable] readonly attribute Location? location; @@ -77,8 +77,8 @@ partial interface Document { [CEReactions] Document open(optional DOMString unused1, optional DOMString unused2); // both arguments are ignored WindowProxy? open(USVString url, DOMString name, DOMString features); [CEReactions] undefined close(); - [CEReactions] undefined write(DOMString... text); - [CEReactions] undefined writeln(DOMString... text); + [CEReactions] undefined write(HTMLString... text); + [CEReactions] undefined writeln(HTMLString... text); // user interaction readonly attribute WindowProxy? defaultView; @@ -123,6 +123,7 @@ interface HTMLElement : Element { readonly attribute DOMString accessKeyLabel; [CEReactions] attribute boolean draggable; [CEReactions] attribute boolean spellcheck; + [CEReactions] attribute DOMString writingSuggestions; [CEReactions] attribute DOMString autocapitalize; [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerText; @@ -451,7 +452,7 @@ interface HTMLIFrameElement : HTMLElement { [HTMLConstructor] constructor(); [CEReactions] attribute USVString src; - [CEReactions] attribute DOMString srcdoc; + [CEReactions] attribute HTMLString srcdoc; [CEReactions] attribute DOMString name; [SameObject, PutForwards=value] readonly attribute DOMTokenList sandbox; [CEReactions] attribute DOMString allow; @@ -1243,6 +1244,8 @@ interface HTMLTemplateElement : HTMLElement { readonly attribute DocumentFragment content; [CEReactions] attribute DOMString shadowRootMode; [CEReactions] attribute boolean shadowRootDelegatesFocus; + [CEReactions] attribute boolean shadowRootClonable; + [CEReactions] attribute boolean shadowRootSerializable; }; [Exposed=Window] @@ -1577,7 +1580,6 @@ interface OffscreenCanvas : EventTarget { [Exposed=(Window,Worker)] interface OffscreenCanvasRenderingContext2D { - undefined commit(); readonly attribute OffscreenCanvas canvas; }; @@ -1633,6 +1635,9 @@ interface ElementInternals { boolean reportValidity(); readonly attribute NodeList labels; + + // Custom state pseudo-class + [SameObject] readonly attribute CustomStateSet states; }; // Accessibility semantics @@ -1651,6 +1656,11 @@ dictionary ValidityStateFlags { boolean customError = false; }; +[Exposed=Window] +interface CustomStateSet { + setlike; +}; + [Exposed=(Window)] interface VisibilityStateEntry : PerformanceEntry { readonly attribute DOMString name; // shadows inherited name @@ -1872,6 +1882,7 @@ interface Navigation : EventTarget { readonly attribute NavigationHistoryEntry? currentEntry; undefined updateCurrentEntry(NavigationUpdateCurrentEntryOptions options); readonly attribute NavigationTransition? transition; + readonly attribute NavigationActivation? activation; readonly attribute boolean canGoBack; readonly attribute boolean canGoForward; @@ -1944,6 +1955,13 @@ interface NavigationTransition { readonly attribute Promise finished; }; +[Exposed=Window] +interface NavigationActivation { + readonly attribute NavigationHistoryEntry? from; + readonly attribute NavigationHistoryEntry entry; + readonly attribute NavigationType navigationType; +}; + [Exposed=Window] interface NavigateEvent : Event { constructor(DOMString type, NavigateEventInit eventInitDict); @@ -2044,6 +2062,28 @@ dictionary HashChangeEventInit : EventInit { USVString newURL = ""; }; +[Exposed=Window] +interface PageSwapEvent : Event { + constructor(DOMString type, optional PageSwapEventInit eventInitDict = {}); + readonly attribute NavigationActivation? activation; + readonly attribute ViewTransition? viewTransition; +}; + +dictionary PageSwapEventInit : EventInit { + NavigationActivation? activation = null; + ViewTransition? viewTransition = null; +}; + +[Exposed=Window] +interface PageRevealEvent : Event { + constructor(DOMString type, optional PageRevealEventInit eventInitDict = {}); + readonly attribute ViewTransition? viewTransition; +}; + +dictionary PageRevealEventInit : EventInit { + ViewTransition? viewTransition = null; +}; + [Exposed=Window] interface PageTransitionEvent : Event { constructor(DOMString type, optional PageTransitionEventInit eventInitDict = {}); @@ -2060,6 +2100,23 @@ interface BeforeUnloadEvent : Event { attribute DOMString returnValue; }; +[Exposed=Window] +interface NotRestoredReasonDetails { + readonly attribute DOMString reason; + [Default] object toJSON(); +}; + +[Exposed=Window] +interface NotRestoredReasons { + readonly attribute DOMString? src; + readonly attribute DOMString? id; + readonly attribute DOMString? name; + readonly attribute DOMString? url; + readonly attribute FrozenArray? reasons; + readonly attribute FrozenArray? children; + [Default] object toJSON(); +}; + [Exposed=*] interface ErrorEvent : Event { constructor(DOMString type, optional ErrorEventInit eventInitDict = {}); @@ -2083,12 +2140,12 @@ dictionary ErrorEventInit : EventInit { interface PromiseRejectionEvent : Event { constructor(DOMString type, PromiseRejectionEventInit eventInitDict); - readonly attribute Promise promise; + readonly attribute object promise; readonly attribute any reason; }; dictionary PromiseRejectionEventInit : EventInit { - required Promise promise; + required object promise; any reason; }; @@ -2193,7 +2250,9 @@ interface mixin WindowEventHandlers { attribute EventHandler onoffline; attribute EventHandler ononline; attribute EventHandler onpagehide; + attribute EventHandler onpagereveal; attribute EventHandler onpageshow; + attribute EventHandler onpageswap; attribute EventHandler onpopstate; attribute EventHandler onrejectionhandled; attribute EventHandler onstorage; @@ -2233,11 +2292,32 @@ interface mixin WindowOrWorkerGlobalScope { Window includes WindowOrWorkerGlobalScope; WorkerGlobalScope includes WindowOrWorkerGlobalScope; +partial interface Element { + [CEReactions] undefined setHTMLUnsafe(HTMLString html); + DOMString getHTML(optional GetHTMLOptions options = {}); + + [CEReactions] attribute [LegacyNullToEmptyString] HTMLString innerHTML; + [CEReactions] attribute [LegacyNullToEmptyString] HTMLString outerHTML; + [CEReactions] undefined insertAdjacentHTML(DOMString position, HTMLString string); +}; + +partial interface ShadowRoot { + [CEReactions] undefined setHTMLUnsafe(HTMLString html); + DOMString getHTML(optional GetHTMLOptions options = {}); + + [CEReactions] attribute [LegacyNullToEmptyString] HTMLString innerHTML; +}; + +dictionary GetHTMLOptions { + boolean serializableShadowRoots = false; + sequence shadowRoots = []; +}; + [Exposed=Window] interface DOMParser { constructor(); - [NewObject] Document parseFromString(DOMString string, DOMParserSupportedType type); + [NewObject] Document parseFromString(HTMLString string, DOMParserSupportedType type); }; enum DOMParserSupportedType { @@ -2248,12 +2328,8 @@ enum DOMParserSupportedType { "image/svg+xml" }; -partial interface Element { - undefined setHTMLUnsafe(DOMString html); -}; - -partial interface ShadowRoot { - undefined setHTMLUnsafe(DOMString html); +partial interface Range { + [CEReactions, NewObject] DocumentFragment createContextualFragment(HTMLString string); }; [Exposed=Window] @@ -2445,6 +2521,7 @@ interface MessagePort : EventTarget { // event handlers attribute EventHandler onmessage; attribute EventHandler onmessageerror; + attribute EventHandler onclose; }; dictionary StructuredSerializeOptions { @@ -2467,7 +2544,7 @@ interface WorkerGlobalScope : EventTarget { readonly attribute WorkerGlobalScope self; readonly attribute WorkerLocation location; readonly attribute WorkerNavigator navigator; - undefined importScripts(USVString... urls); + undefined importScripts(ScriptURLString... urls); attribute OnErrorEventHandler onerror; attribute EventHandler onlanguagechange; @@ -2505,7 +2582,7 @@ interface mixin AbstractWorker { [Exposed=(Window,DedicatedWorker,SharedWorker)] interface Worker : EventTarget { - constructor(USVString scriptURL, optional WorkerOptions options = {}); + constructor(ScriptURLString scriptURL, optional WorkerOptions options = {}); undefined terminate(); @@ -2527,7 +2604,7 @@ Worker includes AbstractWorker; [Exposed=Window] interface SharedWorker : EventTarget { - constructor(USVString scriptURL, optional (DOMString or WorkerOptions) options = {}); + constructor(ScriptURLString scriptURL, optional (DOMString or WorkerOptions) options = {}); readonly attribute MessagePort port; }; diff --git a/test/wpt/tests/interfaces/interest-invokers.tentative.idl b/test/wpt/tests/interfaces/interest-invokers.tentative.idl new file mode 100644 index 00000000000..f89af4d7341 --- /dev/null +++ b/test/wpt/tests/interfaces/interest-invokers.tentative.idl @@ -0,0 +1,7 @@ +interface mixin InterestInvokerElement { + [CEReactions,Reflect=interesttarget] attribute Element? interestTargetElement; +}; + +HTMLInputElement includes InterestInvokerElement; +HTMLButtonElement includes InterestInvokerElement; +HTMLAnchorElement includes InterestInvokerElement; \ No newline at end of file diff --git a/test/wpt/tests/interfaces/invokers.tentative.idl b/test/wpt/tests/interfaces/invokers.tentative.idl index 62f7398b827..eb1b8247f06 100644 --- a/test/wpt/tests/interfaces/invokers.tentative.idl +++ b/test/wpt/tests/interfaces/invokers.tentative.idl @@ -1,6 +1,6 @@ interface mixin InvokerElement { [CEReactions,Reflect=invoketarget] attribute Element? invokeTargetElement; - [CEReactions,Reflect,ReflectMissing="auto",ReflectEmpty="auto"] attribute DOMString invokeAction; + [CEReactions,Reflect=invokeaction] attribute DOMString invokeAction; }; interface InvokeEvent : Event { @@ -11,5 +11,5 @@ interface InvokeEvent : Event { dictionary InvokeEventInit : EventInit { Element? invoker = null; - DOMString action = "auto"; + DOMString action = ""; }; diff --git a/test/wpt/tests/interfaces/long-animation-frames.idl b/test/wpt/tests/interfaces/long-animation-frames.idl new file mode 100644 index 00000000000..79a42ca8f0a --- /dev/null +++ b/test/wpt/tests/interfaces/long-animation-frames.idl @@ -0,0 +1,54 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Long Animation Frames API (https://w3c.github.io/long-animation-frames/) + +[Exposed=Window] +interface PerformanceLongAnimationFrameTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + + readonly attribute DOMHighResTimeStamp renderStart; + readonly attribute DOMHighResTimeStamp styleAndLayoutStart; + readonly attribute DOMHighResTimeStamp blockingDuration; + readonly attribute DOMHighResTimeStamp firstUIEventTimestamp; + [SameObject] readonly attribute FrozenArray scripts; + [Default] object toJSON(); +}; + +enum ScriptInvokerType { + "classic-script", + "module-script", + "event-listener", + "user-callback", + "resolve-promise", + "reject-promise" +}; + +enum ScriptWindowAttribution { + "self", "descendant", "ancestor", "same-page", "other" +}; + +[Exposed=Window] +interface PerformanceScriptTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + + readonly attribute ScriptInvokerType invokerType; + readonly attribute DOMString invoker; + readonly attribute DOMHighResTimeStamp executionStart; + readonly attribute DOMString sourceURL; + readonly attribute DOMString sourceFunctionName; + readonly attribute long long sourceCharPosition; + readonly attribute DOMHighResTimeStamp pauseDuration; + readonly attribute DOMHighResTimeStamp forcedStyleAndLayoutDuration; + readonly attribute Window? window; + readonly attribute ScriptWindowAttribution windowAttribution; + [Default] object toJSON(); +}; diff --git a/test/wpt/tests/interfaces/longtasks.idl b/test/wpt/tests/interfaces/longtasks.idl index 064d1072d2d..3717469fd09 100644 --- a/test/wpt/tests/interfaces/longtasks.idl +++ b/test/wpt/tests/interfaces/longtasks.idl @@ -5,24 +5,27 @@ [Exposed=Window] interface PerformanceLongTaskTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + readonly attribute FrozenArray attribution; [Default] object toJSON(); }; [Exposed=Window] interface TaskAttributionTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + readonly attribute DOMString containerType; readonly attribute DOMString containerSrc; readonly attribute DOMString containerId; readonly attribute DOMString containerName; [Default] object toJSON(); }; - -[Exposed=Window] -interface PerformanceLongAnimationFrameTiming : PerformanceEntry { - readonly attribute DOMHighResTimeStamp renderStart; - readonly attribute DOMHighResTimeStamp styleAndLayoutStart; - readonly attribute DOMHighResTimeStamp blockingDuration; - - [Default] object toJSON(); -}; diff --git a/test/wpt/tests/interfaces/media-source.idl b/test/wpt/tests/interfaces/media-source.idl index adaac2ca9b2..de153e615a4 100644 --- a/test/wpt/tests/interfaces/media-source.idl +++ b/test/wpt/tests/interfaces/media-source.idl @@ -18,23 +18,25 @@ enum EndOfStreamError { interface MediaSource : EventTarget { constructor(); - [ SameObject, Exposed=DedicatedWorker ] - readonly attribute MediaSourceHandle handle; - - readonly attribute SourceBufferList sourceBuffers; - readonly attribute SourceBufferList activeSourceBuffers; - readonly attribute ReadyState readyState; - attribute unrestricted double duration; - attribute EventHandler onsourceopen; - attribute EventHandler onsourceended; - attribute EventHandler onsourceclose; - static readonly attribute boolean canConstructInDedicatedWorker; - SourceBuffer addSourceBuffer (DOMString type); - undefined removeSourceBuffer (SourceBuffer sourceBuffer); - undefined endOfStream (optional EndOfStreamError error); - undefined setLiveSeekableRange (double start, double end); - undefined clearLiveSeekableRange (); - static boolean isTypeSupported (DOMString type); + [SameObject, Exposed=DedicatedWorker] + readonly attribute MediaSourceHandle handle; + readonly attribute SourceBufferList sourceBuffers; + readonly attribute SourceBufferList activeSourceBuffers; + readonly attribute ReadyState readyState; + + attribute unrestricted double duration; + attribute EventHandler onsourceopen; + attribute EventHandler onsourceended; + attribute EventHandler onsourceclose; + + static readonly attribute boolean canConstructInDedicatedWorker; + + SourceBuffer addSourceBuffer(DOMString type); + undefined removeSourceBuffer(SourceBuffer sourceBuffer); + undefined endOfStream(optional EndOfStreamError error); + undefined setLiveSeekableRange(double start, double end); + undefined clearLiveSeekableRange(); + static boolean isTypeSupported(DOMString type); }; [Transferable, Exposed=(Window,DedicatedWorker)] @@ -47,45 +49,75 @@ enum AppendMode { [Exposed=(Window,DedicatedWorker)] interface SourceBuffer : EventTarget { - attribute AppendMode mode; - readonly attribute boolean updating; - readonly attribute TimeRanges buffered; - attribute double timestampOffset; - readonly attribute AudioTrackList audioTracks; - readonly attribute VideoTrackList videoTracks; - readonly attribute TextTrackList textTracks; - attribute double appendWindowStart; - attribute unrestricted double appendWindowEnd; - attribute EventHandler onupdatestart; - attribute EventHandler onupdate; - attribute EventHandler onupdateend; - attribute EventHandler onerror; - attribute EventHandler onabort; - undefined appendBuffer (BufferSource data); - undefined abort (); - undefined changeType (DOMString type); - undefined remove (double start, unrestricted double end); + attribute AppendMode mode; + readonly attribute boolean updating; + readonly attribute TimeRanges buffered; + attribute double timestampOffset; + readonly attribute AudioTrackList audioTracks; + readonly attribute VideoTrackList videoTracks; + readonly attribute TextTrackList textTracks; + attribute double appendWindowStart; + attribute unrestricted double appendWindowEnd; + + attribute EventHandler onupdatestart; + attribute EventHandler onupdate; + attribute EventHandler onupdateend; + attribute EventHandler onerror; + attribute EventHandler onabort; + + undefined appendBuffer(BufferSource data); + undefined abort(); + undefined changeType(DOMString type); + undefined remove(double start, unrestricted double end); }; [Exposed=(Window,DedicatedWorker)] interface SourceBufferList : EventTarget { - readonly attribute unsigned long length; - attribute EventHandler onaddsourcebuffer; - attribute EventHandler onremovesourcebuffer; - getter SourceBuffer (unsigned long index); + readonly attribute unsigned long length; + + attribute EventHandler onaddsourcebuffer; + attribute EventHandler onremovesourcebuffer; + + getter SourceBuffer (unsigned long index); +}; + +[Exposed=(Window,DedicatedWorker)] +interface ManagedMediaSource : MediaSource { + constructor(); + readonly attribute boolean streaming; + attribute EventHandler onstartstreaming; + attribute EventHandler onendstreaming; +}; + +[Exposed=(Window,DedicatedWorker)] +interface BufferedChangeEvent : Event { + constructor(DOMString type, optional BufferedChangeEventInit eventInitDict = {}); + + [SameObject] readonly attribute TimeRanges addedRanges; + [SameObject] readonly attribute TimeRanges removedRanges; +}; + +dictionary BufferedChangeEventInit : EventInit { + TimeRanges addedRanges; + TimeRanges removedRanges; +}; + +[Exposed=(Window,DedicatedWorker)] +interface ManagedSourceBuffer : SourceBuffer { + attribute EventHandler onbufferedchange; }; [Exposed=(Window,DedicatedWorker)] partial interface AudioTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; [Exposed=(Window,DedicatedWorker)] partial interface VideoTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; [Exposed=(Window,DedicatedWorker)] partial interface TextTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; diff --git a/test/wpt/tests/interfaces/mediacapture-streams.idl b/test/wpt/tests/interfaces/mediacapture-streams.idl index 373f0c328d9..f2ca21389e9 100644 --- a/test/wpt/tests/interfaces/mediacapture-streams.idl +++ b/test/wpt/tests/interfaces/mediacapture-streams.idl @@ -189,16 +189,6 @@ dictionary MediaStreamConstraints { (boolean or MediaTrackConstraints) audio = false; }; -partial interface Navigator { - [SecureContext] undefined getUserMedia(MediaStreamConstraints constraints, - NavigatorUserMediaSuccessCallback successCallback, - NavigatorUserMediaErrorCallback errorCallback); -}; - -callback NavigatorUserMediaSuccessCallback = undefined (MediaStream stream); - -callback NavigatorUserMediaErrorCallback = undefined (DOMException error); - dictionary DoubleRange { double max; double min; diff --git a/test/wpt/tests/interfaces/mediasession.idl b/test/wpt/tests/interfaces/mediasession.idl index 2cca3674a5e..e6c8e464627 100644 --- a/test/wpt/tests/interfaces/mediasession.idl +++ b/test/wpt/tests/interfaces/mediasession.idl @@ -44,9 +44,9 @@ interface MediaSession { undefined setPositionState(optional MediaPositionState state = {}); - undefined setMicrophoneActive(boolean active); + Promise setMicrophoneActive(boolean active); - undefined setCameraActive(boolean active); + Promise setCameraActive(boolean active); }; [Exposed=Window] @@ -56,6 +56,7 @@ interface MediaMetadata { attribute DOMString artist; attribute DOMString album; attribute FrozenArray artwork; + [SameObject] readonly attribute FrozenArray chapterInfo; }; dictionary MediaMetadataInit { @@ -63,6 +64,20 @@ dictionary MediaMetadataInit { DOMString artist = ""; DOMString album = ""; sequence artwork = []; + sequence chapterInfo = []; +}; + +[Exposed=Window] +interface ChapterInformation { + readonly attribute DOMString title; + readonly attribute double startTime; + [SameObject] readonly attribute FrozenArray artwork; +}; + +dictionary ChapterInformationInit { + DOMString title = ""; + double startTime = 0; + sequence artwork = []; }; dictionary MediaImage { @@ -72,14 +87,24 @@ dictionary MediaImage { }; dictionary MediaPositionState { - double duration; + unrestricted double duration; double playbackRate; double position; }; dictionary MediaSessionActionDetails { required MediaSessionAction action; +}; + +dictionary MediaSessionSeekActionDetails : MediaSessionActionDetails { double seekOffset; - double seekTime; +}; + +dictionary MediaSessionSeekToActionDetails : MediaSessionActionDetails { + required double seekTime; boolean fastSeek; }; + +dictionary MediaSessionCaptureActionDetails : MediaSessionActionDetails { + boolean isActivating; +}; diff --git a/test/wpt/tests/interfaces/navigation-timing.idl b/test/wpt/tests/interfaces/navigation-timing.idl index 355950160e3..b381b486e19 100644 --- a/test/wpt/tests/interfaces/navigation-timing.idl +++ b/test/wpt/tests/interfaces/navigation-timing.idl @@ -16,6 +16,7 @@ interface PerformanceNavigationTiming : PerformanceResourceTiming { readonly attribute NavigationTimingType type; readonly attribute unsigned short redirectCount; readonly attribute DOMHighResTimeStamp criticalCHRestart; + readonly attribute NotRestoredReasons? notRestoredReasons; [Default] object toJSON(); }; diff --git a/test/wpt/tests/interfaces/observable.tentative.idl b/test/wpt/tests/interfaces/observable.tentative.idl new file mode 100644 index 00000000000..3ccd486a202 --- /dev/null +++ b/test/wpt/tests/interfaces/observable.tentative.idl @@ -0,0 +1,31 @@ +[Exposed=*] +interface Subscriber { + undefined next(any value); + undefined error(any error); + undefined complete(); + undefined addTeardown(VoidFunction teardown); + readonly attribute boolean active; + readonly attribute AbortSignal signal; +}; + + +callback SubscribeCallback = undefined (Subscriber subscriber); +callback SubscriptionObserverCallback = undefined (any value); + +dictionary SubscriptionObserver { + SubscriptionObserverCallback next; + SubscriptionObserverCallback error; + VoidFunction complete; +}; + +typedef (SubscriptionObserverCallback or Observer) ObserverUnion; + +dictionary SubscribeOptions { + AbortSignal signal; +}; + +[Exposed=*] +interface Observable { + constructor(SubscribeCallback callback); + undefined subscribe(optional ObserverUnion observer = {}, optional SubscribeOptions options = {}); +}; diff --git a/test/wpt/tests/interfaces/orientation-event.idl b/test/wpt/tests/interfaces/orientation-event.idl index 965e833fd21..ffacfe576f2 100644 --- a/test/wpt/tests/interfaces/orientation-event.idl +++ b/test/wpt/tests/interfaces/orientation-event.idl @@ -15,7 +15,7 @@ interface DeviceOrientationEvent : Event { readonly attribute double? gamma; readonly attribute boolean absolute; - static Promise requestPermission(); + static Promise requestPermission(optional boolean absolute = false); }; dictionary DeviceOrientationEventInit : EventInit { diff --git a/test/wpt/tests/interfaces/paint-timing.idl b/test/wpt/tests/interfaces/paint-timing.idl index 052b74ef6c2..396f461e94c 100644 --- a/test/wpt/tests/interfaces/paint-timing.idl +++ b/test/wpt/tests/interfaces/paint-timing.idl @@ -1,7 +1,7 @@ // GENERATED CONTENT - DO NOT EDIT // Content was automatically extracted by Reffy into webref // (https://github.com/w3c/webref) -// Source: Paint Timing 1 (https://w3c.github.io/paint-timing/) +// Source: Paint Timing (https://w3c.github.io/paint-timing/) [Exposed=Window] interface PerformancePaintTiming : PerformanceEntry {}; diff --git a/test/wpt/tests/interfaces/permissions.idl b/test/wpt/tests/interfaces/permissions.idl index fbcb674e561..62c2e3ad76f 100644 --- a/test/wpt/tests/interfaces/permissions.idl +++ b/test/wpt/tests/interfaces/permissions.idl @@ -36,6 +36,6 @@ enum PermissionState { }; dictionary PermissionSetParameters { - required PermissionDescriptor descriptor; + required object descriptor; required PermissionState state; }; diff --git a/test/wpt/tests/interfaces/sanitizer-api.idl b/test/wpt/tests/interfaces/sanitizer-api.idl index 117a55fdf77..8f5c667973a 100644 --- a/test/wpt/tests/interfaces/sanitizer-api.idl +++ b/test/wpt/tests/interfaces/sanitizer-api.idl @@ -3,36 +3,45 @@ // (https://github.com/w3c/webref) // Source: HTML Sanitizer API (https://wicg.github.io/sanitizer-api/) -[ - Exposed=(Window), - SecureContext -] interface Sanitizer { - constructor(optional SanitizerConfig config = {}); +dictionary SetHTMLOptions { + (Sanitizer or SanitizerConfig) sanitizer = {}; +}; - DocumentFragment sanitize((Document or DocumentFragment) input); - Element? sanitizeFor(DOMString element, DOMString input); +[Exposed=(Window,Worker)] +interface Sanitizer { + constructor(optional SanitizerConfig config = {}); + SanitizerConfig get(); + SanitizerConfig getUnsafe(); +}; - SanitizerConfig getConfiguration(); - static SanitizerConfig getDefaultConfiguration(); +dictionary SanitizerElementNamespace { + required DOMString name; + DOMString? _namespace = "http://www.w3.org/1999/xhtml"; }; -dictionary SetHTMLOptions { - Sanitizer sanitizer; +// Used by "elements" +dictionary SanitizerElementNamespaceWithAttributes : SanitizerElementNamespace { + sequence attributes; + sequence removeAttributes; }; -[SecureContext] -partial interface Element { - undefined setHTML(DOMString input, optional SetHTMLOptions options = {}); + +typedef (DOMString or SanitizerElementNamespace) SanitizerElement; +typedef (DOMString or SanitizerElementNamespaceWithAttributes) SanitizerElementWithAttributes; + +dictionary SanitizerAttributeNamespace { + required DOMString name; + DOMString? _namespace = null; }; +typedef (DOMString or SanitizerAttributeNamespace) SanitizerAttribute; dictionary SanitizerConfig { - sequence allowElements; - sequence blockElements; - sequence dropElements; - AttributeMatchList allowAttributes; - AttributeMatchList dropAttributes; - boolean allowCustomElements; - boolean allowUnknownMarkup; - boolean allowComments; -}; + sequence elements; + sequence removeElements; + sequence replaceWithChildrenElements; -typedef record> AttributeMatchList; + sequence attributes; + sequence removeAttributes; + + boolean comments; + boolean dataAttributes; +}; diff --git a/test/wpt/tests/interfaces/serial.idl b/test/wpt/tests/interfaces/serial.idl index e624c3c1a60..37986b7ac2f 100644 --- a/test/wpt/tests/interfaces/serial.idl +++ b/test/wpt/tests/interfaces/serial.idl @@ -36,6 +36,7 @@ dictionary SerialPortFilter { interface SerialPort : EventTarget { attribute EventHandler onconnect; attribute EventHandler ondisconnect; + readonly attribute boolean connected; readonly attribute ReadableStream readable; readonly attribute WritableStream writable; diff --git a/test/wpt/tests/interfaces/service-workers.idl b/test/wpt/tests/interfaces/service-workers.idl index 6d44d61debb..1ddc6d71d83 100644 --- a/test/wpt/tests/interfaces/service-workers.idl +++ b/test/wpt/tests/interfaces/service-workers.idl @@ -92,7 +92,7 @@ dictionary NavigationPreloadState { ByteString headerValue; }; -[Global=(Worker,ServiceWorker), Exposed=ServiceWorker] +[Global=(Worker,ServiceWorker), Exposed=ServiceWorker, SecureContext] interface ServiceWorkerGlobalScope : WorkerGlobalScope { [SameObject] readonly attribute Clients clients; [SameObject] readonly attribute ServiceWorkerRegistration registration; @@ -165,6 +165,41 @@ dictionary ExtendableEventInit : EventInit { // Defined for the forward compatibility across the derived events }; +[Exposed=ServiceWorker] +interface InstallEvent : ExtendableEvent { + Promise addRoutes((RouterRule or sequence) rules); +}; + +dictionary RouterRule { + required RouterCondition condition; + required RouterSource source; +}; + +dictionary RouterCondition { + URLPatternCompatible urlPattern; + ByteString requestMethod; + RequestMode requestMode; + RequestDestination requestDestination; + RunningStatus runningStatus; + + sequence _or; + RouterCondition not; +}; + +typedef (RouterSourceDict or RouterSourceEnum) RouterSource; + +dictionary RouterSourceDict { + DOMString cacheName; +}; + +enum RunningStatus { "running", "not-running" }; +enum RouterSourceEnum { + "cache", + "fetch-event", + "network", + "race-network-and-fetch-handler" +}; + [Exposed=ServiceWorker] interface FetchEvent : ExtendableEvent { constructor(DOMString type, FetchEventInit eventInitDict); diff --git a/test/wpt/tests/interfaces/shape-detection-api.idl b/test/wpt/tests/interfaces/shape-detection-api.idl index 4fc1f085ea2..24d3b980854 100644 --- a/test/wpt/tests/interfaces/shape-detection-api.idl +++ b/test/wpt/tests/interfaces/shape-detection-api.idl @@ -17,11 +17,11 @@ dictionary FaceDetectorOptions { dictionary DetectedFace { required DOMRectReadOnly boundingBox; - required FrozenArray? landmarks; + required sequence? landmarks; }; dictionary Landmark { - required FrozenArray locations; + required sequence locations; LandmarkType type; }; @@ -48,7 +48,7 @@ dictionary DetectedBarcode { required DOMRectReadOnly boundingBox; required DOMString rawValue; required BarcodeFormat format; - required FrozenArray cornerPoints; + required sequence cornerPoints; }; enum BarcodeFormat { diff --git a/test/wpt/tests/interfaces/shared-storage.idl b/test/wpt/tests/interfaces/shared-storage.idl index edbe2c2bcc3..c40344e74d2 100644 --- a/test/wpt/tests/interfaces/shared-storage.idl +++ b/test/wpt/tests/interfaces/shared-storage.idl @@ -3,40 +3,30 @@ // (https://github.com/w3c/webref) // Source: Shared Storage API (https://wicg.github.io/shared-storage/) +typedef (USVString or FencedFrameConfig) SharedStorageResponse; + [Exposed=(Window)] interface SharedStorageWorklet : Worklet { + Promise selectURL(DOMString name, + FrozenArray urls, + optional SharedStorageRunOperationMethodOptions options = {}); + Promise run(DOMString name, + optional SharedStorageRunOperationMethodOptions options = {}); }; +callback RunFunctionForSharedStorageSelectURLOperation = Promise(sequence urls, optional any data); + [Exposed=SharedStorageWorklet, Global=SharedStorageWorklet] interface SharedStorageWorkletGlobalScope : WorkletGlobalScope { undefined register(DOMString name, - SharedStorageOperationConstructor operationCtor); + Function operationCtor); readonly attribute WorkletSharedStorage sharedStorage; }; -callback SharedStorageOperationConstructor = - SharedStorageOperation(optional SharedStorageRunOperationMethodOptions options); - -[Exposed=SharedStorageWorklet] -interface SharedStorageOperation { -}; - -dictionary SharedStorageRunOperationMethodOptions { - object data; - boolean resolveToConfig = false; - boolean keepAlive = false; -}; - -[Exposed=SharedStorageWorklet] -interface SharedStorageRunOperation : SharedStorageOperation { - Promise run(object data); -}; - -[Exposed=SharedStorageWorklet] -interface SharedStorageSelectURLOperation : SharedStorageOperation { - Promise run(object data, - FrozenArray urls); +dictionary SharedStorageUrlWithMetadata { + required USVString url; + object reportingMetadata; }; [Exposed=(Window,SharedStorageWorklet)] @@ -54,22 +44,23 @@ dictionary SharedStorageSetMethodOptions { boolean ignoreIfPresent = false; }; -typedef (USVString or FencedFrameConfig) SharedStorageResponse; - [Exposed=(Window)] interface WindowSharedStorage : SharedStorage { - Promise run(DOMString name, - optional SharedStorageRunOperationMethodOptions options = {}); Promise selectURL(DOMString name, FrozenArray urls, optional SharedStorageRunOperationMethodOptions options = {}); + Promise run(DOMString name, + optional SharedStorageRunOperationMethodOptions options = {}); + + Promise createWorklet(USVString moduleURL, optional WorkletOptions options = {}); readonly attribute SharedStorageWorklet worklet; }; -dictionary SharedStorageUrlWithMetadata { - required USVString url; - object reportingMetadata; +dictionary SharedStorageRunOperationMethodOptions { + object data; + boolean resolveToConfig = false; + boolean keepAlive = false; }; partial interface Window { diff --git a/test/wpt/tests/interfaces/storage-buckets.idl b/test/wpt/tests/interfaces/storage-buckets.idl index 79f6c947d16..581ba8e0bb3 100644 --- a/test/wpt/tests/interfaces/storage-buckets.idl +++ b/test/wpt/tests/interfaces/storage-buckets.idl @@ -20,8 +20,8 @@ interface StorageBucketManager { dictionary StorageBucketOptions { boolean persisted = false; - unsigned long long? quota; - DOMHighResTimeStamp? expires; + unsigned long long quota; + DOMHighResTimeStamp expires; }; [Exposed=(Window,Worker), diff --git a/test/wpt/tests/interfaces/text-detection-api.idl b/test/wpt/tests/interfaces/text-detection-api.idl index 95b642749f7..b6745b18754 100644 --- a/test/wpt/tests/interfaces/text-detection-api.idl +++ b/test/wpt/tests/interfaces/text-detection-api.idl @@ -14,5 +14,5 @@ dictionary DetectedText { required DOMRectReadOnly boundingBox; required DOMString rawValue; - required FrozenArray cornerPoints; + required sequence cornerPoints; }; diff --git a/test/wpt/tests/interfaces/trust-token-api.idl b/test/wpt/tests/interfaces/trust-token-api.idl index fb7f15b4b74..9b74290da72 100644 --- a/test/wpt/tests/interfaces/trust-token-api.idl +++ b/test/wpt/tests/interfaces/trust-token-api.idl @@ -29,6 +29,6 @@ partial interface XMLHttpRequest { }; partial interface Document { - Promise hasPrivateTokens(USVString issuer); + Promise hasPrivateToken(USVString issuer); Promise hasRedemptionRecord(USVString issuer); }; diff --git a/test/wpt/tests/interfaces/trusted-types.idl b/test/wpt/tests/interfaces/trusted-types.idl index 23562381294..a0f88e4e6c3 100644 --- a/test/wpt/tests/interfaces/trusted-types.idl +++ b/test/wpt/tests/interfaces/trusted-types.idl @@ -7,21 +7,18 @@ interface TrustedHTML { stringifier; DOMString toJSON(); - static TrustedHTML fromLiteral(object templateStringsArray); }; [Exposed=(Window,Worker)] interface TrustedScript { stringifier; DOMString toJSON(); - static TrustedScript fromLiteral(object templateStringsArray); }; [Exposed=(Window,Worker)] interface TrustedScriptURL { stringifier; USVString toJSON(); - static TrustedScriptURL fromLiteral(object templateStringsArray); }; [Exposed=(Window,Worker)] interface TrustedTypePolicyFactory { @@ -35,12 +32,12 @@ interface TrustedScriptURL { DOMString? getAttributeType( DOMString tagName, DOMString attribute, - optional DOMString elementNs = "", - optional DOMString attrNs = ""); + optional DOMString? elementNs = "", + optional DOMString? attrNs = ""); DOMString? getPropertyType( DOMString tagName, DOMString property, - optional DOMString elementNs = ""); + optional DOMString? elementNs = ""); readonly attribute TrustedTypePolicy? defaultPolicy; }; @@ -53,13 +50,13 @@ interface TrustedTypePolicy { }; dictionary TrustedTypePolicyOptions { - CreateHTMLCallback? createHTML; - CreateScriptCallback? createScript; - CreateScriptURLCallback? createScriptURL; + CreateHTMLCallback createHTML; + CreateScriptCallback createScript; + CreateScriptURLCallback createScriptURL; }; -callback CreateHTMLCallback = DOMString (DOMString input, any... arguments); -callback CreateScriptCallback = DOMString (DOMString input, any... arguments); -callback CreateScriptURLCallback = USVString (DOMString input, any... arguments); +callback CreateHTMLCallback = DOMString? (DOMString input, any... arguments); +callback CreateScriptCallback = DOMString? (DOMString input, any... arguments); +callback CreateScriptURLCallback = USVString? (DOMString input, any... arguments); typedef [StringContext=TrustedHTML] DOMString HTMLString; typedef [StringContext=TrustedScript] DOMString ScriptString; diff --git a/test/wpt/tests/interfaces/turtledove.idl b/test/wpt/tests/interfaces/turtledove.idl index 4700a98038c..39e90ddae19 100644 --- a/test/wpt/tests/interfaces/turtledove.idl +++ b/test/wpt/tests/interfaces/turtledove.idl @@ -25,12 +25,15 @@ dictionary GenerateBidInterestGroup { boolean enableBiddingSignalsPrioritization = false; record priorityVector; + record> sellerCapabilities; DOMString executionMode = "compatibility"; USVString biddingLogicURL; USVString biddingWasmHelperURL; USVString updateURL; USVString trustedBiddingSignalsURL; sequence trustedBiddingSignalsKeys; + DOMString trustedBiddingSignalsSlotSizeMode = "none"; + long maxTrustedBiddingSignalsURLLength; any userBiddingSignals; sequence ads; sequence adComponents; @@ -66,24 +69,31 @@ partial interface Navigator { dictionary AuctionAdConfig { required USVString seller; required USVString decisionLogicURL; + USVString trustedScoringSignalsURL; + long maxTrustedScoringSignalsURLLength; sequence interestGroupBuyers; Promise auctionSignals; Promise sellerSignals; Promise directFromSellerSignalsHeaderAdSlot; + Promise> deprecatedRenderURLReplacements; unsigned long long sellerTimeout; unsigned short sellerExperimentGroupId; - USVString sellerCurrency; Promise> perBuyerSignals; Promise> perBuyerTimeouts; Promise> perBuyerCumulativeTimeouts; + unsigned long long reportingTimeout; + USVString sellerCurrency; + Promise> perBuyerCurrencies; record perBuyerGroupLimits; record perBuyerExperimentGroupIds; record> perBuyerPrioritySignals; - Promise> perBuyerCurrencies; - sequence componentAuctions = []; + sequence requiredSellerCapabilities; + record requestedSize; + sequence> allSlotsRequestedSizes; Promise additionalBids; DOMString auctionNonce; + sequence componentAuctions = []; AbortSignal? signal; Promise resolveToConfig; }; @@ -97,11 +107,24 @@ partial interface Navigator { interface InterestGroupScriptRunnerGlobalScope { }; +[Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope] +interface ForDebuggingOnly { + undefined reportAdAuctionWin(USVString url); + undefined reportAdAuctionLoss(USVString url); +}; + +[Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope, + Global=InterestGroupBiddingAndScoringScriptRunnerGlobalScope] +interface InterestGroupBiddingAndScoringScriptRunnerGlobalScope : InterestGroupScriptRunnerGlobalScope { + + readonly attribute ForDebuggingOnly forDebuggingOnly; +}; + [Exposed=InterestGroupBiddingScriptRunnerGlobalScope, Global=(InterestGroupScriptRunnerGlobalScope, InterestGroupBiddingScriptRunnerGlobalScope)] interface InterestGroupBiddingScriptRunnerGlobalScope - : InterestGroupScriptRunnerGlobalScope { + : InterestGroupBiddingAndScoringScriptRunnerGlobalScope { boolean setBid(optional GenerateBidOutput generateBidOutput = {}); undefined setPriority(double priority); undefined setPrioritySignalsOverride(DOMString key, optional double? priority); @@ -128,7 +151,7 @@ dictionary GenerateBidOutput { Global=(InterestGroupScriptRunnerGlobalScope, InterestGroupScoringScriptRunnerGlobalScope)] interface InterestGroupScoringScriptRunnerGlobalScope - : InterestGroupScriptRunnerGlobalScope { + : InterestGroupBiddingAndScoringScriptRunnerGlobalScope { }; [Exposed=InterestGroupReportingScriptRunnerGlobalScope, @@ -146,10 +169,24 @@ partial interface Navigator { undefined updateAdInterestGroups(); }; +[SecureContext] +partial interface Navigator { + [SameObject] readonly attribute ProtectedAudience protectedAudience; +}; + +[SecureContext, Exposed=Window] +interface ProtectedAudience { + any queryFeatureSupport(DOMString feature); +}; + partial dictionary RequestInit { boolean adAuctionHeaders; }; +partial interface HTMLIFrameElement { + [CEReactions] attribute boolean adAuctionHeaders; +}; + dictionary PreviousWin { required long long timeDelta; required DOMString adJSON; @@ -161,11 +198,13 @@ dictionary BiddingBrowserSignals { required long joinCount; required long bidCount; required long recency; + required long adComponentsLimit; USVString topLevelSeller; sequence prevWinsMs; object wasmHelper; unsigned long dataVersion; + boolean forDebuggingOnlyInCooldownOrLockout = false; }; dictionary ScoringBrowserSignals { @@ -177,6 +216,7 @@ dictionary ScoringBrowserSignals { unsigned long dataVersion; sequence adComponents; + boolean forDebuggingOnlyInCooldownOrLockout = false; }; dictionary ReportingBrowserSignals { diff --git a/test/wpt/tests/interfaces/ua-client-hints.idl b/test/wpt/tests/interfaces/ua-client-hints.idl index 6a40e1bdc4b..5d44b0dd80a 100644 --- a/test/wpt/tests/interfaces/ua-client-hints.idl +++ b/test/wpt/tests/interfaces/ua-client-hints.idl @@ -12,7 +12,7 @@ dictionary UADataValues { DOMString architecture; DOMString bitness; sequence brands; - sequence formFactor; + sequence formFactors; sequence fullVersionList; DOMString model; boolean mobile; diff --git a/test/wpt/tests/interfaces/uievents.idl b/test/wpt/tests/interfaces/uievents.idl index fef90d48410..0f9d3d3c188 100644 --- a/test/wpt/tests/interfaces/uievents.idl +++ b/test/wpt/tests/interfaces/uievents.idl @@ -101,7 +101,7 @@ dictionary WheelEventInit : MouseEventInit { [Exposed=Window] interface InputEvent : UIEvent { constructor(DOMString type, optional InputEventInit eventInitDict = {}); - readonly attribute DOMString? data; + readonly attribute USVString? data; readonly attribute boolean isComposing; readonly attribute DOMString inputType; }; @@ -147,7 +147,7 @@ dictionary KeyboardEventInit : EventModifierInit { [Exposed=Window] interface CompositionEvent : UIEvent { constructor(DOMString type, optional CompositionEventInit eventInitDict = {}); - readonly attribute DOMString data; + readonly attribute USVString data; }; dictionary CompositionEventInit : UIEventInit { @@ -226,6 +226,16 @@ partial dictionary KeyboardEventInit { unsigned long keyCode = 0; }; +[Exposed=Window] +interface TextEvent : UIEvent { + readonly attribute DOMString data; + undefined initTextEvent(DOMString type, + optional boolean bubbles = false, + optional boolean cancelable = false, + optional Window? view = null, + optional DOMString data = "undefined"); +}; + [Exposed=Window] interface MutationEvent : Event { // attrChangeType diff --git a/test/wpt/tests/interfaces/url.idl b/test/wpt/tests/interfaces/url.idl index a5e4d1eb492..cd18a66e31b 100644 --- a/test/wpt/tests/interfaces/url.idl +++ b/test/wpt/tests/interfaces/url.idl @@ -8,6 +8,7 @@ interface URL { constructor(USVString url, optional USVString base); + static URL? parse(USVString url, optional USVString base); static boolean canParse(USVString url, optional USVString base); stringifier attribute USVString href; diff --git a/test/wpt/tests/interfaces/urlpattern.idl b/test/wpt/tests/interfaces/urlpattern.idl index 788486bb627..ca9fb979d22 100644 --- a/test/wpt/tests/interfaces/urlpattern.idl +++ b/test/wpt/tests/interfaces/urlpattern.idl @@ -22,6 +22,8 @@ interface URLPattern { readonly attribute USVString pathname; readonly attribute USVString search; readonly attribute USVString hash; + + readonly attribute boolean hasRegExpGroups; }; dictionary URLPatternInit { @@ -57,3 +59,5 @@ dictionary URLPatternComponentResult { USVString input; record groups; }; + +typedef (USVString or URLPatternInit or URLPattern) URLPatternCompatible; diff --git a/test/wpt/tests/interfaces/wai-aria.idl b/test/wpt/tests/interfaces/wai-aria.idl index 3434bf7c2d3..78083f03f91 100644 --- a/test/wpt/tests/interfaces/wai-aria.idl +++ b/test/wpt/tests/interfaces/wai-aria.idl @@ -8,6 +8,8 @@ interface mixin ARIAMixin { [CEReactions] attribute Element? ariaActiveDescendantElement; [CEReactions] attribute DOMString? ariaAtomic; [CEReactions] attribute DOMString? ariaAutoComplete; + [CEReactions] attribute DOMString? ariaBrailleLabel; + [CEReactions] attribute DOMString? ariaBrailleRoleDescription; [CEReactions] attribute DOMString? ariaBusy; [CEReactions] attribute DOMString? ariaChecked; [CEReactions] attribute DOMString? ariaColCount; @@ -55,5 +57,4 @@ interface mixin ARIAMixin { [CEReactions] attribute DOMString? ariaValueNow; [CEReactions] attribute DOMString? ariaValueText; }; - Element includes ARIAMixin; diff --git a/test/wpt/tests/interfaces/wasm-js-api.idl b/test/wpt/tests/interfaces/wasm-js-api.idl index 0d4384251df..b4f723d050a 100644 --- a/test/wpt/tests/interfaces/wasm-js-api.idl +++ b/test/wpt/tests/interfaces/wasm-js-api.idl @@ -62,6 +62,8 @@ dictionary MemoryDescriptor { interface Memory { constructor(MemoryDescriptor descriptor); unsigned long grow([EnforceRange] unsigned long delta); + ArrayBuffer toFixedLengthBuffer(); + ArrayBuffer toResizableBuffer(); readonly attribute ArrayBuffer buffer; }; diff --git a/test/wpt/tests/interfaces/web-animations-2.idl b/test/wpt/tests/interfaces/web-animations-2.idl index f9f68a0d49a..4c3af535149 100644 --- a/test/wpt/tests/interfaces/web-animations-2.idl +++ b/test/wpt/tests/interfaces/web-animations-2.idl @@ -14,6 +14,7 @@ partial interface AnimationTimeline { partial interface Animation { attribute CSSNumberish? startTime; attribute CSSNumberish? currentTime; + readonly attribute double? progress; }; [Exposed=Window] diff --git a/test/wpt/tests/interfaces/webauthn.idl b/test/wpt/tests/interfaces/webauthn.idl index e28355eb78d..cf1a2fbdc48 100644 --- a/test/wpt/tests/interfaces/webauthn.idl +++ b/test/wpt/tests/interfaces/webauthn.idl @@ -32,7 +32,7 @@ dictionary AuthenticatorAttestationResponseJSON { required Base64URLString authenticatorData; required sequence transports; // The publicKey field will be missing if pubKeyCredParams was used to - // negotiate a public-key algorithm that the user agent doesn’t + // negotiate a public-key algorithm that the user agent doesn't // understand. (See section “Easily accessing credential data” for a // list of which algorithms user agents must support.) If using such an // algorithm then the public key must be parsed directly from @@ -58,7 +58,6 @@ dictionary AuthenticatorAssertionResponseJSON { required Base64URLString authenticatorData; required Base64URLString signature; Base64URLString userHandle; - Base64URLString attestationObject; }; dictionary AuthenticationExtensionsClientOutputsJSON { @@ -77,9 +76,11 @@ partial interface PublicKeyCredential { }; partial interface PublicKeyCredential { - static Promise isPasskeyPlatformAuthenticatorAvailable(); + static Promise getClientCapabilities(); }; +typedef record PublicKeyCredentialClientCapabilities; + partial interface PublicKeyCredential { static PublicKeyCredentialCreationOptions parseCreationOptionsFromJSON(PublicKeyCredentialCreationOptionsJSON options); }; @@ -124,8 +125,6 @@ dictionary PublicKeyCredentialRequestOptionsJSON { sequence allowCredentials = []; DOMString userVerification = "preferred"; sequence hints = []; - DOMString attestation = "none"; - sequence attestationFormats = []; AuthenticationExtensionsClientInputsJSON extensions; }; @@ -148,7 +147,6 @@ interface AuthenticatorAssertionResponse : AuthenticatorResponse { [SameObject] readonly attribute ArrayBuffer authenticatorData; [SameObject] readonly attribute ArrayBuffer signature; [SameObject] readonly attribute ArrayBuffer? userHandle; - [SameObject] readonly attribute ArrayBuffer? attestationObject; }; dictionary PublicKeyCredentialParameters { @@ -217,8 +215,6 @@ dictionary PublicKeyCredentialRequestOptions { sequence allowCredentials = []; DOMString userVerification = "preferred"; sequence hints = []; - DOMString attestation = "none"; - sequence attestationFormats = []; AuthenticationExtensionsClientInputs extensions; }; @@ -270,6 +266,14 @@ enum UserVerificationRequirement { "discouraged" }; +enum ClientCapability { + "conditionalCreate", + "conditionalMediation", + "hybridTransport", + "passkeyPlatformAuthenticator", + "userVerifyingPlatformAuthenticator", +}; + enum PublicKeyCredentialHints { "security-key", "client-device", @@ -375,7 +379,7 @@ partial dictionary AuthenticationExtensionsClientInputs { }; dictionary AuthenticationExtensionsSupplementalPubKeysOutputs { - sequence signatures; + required sequence signatures; }; partial dictionary AuthenticationExtensionsClientOutputs { diff --git a/test/wpt/tests/interfaces/webcodecs-opus-codec-registration.idl b/test/wpt/tests/interfaces/webcodecs-opus-codec-registration.idl index 0d198a6bcde..782a87b37d6 100644 --- a/test/wpt/tests/interfaces/webcodecs-opus-codec-registration.idl +++ b/test/wpt/tests/interfaces/webcodecs-opus-codec-registration.idl @@ -9,6 +9,8 @@ partial dictionary AudioEncoderConfig { dictionary OpusEncoderConfig { OpusBitstreamFormat format = "opus"; + OpusSignal signal = "auto"; + OpusApplication application = "audio"; [EnforceRange] unsigned long long frameDuration = 20000; [EnforceRange] unsigned long complexity; [EnforceRange] unsigned long packetlossperc = 0; @@ -20,3 +22,15 @@ enum OpusBitstreamFormat { "opus", "ogg", }; + +enum OpusSignal { + "auto", + "music", + "voice", +}; + +enum OpusApplication { + "voip", + "audio", + "lowdelay", +}; diff --git a/test/wpt/tests/interfaces/webcodecs.idl b/test/wpt/tests/interfaces/webcodecs.idl index 48d89d0b477..c754b2b036c 100644 --- a/test/wpt/tests/interfaces/webcodecs.idl +++ b/test/wpt/tests/interfaces/webcodecs.idl @@ -158,8 +158,8 @@ dictionary VideoDecoderConfig { dictionary AudioEncoderConfig { required DOMString codec; - [EnforceRange] unsigned long sampleRate; - [EnforceRange] unsigned long numberOfChannels; + [EnforceRange] required unsigned long sampleRate; + [EnforceRange] required unsigned long numberOfChannels; [EnforceRange] unsigned long long bitrate; BitrateMode bitrateMode = "variable"; }; @@ -371,6 +371,8 @@ dictionary VideoFrameBufferInit { VideoColorSpaceInit colorSpace; sequence transfer = []; + + VideoFrameMetadata metadata; }; dictionary VideoFrameMetadata { @@ -380,6 +382,8 @@ dictionary VideoFrameMetadata { dictionary VideoFrameCopyToOptions { DOMRectInit rect; sequence layout; + VideoPixelFormat format; + PredefinedColorSpace colorSpace; }; dictionary PlaneLayout { @@ -390,21 +394,37 @@ dictionary PlaneLayout { enum VideoPixelFormat { // 4:2:0 Y, U, V "I420", + "I420P10", + "I420P12", // 4:2:0 Y, U, V, A "I420A", + "I420AP10", + "I420AP12", // 4:2:2 Y, U, V "I422", + "I422P10", + "I422P12", + // 4:2:2 Y, U, V, A + "I422A", + "I422AP10", + "I422AP12", // 4:4:4 Y, U, V "I444", + "I444P10", + "I444P12", + // 4:4:4 Y, U, V, A + "I444A", + "I444AP10", + "I444AP12", // 4:2:0 Y, UV "NV12", - // 32bpp RGBA + // 4:4:4 RGBA "RGBA", - // 32bpp RGBX (opaque) + // 4:4:4 RGBX (opaque) "RGBX", - // 32bpp BGRA + // 4:4:4 BGRA "BGRA", - // 32bpp BGRX (opaque) + // 4:4:4 BGRX (opaque) "BGRX", }; diff --git a/test/wpt/tests/interfaces/webgl1.idl b/test/wpt/tests/interfaces/webgl1.idl index b61f031ab28..655c294fc1e 100644 --- a/test/wpt/tests/interfaces/webgl1.idl +++ b/test/wpt/tests/interfaces/webgl1.idl @@ -37,6 +37,7 @@ dictionary WebGLContextAttributes { [Exposed=(Window,Worker)] interface WebGLObject { + attribute USVString label; }; [Exposed=(Window,Worker)] @@ -467,6 +468,7 @@ interface mixin WebGLRenderingContextBase const GLenum RGBA4 = 0x8056; const GLenum RGB5_A1 = 0x8057; + const GLenum RGBA8 = 0x8058; const GLenum RGB565 = 0x8D62; const GLenum DEPTH_COMPONENT16 = 0x81A5; const GLenum STENCIL_INDEX8 = 0x8D48; @@ -516,6 +518,10 @@ interface mixin WebGLRenderingContextBase readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; readonly attribute GLsizei drawingBufferWidth; readonly attribute GLsizei drawingBufferHeight; + readonly attribute GLenum drawingBufferFormat; + + /* Upon context creation, drawingBufferColorSpace and unpackColorSpace both + default to the value "srgb". */ attribute PredefinedColorSpace drawingBufferColorSpace; attribute PredefinedColorSpace unpackColorSpace; @@ -525,6 +531,8 @@ interface mixin WebGLRenderingContextBase sequence? getSupportedExtensions(); object? getExtension(DOMString name); + undefined drawingBufferStorage(GLenum sizedFormat, unsigned long width, unsigned long height); + undefined activeTexture(GLenum texture); undefined attachShader(WebGLProgram program, WebGLShader shader); undefined bindAttribLocation(WebGLProgram program, GLuint index, DOMString name); diff --git a/test/wpt/tests/interfaces/webgl2.idl b/test/wpt/tests/interfaces/webgl2.idl index 9cb639932e8..25c2b4dad28 100644 --- a/test/wpt/tests/interfaces/webgl2.idl +++ b/test/wpt/tests/interfaces/webgl2.idl @@ -42,7 +42,6 @@ interface mixin WebGL2RenderingContextBase const GLenum STENCIL = 0x1802; const GLenum RED = 0x1903; const GLenum RGB8 = 0x8051; - const GLenum RGBA8 = 0x8058; const GLenum RGB10_A2 = 0x8059; const GLenum TEXTURE_BINDING_3D = 0x806A; const GLenum UNPACK_SKIP_IMAGES = 0x806D; diff --git a/test/wpt/tests/interfaces/webgpu.idl b/test/wpt/tests/interfaces/webgpu.idl index 373d51dff13..ef5b9c730ab 100644 --- a/test/wpt/tests/interfaces/webgpu.idl +++ b/test/wpt/tests/interfaces/webgpu.idl @@ -11,7 +11,7 @@ dictionary GPUObjectDescriptorBase { USVString label = ""; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSupportedLimits { readonly attribute unsigned long maxTextureDimension1D; readonly attribute unsigned long maxTextureDimension2D; @@ -47,17 +47,17 @@ interface GPUSupportedLimits { readonly attribute unsigned long maxComputeWorkgroupsPerDimension; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSupportedFeatures { readonly setlike; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface WGSLLanguageFeatures { readonly setlike; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUAdapterInfo { readonly attribute DOMString vendor; readonly attribute DOMString architecture; @@ -71,7 +71,7 @@ interface mixin NavigatorGPU { Navigator includes NavigatorGPU; WorkerNavigator includes NavigatorGPU; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPU { Promise requestAdapter(optional GPURequestAdapterOptions options = {}); GPUTextureFormat getPreferredCanvasFormat(); @@ -88,7 +88,7 @@ enum GPUPowerPreference { "high-performance", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUAdapter { [SameObject] readonly attribute GPUSupportedFeatures features; [SameObject] readonly attribute GPUSupportedLimits limits; @@ -119,7 +119,7 @@ enum GPUFeatureName { "float32-filterable", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUDevice : EventTarget { [SameObject] readonly attribute GPUSupportedFeatures features; [SameObject] readonly attribute GPUSupportedLimits limits; @@ -150,7 +150,7 @@ interface GPUDevice : EventTarget { }; GPUDevice includes GPUObjectBase; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBuffer { readonly attribute GPUSize64Out size; readonly attribute GPUFlagsConstant usage; @@ -179,7 +179,7 @@ dictionary GPUBufferDescriptor }; typedef [EnforceRange] unsigned long GPUBufferUsageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUBufferUsage { const GPUFlagsConstant MAP_READ = 0x0001; const GPUFlagsConstant MAP_WRITE = 0x0002; @@ -194,13 +194,13 @@ namespace GPUBufferUsage { }; typedef [EnforceRange] unsigned long GPUMapModeFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUMapMode { const GPUFlagsConstant READ = 0x0001; const GPUFlagsConstant WRITE = 0x0002; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUTexture { GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {}); @@ -235,7 +235,7 @@ enum GPUTextureDimension { }; typedef [EnforceRange] unsigned long GPUTextureUsageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUTextureUsage { const GPUFlagsConstant COPY_SRC = 0x01; const GPUFlagsConstant COPY_DST = 0x02; @@ -244,7 +244,7 @@ namespace GPUTextureUsage { const GPUFlagsConstant RENDER_ATTACHMENT = 0x10; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUTextureView { }; GPUTextureView includes GPUObjectBase; @@ -396,7 +396,7 @@ enum GPUTextureFormat { "astc-12x12-unorm-srgb", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUExternalTexture { }; GPUExternalTexture includes GPUObjectBase; @@ -407,7 +407,7 @@ dictionary GPUExternalTextureDescriptor PredefinedColorSpace colorSpace = "srgb"; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUSampler { }; GPUSampler includes GPUObjectBase; @@ -453,7 +453,7 @@ enum GPUCompareFunction { "always", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBindGroupLayout { }; GPUBindGroupLayout includes GPUObjectBase; @@ -475,7 +475,7 @@ dictionary GPUBindGroupLayoutEntry { }; typedef [EnforceRange] unsigned long GPUShaderStageFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUShaderStage { const GPUFlagsConstant VERTEX = 0x1; const GPUFlagsConstant FRAGMENT = 0x2; @@ -533,7 +533,7 @@ dictionary GPUStorageTextureBindingLayout { dictionary GPUExternalTextureBindingLayout { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUBindGroup { }; GPUBindGroup includes GPUObjectBase; @@ -557,7 +557,7 @@ dictionary GPUBufferBinding { GPUSize64 size; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUPipelineLayout { }; GPUPipelineLayout includes GPUObjectBase; @@ -567,7 +567,7 @@ dictionary GPUPipelineLayoutDescriptor required sequence bindGroupLayouts; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUShaderModule { Promise getCompilationInfo(); }; @@ -591,7 +591,7 @@ enum GPUCompilationMessageType { "info", }; -[Exposed=(Window, DedicatedWorker), Serializable, SecureContext] +[Exposed=(Window, Worker), Serializable, SecureContext] interface GPUCompilationMessage { readonly attribute DOMString message; readonly attribute GPUCompilationMessageType type; @@ -601,12 +601,12 @@ interface GPUCompilationMessage { readonly attribute unsigned long long length; }; -[Exposed=(Window, DedicatedWorker), Serializable, SecureContext] +[Exposed=(Window, Worker), Serializable, SecureContext] interface GPUCompilationInfo { readonly attribute FrozenArray messages; }; -[Exposed=(Window, DedicatedWorker), SecureContext, Serializable] +[Exposed=(Window, Worker), SecureContext, Serializable] interface GPUPipelineError : DOMException { constructor(optional DOMString message = "", GPUPipelineErrorInit options); readonly attribute GPUPipelineErrorReason reason; @@ -640,9 +640,9 @@ dictionary GPUProgrammableStage { record constants; }; -typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32, and f16 if enabled. +typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, u32, and f16 if enabled. -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUComputePipeline { }; GPUComputePipeline includes GPUObjectBase; @@ -653,7 +653,7 @@ dictionary GPUComputePipelineDescriptor required GPUProgrammableStage compute; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderPipeline { }; GPURenderPipeline includes GPUObjectBase; @@ -721,7 +721,7 @@ dictionary GPUBlendState { }; typedef [EnforceRange] unsigned long GPUColorWriteFlags; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] namespace GPUColorWrite { const GPUFlagsConstant RED = 0x1; const GPUFlagsConstant GREEN = 0x2; @@ -895,7 +895,7 @@ dictionary GPUImageCopyExternalImage { boolean flipY = false; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCommandBuffer { }; GPUCommandBuffer includes GPUObjectBase; @@ -907,7 +907,7 @@ dictionary GPUCommandBufferDescriptor interface mixin GPUCommandsMixin { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCommandEncoder { GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor); GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {}); @@ -972,7 +972,7 @@ interface mixin GPUDebugCommandsMixin { undefined insertDebugMarker(USVString markerLabel); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUComputePassEncoder { undefined setPipeline(GPUComputePipeline pipeline); undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1); @@ -996,7 +996,7 @@ dictionary GPUComputePassDescriptor GPUComputePassTimestampWrites timestampWrites; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderPassEncoder { undefined setViewport(float x, float y, float width, float height, @@ -1093,7 +1093,7 @@ interface mixin GPURenderCommandsMixin { undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderBundle { }; GPURenderBundle includes GPUObjectBase; @@ -1102,7 +1102,7 @@ dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPURenderBundleEncoder { GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {}); }; @@ -1122,7 +1122,7 @@ dictionary GPUQueueDescriptor : GPUObjectDescriptorBase { }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUQueue { undefined submit(sequence commandBuffers); @@ -1148,7 +1148,7 @@ interface GPUQueue { }; GPUQueue includes GPUObjectBase; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUQuerySet { undefined destroy(); @@ -1168,7 +1168,7 @@ enum GPUQueryType { "timestamp", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUCanvasContext { readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; @@ -1197,7 +1197,7 @@ enum GPUDeviceLostReason { "destroyed", }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUDeviceLostInfo { readonly attribute GPUDeviceLostReason reason; readonly attribute DOMString message; @@ -1207,24 +1207,24 @@ partial interface GPUDevice { readonly attribute Promise lost; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUError { readonly attribute DOMString message; }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUValidationError : GPUError { constructor(DOMString message); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUOutOfMemoryError : GPUError { constructor(DOMString message); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUInternalError : GPUError { constructor(DOMString message); @@ -1241,7 +1241,7 @@ partial interface GPUDevice { Promise popErrorScope(); }; -[Exposed=(Window, DedicatedWorker), SecureContext] +[Exposed=(Window, Worker), SecureContext] interface GPUUncapturedErrorEvent : Event { constructor( DOMString type, @@ -1255,7 +1255,7 @@ dictionary GPUUncapturedErrorEventInit : EventInit { }; partial interface GPUDevice { - [Exposed=(Window, DedicatedWorker)] + [Exposed=(Window, Worker)] attribute EventHandler onuncapturederror; }; diff --git a/test/wpt/tests/interfaces/webidl.idl b/test/wpt/tests/interfaces/webidl.idl index 4d0dfaa1062..f3db91096ac 100644 --- a/test/wpt/tests/interfaces/webidl.idl +++ b/test/wpt/tests/interfaces/webidl.idl @@ -6,13 +6,13 @@ typedef (Int8Array or Int16Array or Int32Array or Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or BigInt64Array or BigUint64Array or - Float32Array or Float64Array or DataView) ArrayBufferView; + Float16Array or Float32Array or Float64Array or DataView) ArrayBufferView; typedef (ArrayBufferView or ArrayBuffer) BufferSource; typedef (ArrayBuffer or SharedArrayBuffer or [AllowShared] ArrayBufferView) AllowSharedBufferSource; [Exposed=*, Serializable] -interface DOMException { // but see below note about ECMAScript binding +interface DOMException { // but see below note about JavaScript binding constructor(optional DOMString message = "", optional DOMString name = "Error"); readonly attribute DOMString name; readonly attribute DOMString message; diff --git a/test/wpt/tests/interfaces/webnn.idl b/test/wpt/tests/interfaces/webnn.idl index 2a9d0e639fb..9af2879214e 100644 --- a/test/wpt/tests/interfaces/webnn.idl +++ b/test/wpt/tests/interfaces/webnn.idl @@ -29,11 +29,19 @@ dictionary MLContextOptions { interface ML { Promise createContext(optional MLContextOptions options = {}); Promise createContext(GPUDevice gpuDevice); +}; + +typedef record MLNamedArrayBufferViews; + +dictionary MLComputeResult { + MLNamedArrayBufferViews inputs; + MLNamedArrayBufferViews outputs; +}; - [Exposed=(DedicatedWorker)] - MLContext createContextSync(optional MLContextOptions options = {}); - [Exposed=(DedicatedWorker)] - MLContext createContextSync(GPUDevice gpuDevice); +[SecureContext, Exposed=(Window, DedicatedWorker)] +interface MLContext { + Promise compute( + MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs); }; [SecureContext, Exposed=(Window, DedicatedWorker)] @@ -49,79 +57,28 @@ enum MLOperandDataType { "float16", "int32", "uint32", + "int64", + "uint64", "int8", "uint8" }; dictionary MLOperandDescriptor { - // The operand data type. required MLOperandDataType dataType; - - // The dimensions field is only required for tensor operands. - sequence dimensions; + sequence<[EnforceRange] unsigned long> dimensions = []; }; [SecureContext, Exposed=(Window, DedicatedWorker)] -interface MLOperand {}; - -[SecureContext, Exposed=(Window, DedicatedWorker)] -interface MLActivation { +interface MLOperand { + MLOperandDataType dataType(); + sequence shape(); }; -typedef record MLNamedArrayBufferViews; - [SecureContext, Exposed=(Window, DedicatedWorker)] -interface MLContext {}; - -partial interface MLContext { - [Exposed=(DedicatedWorker)] - undefined computeSync( - MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs); -}; - -dictionary MLComputeResult { - MLNamedArrayBufferViews inputs; - MLNamedArrayBufferViews outputs; -}; - -partial interface MLContext { - Promise compute( - MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs); -}; - -partial interface MLContext { - MLCommandEncoder createCommandEncoder(); -}; - -typedef (GPUBuffer or GPUTexture) MLGPUResource; - -typedef record MLNamedGPUResources; - -[SecureContext, Exposed=(Window, DedicatedWorker)] -interface MLCommandEncoder {}; - -partial interface MLCommandEncoder { - undefined initializeGraph(MLGraph graph); -}; - -partial interface MLCommandEncoder { - undefined dispatch(MLGraph graph, MLNamedGPUResources inputs, MLNamedGPUResources outputs); -}; - -partial interface MLCommandEncoder { - GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {}); -}; +interface MLActivation {}; typedef record MLNamedOperands; -dictionary MLBufferResourceView { - required GPUBuffer resource; - unsigned long long offset = 0; - unsigned long long size; -}; - -typedef (ArrayBufferView or MLBufferResourceView) MLBufferView; - [SecureContext, Exposed=(Window, DedicatedWorker)] interface MLGraphBuilder { // Construct the graph builder from the context. @@ -131,30 +88,41 @@ interface MLGraphBuilder { MLOperand input(DOMString name, MLOperandDescriptor descriptor); // Create an operand for a graph constant. - MLOperand constant(MLOperandDescriptor descriptor, MLBufferView bufferView); + MLOperand constant(MLOperandDescriptor descriptor, ArrayBufferView bufferView); // Create a single-value operand from the specified number of the specified type. - MLOperand constant(double value, optional MLOperandDataType dataType = "float32"); + MLOperand constant(double value, optional MLOperandDataType type = "float32"); // Compile the graph up to the specified output operands asynchronously. Promise build(MLNamedOperands outputs); +}; + +dictionary MLArgMinMaxOptions { + sequence<[EnforceRange] unsigned long> axes; + boolean keepDimensions = false; + boolean selectLastIndex = false; +}; - // Compile the graph up to the specified output operands synchronously. - [Exposed=(DedicatedWorker)] - MLGraph buildSync(MLNamedOperands outputs); +partial interface MLGraphBuilder { + MLOperand argMin(MLOperand input, optional MLArgMinMaxOptions options = {}); + MLOperand argMax(MLOperand input, optional MLArgMinMaxOptions options = {}); }; dictionary MLBatchNormalizationOptions { MLOperand scale; MLOperand bias; - unsigned long axis = 1; + [EnforceRange] unsigned long axis = 1; float epsilon = 1e-5; MLActivation activation; }; partial interface MLGraphBuilder { MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance, - optional MLBatchNormalizationOptions options = {}); + optional MLBatchNormalizationOptions options = {}); +}; + +partial interface MLGraphBuilder { + MLOperand cast(MLOperand input, MLOperandDataType type); }; dictionary MLClampOptions { @@ -163,12 +131,12 @@ dictionary MLClampOptions { }; partial interface MLGraphBuilder { - MLOperand clamp(MLOperand operand, optional MLClampOptions options = {}); + MLOperand clamp(MLOperand input, optional MLClampOptions options = {}); MLActivation clamp(optional MLClampOptions options = {}); }; partial interface MLGraphBuilder { - MLOperand concat(sequence inputs, unsigned long axis); + MLOperand concat(sequence inputs, [EnforceRange] unsigned long axis); }; enum MLConv2dFilterOperandLayout { @@ -178,18 +146,11 @@ enum MLConv2dFilterOperandLayout { "ihwo" }; -enum MLAutoPad { - "explicit", - "same-upper", - "same-lower" -}; - dictionary MLConv2dOptions { - sequence padding; - sequence strides; - sequence dilations; - MLAutoPad autoPad = "explicit"; - unsigned long groups = 1; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; + [EnforceRange] unsigned long groups = 1; MLInputOperandLayout inputLayout = "nchw"; MLConv2dFilterOperandLayout filterLayout = "oihw"; MLOperand bias; @@ -197,7 +158,9 @@ dictionary MLConv2dOptions { }; partial interface MLGraphBuilder { - MLOperand conv2d(MLOperand input, MLOperand filter, optional MLConv2dOptions options = {}); + MLOperand conv2d(MLOperand input, + MLOperand filter, + optional MLConv2dOptions options = {}); }; enum MLConvTranspose2dFilterOperandLayout { @@ -207,13 +170,12 @@ enum MLConvTranspose2dFilterOperandLayout { }; dictionary MLConvTranspose2dOptions { - sequence padding; - sequence strides; - sequence dilations; - sequence outputPadding; - sequence outputSizes; - MLAutoPad autoPad = "explicit"; - unsigned long groups = 1; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; + sequence<[EnforceRange] unsigned long> outputPadding; + sequence<[EnforceRange] unsigned long> outputSizes; + [EnforceRange] unsigned long groups = 1; MLInputOperandLayout inputLayout = "nchw"; MLConvTranspose2dFilterOperandLayout filterLayout = "iohw"; MLOperand bias; @@ -235,15 +197,28 @@ partial interface MLGraphBuilder { MLOperand pow(MLOperand a, MLOperand b); }; +partial interface MLGraphBuilder { + MLOperand equal(MLOperand a, MLOperand b); + MLOperand greater(MLOperand a, MLOperand b); + MLOperand greaterOrEqual(MLOperand a, MLOperand b); + MLOperand lesser(MLOperand a, MLOperand b); + MLOperand lesserOrEqual(MLOperand a, MLOperand b); + MLOperand not(MLOperand a); +}; + partial interface MLGraphBuilder { MLOperand abs(MLOperand input); MLOperand ceil(MLOperand input); MLOperand cos(MLOperand input); + MLOperand erf(MLOperand input); MLOperand exp(MLOperand input); MLOperand floor(MLOperand input); + MLOperand identity(MLOperand input); MLOperand log(MLOperand input); MLOperand neg(MLOperand input); + MLOperand reciprocal(MLOperand input); MLOperand sin(MLOperand input); + MLOperand sqrt(MLOperand input); MLOperand tan(MLOperand input); }; @@ -256,6 +231,25 @@ partial interface MLGraphBuilder { MLActivation elu(optional MLEluOptions options = {}); }; +partial interface MLGraphBuilder { + MLOperand expand(MLOperand input, sequence<[EnforceRange] unsigned long> newShape); +}; + +dictionary MLGatherOptions { + [EnforceRange] unsigned long axis = 0; +}; + +partial interface MLGraphBuilder { + MLOperand gather(MLOperand input, + MLOperand indices, + optional MLGatherOptions options = {}); +}; + +partial interface MLGraphBuilder { + MLOperand gelu(MLOperand input); + MLActivation gelu(); +}; + dictionary MLGemmOptions { MLOperand c; float alpha = 1.0; @@ -291,8 +285,11 @@ dictionary MLGruOptions { }; partial interface MLGraphBuilder { - sequence gru(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - unsigned long steps, unsigned long hiddenSize, + sequence gru(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + [EnforceRange] unsigned long steps, + [EnforceRange] unsigned long hiddenSize, optional MLGruOptions options = {}); }; @@ -305,8 +302,11 @@ dictionary MLGruCellOptions { }; partial interface MLGraphBuilder { - MLOperand gruCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - MLOperand hiddenState, unsigned long hiddenSize, + MLOperand gruCell(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + MLOperand hiddenState, + [EnforceRange] unsigned long hiddenSize, optional MLGruCellOptions options = {}); }; @@ -334,7 +334,19 @@ dictionary MLInstanceNormalizationOptions { partial interface MLGraphBuilder { MLOperand instanceNormalization(MLOperand input, - optional MLInstanceNormalizationOptions options = {}); + optional MLInstanceNormalizationOptions options = {}); +}; + +dictionary MLLayerNormalizationOptions { + MLOperand scale; + MLOperand bias; + sequence<[EnforceRange] unsigned long> axes; + float epsilon = 1e-5; +}; + +partial interface MLGraphBuilder { + MLOperand layerNormalization(MLOperand input, + optional MLLayerNormalizationOptions options = {}); }; dictionary MLLeakyReluOptions { @@ -374,8 +386,11 @@ dictionary MLLstmOptions { }; partial interface MLGraphBuilder { - sequence lstm(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - unsigned long steps, unsigned long hiddenSize, + sequence lstm(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + [EnforceRange] unsigned long steps, + [EnforceRange] unsigned long hiddenSize, optional MLLstmOptions options = {}); }; @@ -388,8 +403,12 @@ dictionary MLLstmCellOptions { }; partial interface MLGraphBuilder { - sequence lstmCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - MLOperand hiddenState, MLOperand cellState, unsigned long hiddenSize, + sequence lstmCell(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + MLOperand hiddenState, + MLOperand cellState, + [EnforceRange] unsigned long hiddenSize, optional MLLstmCellOptions options = {}); }; @@ -411,8 +430,8 @@ dictionary MLPadOptions { partial interface MLGraphBuilder { MLOperand pad(MLOperand input, - sequence beginningPadding, - sequence endingPadding, + sequence<[EnforceRange] unsigned long> beginningPadding, + sequence<[EnforceRange] unsigned long> endingPadding, optional MLPadOptions options = {}); }; @@ -422,14 +441,13 @@ enum MLRoundingType { }; dictionary MLPool2dOptions { - sequence windowDimensions; - sequence padding; - sequence strides; - sequence dilations; - MLAutoPad autoPad = "explicit"; + sequence<[EnforceRange] unsigned long> windowDimensions; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; MLInputOperandLayout layout = "nchw"; MLRoundingType roundingType = "floor"; - sequence outputSizes; + sequence<[EnforceRange] unsigned long> outputSizes; }; partial interface MLGraphBuilder { @@ -443,7 +461,7 @@ partial interface MLGraphBuilder { }; dictionary MLReduceOptions { - sequence axes = null; + sequence<[EnforceRange] unsigned long> axes; boolean keepDimensions = false; }; @@ -473,8 +491,8 @@ enum MLInterpolationMode { dictionary MLResample2dOptions { MLInterpolationMode mode = "nearest-neighbor"; sequence scales; - sequence sizes; - sequence axes; + sequence<[EnforceRange] unsigned long> sizes; + sequence<[EnforceRange] unsigned long> axes; }; partial interface MLGraphBuilder { @@ -482,7 +500,7 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand reshape(MLOperand input, sequence newShape); + MLOperand reshape(MLOperand input, sequence<[EnforceRange] unsigned long> newShape); }; partial interface MLGraphBuilder { @@ -491,21 +509,19 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand slice(MLOperand input, sequence starts, sequence sizes); + MLOperand slice(MLOperand input, + sequence<[EnforceRange] unsigned long> starts, + sequence<[EnforceRange] unsigned long> sizes); }; partial interface MLGraphBuilder { - MLOperand softmax(MLOperand input); - MLActivation softmax(); -}; - -dictionary MLSoftplusOptions { - float steepness = 1; + MLOperand softmax(MLOperand input, unsigned long axis); + MLActivation softmax(unsigned long axis); }; partial interface MLGraphBuilder { - MLOperand softplus(MLOperand input, optional MLSoftplusOptions options = {}); - MLActivation softplus(optional MLSoftplusOptions options = {}); + MLOperand softplus(MLOperand input); + MLActivation softplus(); }; partial interface MLGraphBuilder { @@ -514,21 +530,14 @@ partial interface MLGraphBuilder { }; dictionary MLSplitOptions { - unsigned long axis = 0; -}; - -partial interface MLGraphBuilder { - sequence split(MLOperand input, - (unsigned long or sequence) splits, - optional MLSplitOptions options = {}); -}; - -dictionary MLSqueezeOptions { - sequence axes; + [EnforceRange] unsigned long axis = 0; }; partial interface MLGraphBuilder { - MLOperand squeeze(MLOperand input, optional MLSqueezeOptions options = {}); + sequence split( + MLOperand input, + ([EnforceRange] unsigned long or sequence<[EnforceRange] unsigned long>) splits, + optional MLSplitOptions options = {}); }; partial interface MLGraphBuilder { @@ -537,9 +546,22 @@ partial interface MLGraphBuilder { }; dictionary MLTransposeOptions { - sequence permutation; + sequence<[EnforceRange] unsigned long> permutation; }; partial interface MLGraphBuilder { MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {}); }; + +dictionary MLTriangularOptions { + boolean upper = true; + [EnforceRange] long diagonal = 0; +}; + +partial interface MLGraphBuilder { + MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {}); +}; + +partial interface MLGraphBuilder { + MLOperand where(MLOperand condition, MLOperand input, MLOperand other); +}; diff --git a/test/wpt/tests/interfaces/webrtc-encoded-transform.idl b/test/wpt/tests/interfaces/webrtc-encoded-transform.idl index 3b169b066dc..0db2f2b9a81 100644 --- a/test/wpt/tests/interfaces/webrtc-encoded-transform.idl +++ b/test/wpt/tests/interfaces/webrtc-encoded-transform.idl @@ -78,10 +78,15 @@ dictionary RTCEncodedVideoFrameMetadata { DOMString mimeType; }; +dictionary RTCEncodedVideoFrameOptions { + RTCEncodedVideoFrameMetadata metadata; +}; + // New interfaces to define encoded video and audio frames. Will eventually // re-use or extend the equivalent defined in WebCodecs. [Exposed=(Window,DedicatedWorker), Serializable] interface RTCEncodedVideoFrame { + constructor(RTCEncodedVideoFrame originalFrame, optional RTCEncodedVideoFrameOptions options = {}); readonly attribute RTCEncodedVideoFrameType type; attribute ArrayBuffer data; RTCEncodedVideoFrameMetadata getMetadata(); @@ -96,8 +101,13 @@ dictionary RTCEncodedAudioFrameMetadata { DOMString mimeType; }; +dictionary RTCEncodedAudioFrameOptions { + RTCEncodedAudioFrameMetadata metadata; +}; + [Exposed=(Window,DedicatedWorker), Serializable] interface RTCEncodedAudioFrame { + constructor(RTCEncodedAudioFrame originalFrame, optional RTCEncodedAudioFrameOptions options = {}); attribute ArrayBuffer data; RTCEncodedAudioFrameMetadata getMetadata(); }; @@ -112,12 +122,16 @@ partial interface DedicatedWorkerGlobalScope { }; [Exposed=DedicatedWorker] -interface RTCRtpScriptTransformer { +interface RTCRtpScriptTransformer : EventTarget { + // Attributes and methods related to the transformer source readonly attribute ReadableStream readable; - readonly attribute WritableStream writable; - readonly attribute any options; Promise generateKeyFrame(optional DOMString rid); Promise sendKeyFrameRequest(); + // Attributes and methods related to the transformer sink + readonly attribute WritableStream writable; + attribute EventHandler onkeyframerequest; + // Attributes for configuring the Javascript code + readonly attribute any options; }; [Exposed=Window] @@ -125,6 +139,12 @@ interface RTCRtpScriptTransform { constructor(Worker worker, optional any options, optional sequence transfer); }; +[Exposed=DedicatedWorker] +interface KeyFrameRequestEvent : Event { + constructor(DOMString type, optional DOMString rid); + readonly attribute DOMString? rid; +}; + partial interface RTCRtpSender { Promise generateKeyFrame(optional sequence rids); }; diff --git a/test/wpt/tests/interfaces/webrtc-stats.idl b/test/wpt/tests/interfaces/webrtc-stats.idl index 8e65578b6ee..0b2e956a207 100644 --- a/test/wpt/tests/interfaces/webrtc-stats.idl +++ b/test/wpt/tests/interfaces/webrtc-stats.idl @@ -171,10 +171,6 @@ dictionary RTCAudioSourceStats : RTCMediaSourceStats { double totalSamplesDuration; double echoReturnLoss; double echoReturnLossEnhancement; - double droppedSamplesDuration; - unsigned long droppedSamplesEvents; - double totalCaptureDelay; - unsigned long long totalSamplesCaptured; }; dictionary RTCVideoSourceStats : RTCMediaSourceStats { diff --git a/test/wpt/tests/interfaces/webrtc.idl b/test/wpt/tests/interfaces/webrtc.idl index e571abb527f..65e7aa622c5 100644 --- a/test/wpt/tests/interfaces/webrtc.idl +++ b/test/wpt/tests/interfaces/webrtc.idl @@ -368,6 +368,7 @@ interface RTCRtpReceiver { sequence getContributingSources(); sequence getSynchronizationSources(); Promise getStats(); + attribute DOMHighResTimeStamp? jitterBufferTarget; }; dictionary RTCRtpContributingSource { @@ -387,7 +388,7 @@ interface RTCRtpTransceiver { attribute RTCRtpTransceiverDirection direction; readonly attribute RTCRtpTransceiverDirection? currentDirection; undefined stop(); - undefined setCodecPreferences(sequence codecs); + undefined setCodecPreferences(sequence codecs); }; [Exposed=Window] @@ -434,8 +435,8 @@ dictionary RTCIceParameters { }; dictionary RTCIceCandidatePair { - RTCIceCandidate local; - RTCIceCandidate remote; + required RTCIceCandidate local; + required RTCIceCandidate remote; }; enum RTCIceGathererState { diff --git a/test/wpt/tests/interfaces/webtransport.idl b/test/wpt/tests/interfaces/webtransport.idl index 86178a4906b..e598059c93e 100644 --- a/test/wpt/tests/interfaces/webtransport.idl +++ b/test/wpt/tests/interfaces/webtransport.idl @@ -23,6 +23,9 @@ interface WebTransport { readonly attribute Promise ready; readonly attribute WebTransportReliabilityMode reliability; readonly attribute WebTransportCongestionControl congestionControl; + [EnforceRange] attribute unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams; + [EnforceRange] attribute unsigned short? anticipatedConcurrentIncomingBidirectionalStreams; + readonly attribute Promise closed; readonly attribute Promise draining; undefined close(optional WebTransportCloseInfo closeInfo = {}); @@ -39,6 +42,8 @@ interface WebTransport { /* a ReadableStream of WebTransportReceiveStream objects */ readonly attribute ReadableStream incomingUnidirectionalStreams; WebTransportSendGroup createSendGroup(); + + static readonly attribute boolean supportsReliableOnly; }; enum WebTransportReliabilityMode { @@ -57,6 +62,8 @@ dictionary WebTransportOptions { boolean requireUnreliable = false; sequence serverCertificateHashes; WebTransportCongestionControl congestionControl = "default"; + [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams = null; + [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams = null; }; enum WebTransportCongestionControl { @@ -91,8 +98,9 @@ dictionary WebTransportConnectionStats { }; dictionary WebTransportDatagramStats { - unsigned long long expiredOutgoing; unsigned long long droppedIncoming; + unsigned long long expiredIncoming; + unsigned long long expiredOutgoing; unsigned long long lostOutgoing; }; @@ -101,6 +109,7 @@ interface WebTransportSendStream : WritableStream { attribute WebTransportSendGroup? sendGroup; attribute long long sendOrder; Promise getStats(); + WebTransportWriter getWriter(); }; dictionary WebTransportSendStreamStats { @@ -130,6 +139,11 @@ interface WebTransportBidirectionalStream { readonly attribute WebTransportSendStream writable; }; +[Exposed=*, SecureContext] +interface WebTransportWriter : WritableStreamDefaultWriter { + Promise atomicWrite(optional any chunk); +}; + [Exposed=(Window,Worker), Serializable, SecureContext] interface WebTransportError : DOMException { constructor(optional DOMString message = "", optional WebTransportErrorOptions options = {}); diff --git a/test/wpt/tests/interfaces/webxr.idl b/test/wpt/tests/interfaces/webxr.idl index 3b7f8a55b7c..8e02fbd38a0 100644 --- a/test/wpt/tests/interfaces/webxr.idl +++ b/test/wpt/tests/interfaces/webxr.idl @@ -178,6 +178,7 @@ interface XRInputSource { [SameObject] readonly attribute XRSpace targetRaySpace; [SameObject] readonly attribute XRSpace? gripSpace; [SameObject] readonly attribute FrozenArray profiles; + [SameObject] readonly attribute boolean skipRendering; }; [SecureContext, Exposed=Window] @@ -263,8 +264,8 @@ interface XRInputSourcesChangeEvent : Event { dictionary XRInputSourcesChangeEventInit : EventInit { required XRSession session; - required FrozenArray added; - required FrozenArray removed; + required sequence added; + required sequence removed; }; diff --git a/test/wpt/tests/lint.ignore b/test/wpt/tests/lint.ignore index c82f8027f69..14e02983f75 100644 --- a/test/wpt/tests/lint.ignore +++ b/test/wpt/tests/lint.ignore @@ -162,6 +162,7 @@ SET TIMEOUT: custom-elements/scoped-registry/scoped-registry-define-upgrade-crit SET TIMEOUT: encrypted-media/polyfill/chrome-polyfill.js SET TIMEOUT: encrypted-media/polyfill/clearkey-polyfill.js SET TIMEOUT: encrypted-media/scripts/playback-temporary-events.js +SET TIMEOUT: fetch/fetch-later/resources/fetch-later-helper.js SET TIMEOUT: fetch/metadata/resources/helper.sub.js SET TIMEOUT: fetch/metadata/resources/message-opener.html SET TIMEOUT: fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html @@ -234,6 +235,7 @@ SET TIMEOUT: png/apng/fcTL-dispose-previous-final.html SET TIMEOUT: png/apng/fcTL-dispose-previous-first.html SET TIMEOUT: png/apng/fcTL-dispose-previous.html SET TIMEOUT: png/apng/fdAT-16bit.html +SET TIMEOUT: png/apng/fDAT-inherits-cICP.html SET TIMEOUT: png/apng/fdAT-1bit-PLTE-tRNS.html SET TIMEOUT: png/apng/fdAT-1bit-PLTE.html SET TIMEOUT: png/apng/fdAT-2bit-PLTE-tRNS.html @@ -275,7 +277,6 @@ SET TIMEOUT: shadow-dom/scroll-to-the-fragment-in-shadow-tree.html SET TIMEOUT: shadow-dom/slotchange-event.html SET TIMEOUT: trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.html SET TIMEOUT: trusted-types/DOMWindowTimers-setTimeout-setInterval.html -SET TIMEOUT: pending-beacon/resources/pending_beacon-helper.js SET TIMEOUT: user-timing/* SET TIMEOUT: web-animations/crashtests/reparent-animating-element-002.html SET TIMEOUT: web-animations/timing-model/animations/* @@ -387,7 +388,6 @@ SET TIMEOUT: html/cross-origin-opener-policy/navigate-top-to-aboutblank.https.ht SET TIMEOUT: html/dom/documents/dom-tree-accessors/Document.currentScript.html SET TIMEOUT: html/webappapis/dynamic-markup-insertion/opening-the-input-stream/crbug-583445-regression.window.js SET TIMEOUT: html/webappapis/timers/* -SET TIMEOUT: orientation-event/resources/orientation-event-helpers.js SET TIMEOUT: portals/history/resources/portal-harness.js SET TIMEOUT: requestidlecallback/deadline-after-expired-timer.html SET TIMEOUT: resources/* @@ -395,6 +395,7 @@ SET TIMEOUT: scheduler/tentative/current-task-signal-async-abort.any.js SET TIMEOUT: scheduler/tentative/current-task-signal-async-priority.any.js SET TIMEOUT: speculation-rules/prerender/resources/activation-start.html SET TIMEOUT: speculation-rules/prerender/resources/prerender-response-code.html +SET TIMEOUT: speculation-rules/prerender/resources/prerender-while-prerender-outer.html SET TIMEOUT: speculation-rules/prerender/resources/deferred-promise-utils.js SET TIMEOUT: speculation-rules/prerender/resources/session-history-harness.js SET TIMEOUT: speculation-rules/prerender/resources/utils.js @@ -520,7 +521,6 @@ SET TIMEOUT: css/CSS2/tables/tables-102.xht SET TIMEOUT: css/mediaqueries/min-width-tables-001.html SET TIMEOUT: css/css-text/crashtests/rendering-rtl-bidi-override-crash.html SET TIMEOUT: css/css-backgrounds/color-mix-currentcolor-border-repaint-parent.html -SET TIMEOUT: svg/painting/color-mix-currentcolor-fill-stroke-repaint.html SET TIMEOUT: svg/painting/currentcolor-fill-stroke-repaint.html SET TIMEOUT: resource-timing/resources/run-async-tasks-promise.js @@ -579,6 +579,8 @@ MISSING-LINK: css/cssom-view/scrollTop-display-change.html # TODO https://github.com/web-platform-tests/wpt/issues/5770 MISSING-LINK: css/css-highlight-api/idlharness.window.js +MISSING-LINK: css/css-highlight-api/historical.window.js +MISSING-LINK: css/cssom/cssstyledeclaration-csstext-setter.window.js MISSING-LINK: css/geometry/*.worker.js MISSING-LINK: css/geometry/*.any.js MISSING-LINK: css/filter-effects/*.any.js @@ -586,7 +588,6 @@ MISSING-LINK: css/filter-effects/*.any.js # Tests that use WebKit/Blink testing APIs LAYOUTTESTS APIS: import-maps/data-driven/resources/test-helper-iframe.js LAYOUTTESTS APIS: resources/chromium/enable-hyperlink-auditing.js -LAYOUTTESTS APIS: resources/chromium/generic_sensor_mocks.js LAYOUTTESTS APIS: resources/chromium/webxr-test.js LAYOUTTESTS APIS: webxr/resources/webxr_util.js @@ -613,10 +614,8 @@ MISSING DEPENDENCY: resources/chromium/web-bluetooth-test.js MISSING DEPENDENCY: resources/chromium/webusb-test.js MISSING DEPENDENCY: resources/chromium/fake-serial.js MISSING DEPENDENCY: resources/chromium/fake-hid.js -MISSING DEPENDENCY: resources/chromium/generic_sensor_mocks.js MISSING DEPENDENCY: resources/chromium/mock-battery-monitor.js MISSING DEPENDENCY: resources/chromium/mock-barcodedetection.js -MISSING DEPENDENCY: resources/chromium/mock-direct-sockets.js MISSING DEPENDENCY: resources/chromium/mock-facedetection.js MISSING DEPENDENCY: resources/chromium/mock-idle-detection.js MISSING DEPENDENCY: resources/chromium/mock-imagecapture.js @@ -700,6 +699,17 @@ TESTHARNESS-IN-OTHER-TYPE: svg/svg-in-svg/svg-in-svg-circular-filter-reference-c # Adding the testharnessreport.js script causes the test to never complete. MISSING-TESTHARNESSREPORT: accessibility/crashtests/computed-node-checked.html +# Existing manual tests with references +REFERENCE-IN-OTHER-TYPE: css/CSS2/backgrounds/background-root-012a.xht +REFERENCE-IN-OTHER-TYPE: css/CSS2/backgrounds/background-root-013a.xht +REFERENCE-IN-OTHER-TYPE: css/CSS2/backgrounds/background-root-014a.xht +REFERENCE-IN-OTHER-TYPE: css/CSS2/cascade/html-precedence-004.xht +REFERENCE-IN-OTHER-TYPE: css/css-flexbox/css-flexbox-height-animation-stretch.html +REFERENCE-IN-OTHER-TYPE: css/css-transforms/css-transform-scale-001-manual.html + +# Needs control of page size +REFERENCE-IN-OTHER-TYPE: css/css-multicol/moz-multicol3-column-balancing-break-inside-avoid-1.html + PRINT STATEMENT: webdriver/tests/bidi/browsing_context/print/* PRINT STATEMENT: webdriver/tests/classic/print/* PRINT STATEMENT: webdriver/tests/support/fixtures_bidi.py @@ -731,6 +741,7 @@ SET TIMEOUT: editing/crashtests/make-editable-div-inline-and-set-contenteditable SET TIMEOUT: editing/crashtests/outdent-across-svg-boundary.html SET TIMEOUT: editing/crashtests/textarea-will-be-blurred-by-focus-event-listener.html SET TIMEOUT: mathml/crashtests/mozilla/* +SET TIMEOUT: selection/crashtests/selection-modify-line-boundary-around-empty-details.html PARSE-FAILED: mathml/crashtests/mozilla/289180-1.xml # Invalid HTML syntax /> on non-void elements @@ -744,5 +755,77 @@ HTML INVALID SYNTAX: domparsing/DOMParser-parseFromString-xml-doctype.html HTML INVALID SYNTAX: domparsing/DOMParser-parseFromString-xml-parsererror.html HTML INVALID SYNTAX: domparsing/XMLSerializer-serializeToString.html HTML INVALID SYNTAX: html/canvas/element/manual/unclosed-canvas-4.htm +HTML INVALID SYNTAX: html/syntax/parsing/unclosed-svg-script.html +HTML INVALID SYNTAX: mathml/crashtests/mozilla/411603-1.html HTML INVALID SYNTAX: quirks/percentage-height-calculation.html HTML INVALID SYNTAX: trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html + +# Tests which include testdriver.js but aren't testharness.js tests +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-grid/grid-model/grid-layout-stale-001.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-grid/grid-model/grid-layout-stale-002.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-scroll-anchoring/fullscreen-crash.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-shadow-parts/interaction-with-nested-pseudo-class.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-text-decor/invalidation/text-decoration-thickness.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-view-transitions/hit-test-unpainted-element.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/css-view-transitions/hit-test-unrelated-element.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: css/selectors/remove-hovered-element.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-IndexedDB-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-getUniqueId-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-isSameEntry-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-BroadcastChannel-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-Error-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-frames-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-windows-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-workers-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-frames-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-windows-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-postMessage-workers-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemBaseHandle-remove-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-getDirectoryHandle-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-getFileHandle-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-iteration-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-move-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-partitioned-manual.https.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-removeEntry-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemDirectoryHandle-resolve-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemFileHandle-create-sync-access-handle-manual.https.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemFileHandle-getFile-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemFileHandle-move-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemFileHandle-partitioned-manual.https.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemWritableFileStream-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemWritableFileStream-piped-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/local_FileSystemWritableFileStream-write-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/showDirectoryPicker-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/showOpenFilePicker-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: file-system-access/showSaveFilePicker-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/crashtests/chrome-1312699.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/crashtests/content-visibility-crash.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/rendering/backdrop-iframe.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/rendering/backdrop-inherit.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/rendering/backdrop-object.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: fullscreen/rendering/fullscreen-root-fills-page.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/crashtests/fieldset-middleclick.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/native-popup-with-datalist-ref.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/native-popup-with-datalist.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/select-appearance-writing-mode-vertical-lr.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-select-element/stylable-select/select-appearance-writing-mode-vertical-rl.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/popovers/popover-dialog-crash.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/popovers/popover-hint-crash.tentative.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: html/semantics/popovers/popover-manual-crash.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: infrastructure/testdriver/click_child_crossorigin.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: infrastructure/testdriver/click_child_testdriver.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: mediacapture-streams/MediaStreamTrack-end-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/change-payment-method-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/change-shipping-address-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/change-shipping-option-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/payment-request-event-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/supports-shipping-contact-delegation-manual.https.html +TESTDRIVER-IN-UNSUPPORTED-TYPE: shadow-dom/crashtests/move-to-new-tree-1343016.html + +# Pre compressed data using Shared Brotli and Shared Zstandard. +TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.br-d.data +TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.zstd-d.data diff --git a/test/wpt/tests/resources/chromium/fake-serial.js b/test/wpt/tests/resources/chromium/fake-serial.js index 29c8168fb59..8614b4d64f9 100644 --- a/test/wpt/tests/resources/chromium/fake-serial.js +++ b/test/wpt/tests/resources/chromium/fake-serial.js @@ -356,6 +356,10 @@ class FakeSerialService { portInfo.hasUsbProductId = true; portInfo.usbProductId = info.usbProductId; } + portInfo.connected = true; + if (info?.connected !== undefined) { + portInfo.connected = info.connected; + } let token = ++this.nextToken_; portInfo.token = {high: 0n, low: BigInt(token)}; @@ -366,8 +370,10 @@ class FakeSerialService { }; this.ports_.set(token, record); - for (let client of this.clients_) { - client.onPortAdded(portInfo); + if (portInfo.connected) { + for (let client of this.clients_) { + client.onPortConnectedStateChanged(portInfo); + } } return token; @@ -381,8 +387,26 @@ class FakeSerialService { this.ports_.delete(token); + record.portInfo.connected = false; + for (let client of this.clients_) { + client.onPortConnectedStateChanged(record.portInfo); + } + } + + setPortConnectedState(token, connected) { + let record = this.ports_.get(token); + if (record === undefined) { + return; + } + + let was_connected = record.portInfo.connected; + if (was_connected === connected) { + return; + } + + record.portInfo.connected = connected; for (let client of this.clients_) { - client.onPortRemoved(record.portInfo); + client.onPortConnectedStateChanged(record.portInfo); } } diff --git a/test/wpt/tests/resources/chromium/generic_sensor_mocks.js b/test/wpt/tests/resources/chromium/generic_sensor_mocks.js deleted file mode 100644 index baf57c7c85a..00000000000 --- a/test/wpt/tests/resources/chromium/generic_sensor_mocks.js +++ /dev/null @@ -1,523 +0,0 @@ -import {ReportingMode, Sensor, SensorClientRemote, SensorReceiver, SensorRemote, SensorType} from '/gen/services/device/public/mojom/sensor.mojom.m.js'; -import {SensorCreationResult, SensorInitParams_READ_BUFFER_SIZE_FOR_TESTS} from '/gen/services/device/public/mojom/sensor_provider.mojom.m.js'; -import {WebSensorProvider, WebSensorProviderReceiver} from '/gen/third_party/blink/public/mojom/sensor/web_sensor_provider.mojom.m.js'; - -// A "sliding window" that iterates over |data| and returns one item at a -// time, advancing and wrapping around as needed. |data| must be an array of -// arrays. -self.RingBuffer = class { - constructor(data) { - this.bufferPosition_ = 0; - // Validate |data|'s format and deep-copy every element. - this.data_ = Array.from(data, element => { - if (!Array.isArray(element)) { - throw new TypeError('Every |data| element must be an array.'); - } - return Array.from(element); - }) - } - - next() { - const value = this.data_[this.bufferPosition_]; - this.bufferPosition_ = (this.bufferPosition_ + 1) % this.data_.length; - return { done: false, value: value }; - } - - value() { - return this.data_[this.bufferPosition_]; - } - - [Symbol.iterator]() { - return this; - } -}; - -class DefaultSensorTraits { - // https://w3c.github.io/sensors/#threshold-check-algorithm - static isSignificantlyDifferent(reading1, reading2) { - return true; - } - - // https://w3c.github.io/sensors/#reading-quantization-algorithm - static roundToMultiple(reading) { - return reading; - } - - // https://w3c.github.io/ambient-light/#ambient-light-threshold-check-algorithm - static areReadingsEqual(reading1, reading2) { - return false; - } -} - -class AmbientLightSensorTraits extends DefaultSensorTraits { - // https://w3c.github.io/ambient-light/#reduce-sensor-accuracy - static #ROUNDING_MULTIPLE = 50; - static #SIGNIFICANCE_THRESHOLD = 25; - - // https://w3c.github.io/ambient-light/#ambient-light-threshold-check-algorithm - static isSignificantlyDifferent([illuminance1], [illuminance2]) { - return Math.abs(illuminance1 - illuminance2) >= - this.#SIGNIFICANCE_THRESHOLD; - } - - // https://w3c.github.io/ambient-light/#ambient-light-reading-quantization-algorithm - static roundToMultiple(reading) { - const illuminance = reading[0]; - const scaledValue = - illuminance / AmbientLightSensorTraits.#ROUNDING_MULTIPLE; - let roundedReading = reading.splice(); - - if (illuminance < 0.0) { - roundedReading[0] = -AmbientLightSensorTraits.#ROUNDING_MULTIPLE * - Math.floor(-scaledValue + 0.5); - } else { - roundedReading[0] = AmbientLightSensorTraits.#ROUNDING_MULTIPLE * - Math.floor(scaledValue + 0.5); - } - - return roundedReading; - } - - // https://w3c.github.io/ambient-light/#ambient-light-threshold-check-algorithm - static areReadingsEqual([illuminance1], [illuminance2]) { - return illuminance1 === illuminance2; - } -} - -self.GenericSensorTest = (() => { - // Default sensor frequency in default configurations. - const DEFAULT_FREQUENCY = 5; - - // Class that mocks Sensor interface defined in - // https://cs.chromium.org/chromium/src/services/device/public/mojom/sensor.mojom - class MockSensor { - static #BUFFER_OFFSET_TIMESTAMP = 1; - static #BUFFER_OFFSET_READINGS = 2; - - constructor(sensorRequest, buffer, reportingMode, sensorType) { - this.client_ = null; - this.startShouldFail_ = false; - this.notifyOnReadingChange_ = true; - this.reportingMode_ = reportingMode; - this.sensorType_ = sensorType; - this.sensorReadingTimerId_ = null; - this.readingData_ = null; - this.requestedFrequencies_ = []; - // The Blink implementation (third_party/blink/renderer/modules/sensor/sensor.cc) - // sets a timestamp by creating a DOMHighResTimeStamp from a given platform timestamp. - // In this mock implementation we use a starting value - // and an increment step value that resemble a platform timestamp reasonably enough. - this.timestamp_ = window.performance.timeOrigin; - // |buffer| represents a SensorReadingSharedBuffer on the C++ side in - // Chromium. It consists, in this order, of a - // SensorReadingField (an 8-byte union that includes - // 32-bit integer used by the lock class), and a SensorReading consisting - // of an 8-byte timestamp and 4 8-byte reading fields. - // - // |this.buffer_[0]| is zeroed by default, which allows OneWriterSeqLock - // to work with our custom memory buffer that did not actually create a - // OneWriterSeqLock instance. It is never changed manually here. - // - // Use MockSensor.#BUFFER_OFFSET_TIMESTAMP and - // MockSensor.#BUFFER_OFFSET_READINGS to access the other positions in - // |this.buffer_| without having to hardcode magic numbers in the code. - this.buffer_ = buffer; - this.buffer_.fill(0); - this.receiver_ = new SensorReceiver(this); - this.receiver_.$.bindHandle(sensorRequest.handle); - this.lastRawReading_ = null; - this.lastRoundedReading_ = null; - - if (sensorType == SensorType.AMBIENT_LIGHT) { - this.sensorTraits = AmbientLightSensorTraits; - } else { - this.sensorTraits = DefaultSensorTraits; - } - } - - // Returns default configuration. - async getDefaultConfiguration() { - return { frequency: DEFAULT_FREQUENCY }; - } - - // Adds configuration for the sensor and starts reporting fake data - // through setSensorReading function. - async addConfiguration(configuration) { - this.requestedFrequencies_.push(configuration.frequency); - // Sort using descending order. - this.requestedFrequencies_.sort( - (first, second) => { return second - first }); - - if (!this.startShouldFail_ ) - this.startReading(); - - return { success: !this.startShouldFail_ }; - } - - // Removes sensor configuration from the list of active configurations and - // stops notification about sensor reading changes if - // requestedFrequencies_ is empty. - removeConfiguration(configuration) { - const index = this.requestedFrequencies_.indexOf(configuration.frequency); - if (index == -1) - return; - - this.requestedFrequencies_.splice(index, 1); - if (this.requestedFrequencies_.length === 0) - this.stopReading(); - } - - // ConfigureReadingChangeNotifications(bool enabled) - // Configures whether to report a reading change when in ON_CHANGE - // reporting mode. - configureReadingChangeNotifications(notifyOnReadingChange) { - this.notifyOnReadingChange_ = notifyOnReadingChange; - } - - resume() { - this.startReading(); - } - - suspend() { - this.stopReading(); - } - - // Mock functions - - // Resets mock Sensor state. - reset() { - this.stopReading(); - this.startShouldFail_ = false; - this.requestedFrequencies_ = []; - this.notifyOnReadingChange_ = true; - this.readingData_ = null; - this.buffer_.fill(0); - this.receiver_.$.close(); - this.lastRawReading_ = null; - this.lastRoundedReading_ = null; - } - - // Sets fake data that is used to deliver sensor reading updates. - setSensorReading(readingData) { - this.readingData_ = new RingBuffer(readingData); - } - - // This is a workaround to accommodate Blink's Device Orientation - // implementation. In general, all tests should use setSensorReading() - // instead. - setSensorReadingImmediately(readingData) { - this.setSensorReading(readingData); - - const reading = this.readingData_.value(); - this.buffer_.set(reading, MockSensor.#BUFFER_OFFSET_READINGS); - this.buffer_[MockSensor.#BUFFER_OFFSET_TIMESTAMP] = this.timestamp_++; - } - - // Sets flag that forces sensor to fail when addConfiguration is invoked. - setStartShouldFail(shouldFail) { - this.startShouldFail_ = shouldFail; - } - - startReading() { - if (this.readingData_ != null) { - this.stopReading(); - } - let maxFrequencyUsed = this.requestedFrequencies_[0]; - let timeout = (1 / maxFrequencyUsed) * 1000; - this.sensorReadingTimerId_ = window.setInterval(() => { - if (this.readingData_) { - // |buffer_| is a TypedArray, so we need to make sure pass an - // array to set(). - const reading = this.readingData_.next().value; - if (!Array.isArray(reading)) { - throw new TypeError("startReading(): The readings passed to " + - "setSensorReading() must be arrays"); - } - - if (this.reportingMode_ == ReportingMode.ON_CHANGE && - this.lastRawReading_ !== null && - !this.sensorTraits.isSignificantlyDifferent( - this.lastRawReading_, reading)) { - // In case new value is not significantly different compared to - // old value, new value is not sent. - return; - } - - this.lastRawReading_ = reading.slice(); - const roundedReading = this.sensorTraits.roundToMultiple(reading); - - if (this.reportingMode_ == ReportingMode.ON_CHANGE && - this.lastRoundedReading_ !== null && - this.sensorTraits.areReadingsEqual( - roundedReading, this.lastRoundedReading_)) { - // In case new rounded value is not different compared to old - // value, new value is not sent. - return; - } - this.buffer_.set(roundedReading, MockSensor.#BUFFER_OFFSET_READINGS); - this.lastRoundedReading_ = roundedReading; - } - - // For all tests sensor reading should have monotonically - // increasing timestamp. - this.buffer_[MockSensor.#BUFFER_OFFSET_TIMESTAMP] = this.timestamp_++; - - if (this.reportingMode_ === ReportingMode.ON_CHANGE && - this.notifyOnReadingChange_) { - this.client_.sensorReadingChanged(); - } - }, timeout); - } - - stopReading() { - if (this.sensorReadingTimerId_ != null) { - window.clearInterval(this.sensorReadingTimerId_); - this.sensorReadingTimerId_ = null; - } - this.buffer_.fill(0); - this.lastRawReading_ = null; - this.lastRoundedReading_ = null; - } - - getSamplingFrequency() { - if (this.requestedFrequencies_.length == 0) { - throw new Error("getSamplingFrequency(): No configured frequency"); - } - return this.requestedFrequencies_[0]; - } - - isReadingData() { - return this.sensorReadingTimerId_ != null; - } - } - - // Class that mocks the WebSensorProvider interface defined in - // https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/sensor/web_sensor_provider.mojom - class MockSensorProvider { - constructor() { - this.readingSizeInBytes_ = - Number(SensorInitParams_READ_BUFFER_SIZE_FOR_TESTS); - this.sharedBufferSizeInBytes_ = - this.readingSizeInBytes_ * (SensorType.MAX_VALUE + 1); - let rv = Mojo.createSharedBuffer(this.sharedBufferSizeInBytes_); - if (rv.result != Mojo.RESULT_OK) { - throw new Error('MockSensorProvider: Failed to create shared buffer'); - } - const handle = rv.handle; - rv = handle.mapBuffer(0, this.sharedBufferSizeInBytes_); - if (rv.result != Mojo.RESULT_OK) { - throw new Error("MockSensorProvider: Failed to map shared buffer"); - } - this.shmemArrayBuffer_ = rv.buffer; - rv = handle.duplicateBufferHandle({readOnly: true}); - if (rv.result != Mojo.RESULT_OK) { - throw new Error( - 'MockSensorProvider: failed to duplicate shared buffer'); - } - this.readOnlySharedBufferHandle_ = rv.handle; - this.activeSensors_ = new Map(); - this.resolveFuncs_ = new Map(); - this.getSensorShouldFail_ = new Map(); - this.permissionsDenied_ = new Map(); - this.maxFrequency_ = 60; - this.minFrequency_ = 1; - this.mojomSensorType_ = new Map([ - ['Accelerometer', SensorType.ACCELEROMETER], - ['LinearAccelerationSensor', SensorType.LINEAR_ACCELERATION], - ['GravitySensor', SensorType.GRAVITY], - ['AmbientLightSensor', SensorType.AMBIENT_LIGHT], - ['Gyroscope', SensorType.GYROSCOPE], - ['Magnetometer', SensorType.MAGNETOMETER], - ['AbsoluteOrientationSensor', - SensorType.ABSOLUTE_ORIENTATION_QUATERNION], - ['AbsoluteOrientationEulerAngles', - SensorType.ABSOLUTE_ORIENTATION_EULER_ANGLES], - ['RelativeOrientationSensor', - SensorType.RELATIVE_ORIENTATION_QUATERNION], - ['RelativeOrientationEulerAngles', - SensorType.RELATIVE_ORIENTATION_EULER_ANGLES], - ['ProximitySensor', SensorType.PROXIMITY] - ]); - this.receiver_ = new WebSensorProviderReceiver(this); - - this.interceptor_ = - new MojoInterfaceInterceptor(WebSensorProvider.$interfaceName); - this.interceptor_.oninterfacerequest = e => { - this.bindToPipe(e.handle); - }; - this.interceptor_.start(); - } - - // Returns initialized Sensor proxy to the client. - async getSensor(type) { - if (this.getSensorShouldFail_.get(type)) { - return {result: SensorCreationResult.ERROR_NOT_AVAILABLE, - initParams: null}; - } - if (this.permissionsDenied_.get(type)) { - return {result: SensorCreationResult.ERROR_NOT_ALLOWED, - initParams: null}; - } - - const offset = type * this.readingSizeInBytes_; - const reportingMode = ReportingMode.ON_CHANGE; - - const sensor = new SensorRemote(); - if (!this.activeSensors_.has(type)) { - const shmemView = new Float64Array( - this.shmemArrayBuffer_, offset, - this.readingSizeInBytes_ / Float64Array.BYTES_PER_ELEMENT); - const mockSensor = new MockSensor( - sensor.$.bindNewPipeAndPassReceiver(), shmemView, reportingMode, - type); - this.activeSensors_.set(type, mockSensor); - this.activeSensors_.get(type).client_ = new SensorClientRemote(); - } - - const rv = this.readOnlySharedBufferHandle_.duplicateBufferHandle( - {readOnly: true}); - if (rv.result != Mojo.RESULT_OK) { - throw new Error('getSensor(): failed to duplicate shared buffer'); - } - - const defaultConfig = { frequency: DEFAULT_FREQUENCY }; - // Consider sensor traits to meet assertions in C++ code (see - // services/device/public/cpp/generic_sensor/sensor_traits.h) - if (type == SensorType.AMBIENT_LIGHT || type == SensorType.MAGNETOMETER) { - this.maxFrequency_ = Math.min(10, this.maxFrequency_); - } - - const client = this.activeSensors_.get(type).client_; - const initParams = { - sensor, - clientReceiver: client.$.bindNewPipeAndPassReceiver(), - memory: {buffer: rv.handle}, - bufferOffset: BigInt(offset), - mode: reportingMode, - defaultConfiguration: defaultConfig, - minimumFrequency: this.minFrequency_, - maximumFrequency: this.maxFrequency_ - }; - - if (this.resolveFuncs_.has(type)) { - for (let resolveFunc of this.resolveFuncs_.get(type)) { - resolveFunc(this.activeSensors_.get(type)); - } - this.resolveFuncs_.delete(type); - } - - return {result: SensorCreationResult.SUCCESS, initParams}; - } - - // Binds object to mojo message pipe - bindToPipe(pipe) { - this.receiver_.$.bindHandle(pipe); - } - - // Mock functions - - // Resets state of mock SensorProvider between test runs. - reset() { - for (const sensor of this.activeSensors_.values()) { - sensor.reset(); - } - this.activeSensors_.clear(); - this.resolveFuncs_.clear(); - this.getSensorShouldFail_.clear(); - this.permissionsDenied_.clear(); - this.maxFrequency_ = 60; - this.minFrequency_ = 1; - this.receiver_.$.close(); - this.interceptor_.stop(); - } - - // Sets flag that forces mock SensorProvider to fail when getSensor() is - // invoked. - setGetSensorShouldFail(sensorType, shouldFail) { - this.getSensorShouldFail_.set(this.mojomSensorType_.get(sensorType), - shouldFail); - } - - setPermissionsDenied(sensorType, permissionsDenied) { - this.permissionsDenied_.set(this.mojomSensorType_.get(sensorType), - permissionsDenied); - } - - // Returns mock sensor that was created in getSensor to the layout test. - getCreatedSensor(sensorType) { - const type = this.mojomSensorType_.get(sensorType); - if (typeof type != "number") { - throw new TypeError(`getCreatedSensor(): Invalid sensor type ${sensorType}`); - } - - if (this.activeSensors_.has(type)) { - return Promise.resolve(this.activeSensors_.get(type)); - } - - return new Promise(resolve => { - if (!this.resolveFuncs_.has(type)) { - this.resolveFuncs_.set(type, []); - } - this.resolveFuncs_.get(type).push(resolve); - }); - } - - // Sets the maximum frequency for a concrete sensor. - setMaximumSupportedFrequency(frequency) { - this.maxFrequency_ = frequency; - } - - // Sets the minimum frequency for a concrete sensor. - setMinimumSupportedFrequency(frequency) { - this.minFrequency_ = frequency; - } - } - - let testInternal = { - initialized: false, - sensorProvider: null - } - - class GenericSensorTestChromium { - constructor() { - Object.freeze(this); // Make it immutable. - } - - async initialize() { - if (testInternal.initialized) - throw new Error('Call reset() before initialize().'); - - // Grant sensor permissions for Chromium testdriver. - // testdriver.js only works in the top-level browsing context, so do - // nothing if we're in e.g. an iframe. - if (window.parent === window) { - for (const entry - of ['accelerometer', 'gyroscope', 'magnetometer', - 'ambient-light-sensor']) { - await test_driver.set_permission({name: entry}, 'granted'); - } - } - - testInternal.sensorProvider = new MockSensorProvider; - testInternal.initialized = true; - } - // Resets state of sensor mocks between test runs. - async reset() { - if (!testInternal.initialized) - throw new Error('Call initialize() before reset().'); - testInternal.sensorProvider.reset(); - testInternal.sensorProvider = null; - testInternal.initialized = false; - - // Wait for an event loop iteration to let any pending mojo commands in - // the sensor provider finish. - await new Promise(resolve => setTimeout(resolve, 0)); - } - - getSensorProvider() { - return testInternal.sensorProvider; - } - } - - return GenericSensorTestChromium; -})(); diff --git a/test/wpt/tests/resources/chromium/generic_sensor_mocks.js.headers b/test/wpt/tests/resources/chromium/generic_sensor_mocks.js.headers deleted file mode 100644 index 6805c323df5..00000000000 --- a/test/wpt/tests/resources/chromium/generic_sensor_mocks.js.headers +++ /dev/null @@ -1 +0,0 @@ -Content-Type: text/javascript; charset=utf-8 diff --git a/test/wpt/tests/resources/chromium/mock-direct-sockets.js b/test/wpt/tests/resources/chromium/mock-direct-sockets.js deleted file mode 100644 index 6d557f7a015..00000000000 --- a/test/wpt/tests/resources/chromium/mock-direct-sockets.js +++ /dev/null @@ -1,94 +0,0 @@ -'use strict'; - -import {DirectSocketsService, DirectSocketsServiceReceiver} from '/gen/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom.m.js'; - -self.DirectSocketsServiceTest = (() => { - // Class that mocks DirectSocketsService interface defined in - // https://source.chromium.org/chromium/chromium/src/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom - class MockDirectSocketsService { - constructor() { - this.interceptor_ = new MojoInterfaceInterceptor(DirectSocketsService.$interfaceName); - this.receiver_ = new DirectSocketsServiceReceiver(this); - this.interceptor_.oninterfacerequest = e => - this.receiver_.$.bindHandle(e.handle); - this.interceptor_.start(); - } - - reset() { - this.receiver_.$.close(); - this.interceptor_.stop(); - } - - openTCPSocket( - options, - receiver, - observer) { - return Promise.resolve({ - // return result = net:Error::NOT_IMPLEMENTED (code -11) - result: -11 - }); - } - - openConnectedUDPSocket( - options, - receiver, - listener) { - return Promise.resolve({ - // return result = net:Error::NOT_IMPLEMENTED (code -11) - result: -11 - }); - } - - openBoundUDPSocket( - options, - receiver, - listener) { - return Promise.resolve({ - // return result = net:Error::NOT_IMPLEMENTED (code -11) - result: -11 - }); - } - - openTCPServerSocket( - options, - receiver) { - return Promise.resolve({ - // return result = net:Error::NOT_IMPLEMENTED (code -11) - result: -11 - }); - } - } - - let testInternal = { - initialized: false, - mockDirectSocketsService: null - } - - class DirectSocketsServiceTestChromium { - constructor() { - Object.freeze(this); // Make it immutable. - } - - initialize() { - if (!testInternal.initialized) { - testInternal = { - mockDirectSocketsService: new MockDirectSocketsService(), - initialized: true - }; - } - } - - async reset() { - if (testInternal.initialized) { - testInternal.mockDirectSocketsService.reset(); - testInternal = { - mockDirectSocketsService: null, - initialized: false - }; - await new Promise(resolve => setTimeout(resolve, 0)); - } - } - } - - return DirectSocketsServiceTestChromium; -})(); diff --git a/test/wpt/tests/resources/chromium/mock-pressure-service.js b/test/wpt/tests/resources/chromium/mock-pressure-service.js index 02d10f856ae..016c6d97e73 100644 --- a/test/wpt/tests/resources/chromium/mock-pressure-service.js +++ b/test/wpt/tests/resources/chromium/mock-pressure-service.js @@ -58,39 +58,32 @@ class MockPressureService { return {status: this.pressureStatus_}; } - startPlatformCollector(sampleRate) { - if (sampleRate === 0) + startPlatformCollector(sampleInterval) { + if (sampleInterval === 0) return; if (this.pressureServiceReadingTimerId_ != null) this.stopPlatformCollector(); - // The following code for calculating the timestamp was taken from - // https://source.chromium.org/chromium/chromium/src/+/main:third_party/ - // blink/web_tests/http/tests/resources/ - // geolocation-mock.js;l=131;drc=37a9b6c03b9bda9fcd62fc0e5e8016c278abd31f - - // The new Date().getTime() returns the number of milliseconds since the - // UNIX epoch (1970-01-01 00::00:00 UTC), while |internalValue| of the - // device.mojom.PressureUpdate represents the value of microseconds since - // the Windows FILETIME epoch (1601-01-01 00:00:00 UTC). So add the delta - // when sets the |internalValue|. See more info in //base/time/time.h. - const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0); - const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0); - // |epochDeltaInMs| equals to base::Time::kTimeTToMicrosecondsOffset. - const epochDeltaInMs = unixEpoch - windowsEpoch; - - const timeout = (1 / sampleRate) * 1000; this.pressureServiceReadingTimerId_ = self.setInterval(() => { if (this.pressureUpdate_ === null || this.observers_.length === 0) return; + + // Because we cannot retrieve directly the timeOrigin internal in + // TimeTicks from Chromium, we need to create a timestamp that + // would help to test basic functionality. + // by multiplying performance.timeOrigin by 10 we make sure that the + // origin is higher than the internal time origin in TimeTicks. + // performance.now() allows us to have an increase matching the TimeTicks + // that would be read from the platform collector. this.pressureUpdate_.timestamp = { - internalValue: BigInt((new Date().getTime() + epochDeltaInMs) * 1000) + internalValue: + Math.round((performance.timeOrigin * 10) + performance.now()) * 1000 }; for (let observer of this.observers_) observer.onPressureUpdated(this.pressureUpdate_); this.updatesDelivered_++; - }, timeout); + }, sampleInterval); } stopPlatformCollector() { diff --git a/test/wpt/tests/resources/declarative-shadow-dom-polyfill.js b/test/wpt/tests/resources/declarative-shadow-dom-polyfill.js deleted file mode 100644 index 99a3e911eb6..00000000000 --- a/test/wpt/tests/resources/declarative-shadow-dom-polyfill.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Polyfill for attaching shadow trees for declarative Shadow DOM for - * implementations that do not support declarative Shadow DOM. - * - * Note: this polyfill will feature-detect the native feature, and do nothing - * if supported. - * - * See: https://github.com/whatwg/html/pull/5465 - * - * root: The root of the subtree in which to upgrade shadow roots - * - */ - -function polyfill_declarative_shadow_dom(root) { - if (HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode')) - return; - root.querySelectorAll("template[shadowrootmode]").forEach(template => { - const mode = template.getAttribute("shadowrootmode"); - const delegatesFocus = template.hasAttribute("shadowrootdelegatesfocus"); - const shadowRoot = template.parentNode.attachShadow({ mode, delegatesFocus }); - shadowRoot.appendChild(template.content); - template.remove(); - polyfill_declarative_shadow_dom(shadowRoot); - }); -} diff --git a/test/wpt/tests/resources/idlharness.js b/test/wpt/tests/resources/idlharness.js index 8f741b09b26..4cf19234af2 100644 --- a/test/wpt/tests/resources/idlharness.js +++ b/test/wpt/tests/resources/idlharness.js @@ -566,6 +566,7 @@ IdlArray.prototype.is_json_type = function(type) case "Uint8ClampedArray": case "BigInt64Array": case "BigUint64Array": + case "Float16Array": case "Float32Array": case "Float64Array": case "ArrayBuffer": diff --git a/test/wpt/tests/resources/test/tests/unit/IdlArray/is_json_type.html b/test/wpt/tests/resources/test/tests/unit/IdlArray/is_json_type.html index 18e83a8e893..caea20067fa 100644 --- a/test/wpt/tests/resources/test/tests/unit/IdlArray/is_json_type.html +++ b/test/wpt/tests/resources/test/tests/unit/IdlArray/is_json_type.html @@ -39,6 +39,7 @@ assert_false(idl.is_json_type(typeFrom("Uint8ClampedArray"))); assert_false(idl.is_json_type(typeFrom("BigInt64Array"))); assert_false(idl.is_json_type(typeFrom("BigUint64Array"))); + assert_false(idl.is_json_type(typeFrom("Float16Array"))); assert_false(idl.is_json_type(typeFrom("Float32Array"))); assert_false(idl.is_json_type(typeFrom("Float64Array"))); assert_false(idl.is_json_type(typeFrom("ArrayBuffer"))); diff --git a/test/wpt/tests/resources/test/tox.ini b/test/wpt/tests/resources/test/tox.ini index 12013a1a705..49603ef64bd 100644 --- a/test/wpt/tests/resources/test/tox.ini +++ b/test/wpt/tests/resources/test/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37,py38,py39,py310,py311 +envlist = py38,py39,py310,py311 skipsdist=True [testenv] diff --git a/test/wpt/tests/resources/testdriver.js b/test/wpt/tests/resources/testdriver.js index 70c799167f9..2d1a89690cc 100644 --- a/test/wpt/tests/resources/testdriver.js +++ b/test/wpt/tests/resources/testdriver.js @@ -357,6 +357,25 @@ return window.test_driver_internal.set_window_rect(rect, context); }, + /** + * Gets a rect with the size and position on the screen from the current window state. + * + * Matches the behaviour of the `Get Window Rect + * `_ + * WebDriver command + * + * @param {WindowProxy} context - Browsing context in which + * to run the call, or null for the current + * browsing context. + * + * @returns {Promise} fulfilled after the window rect is returned, or rejected + * in cases the WebDriver command returns errors. Returns a + * `WindowRect `_ + */ + get_window_rect: function(context=null) { + return window.test_driver_internal.get_window_rect(context); + }, + /** * Send a sequence of actions * @@ -1004,6 +1023,49 @@ */ get_virtual_sensor_information: function(sensor_type, context=null) { return window.test_driver_internal.get_virtual_sensor_information(sensor_type, context); + }, + + /** + * Overrides device posture set by hardware. + * + * Matches the `Set device posture + * `_ + * WebDriver command. + * + * @param {String} posture - A `DevicePostureType + * `_ + * either "continuous" or "folded". + * @param {WindowProxy} [context=null] - Browsing context in which to + * run the call, or null for the + * current browsing context. + * + * @returns {Promise} Fulfilled when device posture is set. + * Rejected in case the WebDriver command errors out + * (including if a device posture of the given type + * does not exist). + */ + set_device_posture: function(posture, context=null) { + return window.test_driver_internal.set_device_posture(posture, context); + }, + + /** + * Removes device posture override and returns device posture control + * back to hardware. + * + * Matches the `Clear device posture + * `_ + * WebDriver command. + * + * @param {WindowProxy} [context=null] - Browsing context in which to + * run the call, or null for the + * current browsing context. + * + * @returns {Promise} Fulfilled after the device posture override has + * been removed. Rejected in case the WebDriver + * command errors out. + */ + clear_device_posture: function(context=null) { + return window.test_driver_internal.clear_device_posture(context); } }; @@ -1082,6 +1144,10 @@ throw new Error("set_window_rect() is not implemented by testdriver-vendor.js"); }, + async get_window_rect(context=null) { + throw new Error("get_window_rect() is not implemented by testdriver-vendor.js"); + }, + async action_sequence(actions, context=null) { throw new Error("action_sequence() is not implemented by testdriver-vendor.js"); }, @@ -1180,6 +1246,14 @@ async get_virtual_sensor_information(sensor_type, context=null) { throw new Error("get_virtual_sensor_information() is not implemented by testdriver-vendor.js"); + }, + + async set_device_posture(posture, context=null) { + throw new Error("set_device_posture() is not implemented by testdriver-vendor.js"); + }, + + async clear_device_posture(context=null) { + throw new Error("clear_device_posture() is not implemented by testdriver-vendor.js"); } }; })(); diff --git a/test/wpt/tests/resources/testharness.js b/test/wpt/tests/resources/testharness.js index a0011f03ac0..c5c375e1720 100644 --- a/test/wpt/tests/resources/testharness.js +++ b/test/wpt/tests/resources/testharness.js @@ -1201,6 +1201,23 @@ object.addEventListener(event, callback, false); } + // Internal helper function to provide timeout-like functionality in + // environments where there is no setTimeout(). (No timeout ID or + // clearTimeout().) + function fake_set_timeout(callback, delay) { + var p = Promise.resolve(); + var start = Date.now(); + var end = start + delay; + function check() { + if ((end - Date.now()) > 0) { + p.then(check); + } else { + callback(); + } + } + p.then(check); + } + /** * Global version of :js:func:`Test.step_timeout` for use in single page tests. * @@ -1212,7 +1229,8 @@ function step_timeout(func, timeout) { var outer_this = this; var args = Array.prototype.slice.call(arguments, 2); - return setTimeout(function() { + var local_set_timeout = typeof global_scope.setTimeout === "undefined" ? fake_set_timeout : setTimeout; + return local_set_timeout(function() { func.apply(outer_this, args); }, timeout * tests.timeout_multiplier); } @@ -1247,7 +1265,7 @@ */ function is_node(object) { - // I use duck-typing instead of instanceof because + // I use duck-typing instead of instanceof, because // instanceof doesn't work if the node is from another window (like an // iframe's contentWindow): // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295 @@ -2720,7 +2738,8 @@ Test.prototype.step_timeout = function(func, timeout) { var test_this = this; var args = Array.prototype.slice.call(arguments, 2); - return setTimeout(this.step_func(function() { + var local_set_timeout = typeof global_scope.setTimeout === "undefined" ? fake_set_timeout : setTimeout; + return local_set_timeout(this.step_func(function() { return func.apply(test_this, args); }), timeout * tests.timeout_multiplier); }; @@ -2751,6 +2770,7 @@ var timeout_full = timeout * tests.timeout_multiplier; var remaining = Math.ceil(timeout_full / interval); var test_this = this; + var local_set_timeout = typeof global_scope.setTimeout === 'undefined' ? fake_set_timeout : setTimeout; const step = test_this.step_func((result) => { if (result) { @@ -2761,7 +2781,7 @@ "Timed out waiting on condition"); } remaining--; - setTimeout(wait_for_inner, interval); + local_set_timeout(wait_for_inner, interval); } }); @@ -4174,11 +4194,7 @@ status ], ], - ["button", - {"onclick": "let evt = new Event('__test_restart'); " + - "let canceled = !window.dispatchEvent(evt);" + - "if (!canceled) { location.reload() }"}, - "Rerun"] + ["button", {"id":"rerun"}, "Rerun"] ]]; if (harness_status.status === harness_status.ERROR) { @@ -4210,6 +4226,13 @@ log.appendChild(render(summary_template, {num_tests:tests.length}, output_document)); + output_document.getElementById("rerun").addEventListener("click", + function() { + let evt = new Event('__test_restart'); + let canceled = !window.dispatchEvent(evt); + if (!canceled) { location.reload(); } + }); + forEach(output_document.querySelectorAll("section#summary label"), function(element) { @@ -4234,18 +4257,6 @@ }); }); - // This use of innerHTML plus manual escaping is not recommended in - // general, but is necessary here for performance. Using textContent - // on each individual adds tens of seconds of execution time for - // large test suites (tens of thousands of tests). - function escape_html(s) - { - return s.replace(/\&/g, "&") - .replace(/ { - var output_fn = "" + escape_html(assert.assert_name) + "("; - var prefix_len = output_fn.length; - var output_args = assert.args; - var output_len = output_args.reduce((prev, current) => prev+current, prefix_len); - if (output_len[output_len.length - 1] > 50) { - output_args = output_args.map((x, i) => - (i > 0 ? " ".repeat(prefix_len) : "" )+ x + (i < output_args.length - 1 ? ",\n" : "")); - } else { - output_args = output_args.map((x, i) => x + (i < output_args.length - 1 ? ", " : "")); - } - output_fn += escape_html(output_args.join("")); - output_fn += ')'; - var output_location; + + const table = asserts_output.querySelector("table"); + for (const assert of asserts) { + const status_class_name = status_class(Test.prototype.status_formats[assert.status]); + var output_fn = "(" + assert.args.join(", ") + ")"; if (assert.stack) { - output_location = assert.stack.split("\n", 1)[0].replace(/@?\w+:\/\/[^ "\/]+(?::\d+)?/g, " "); + output_fn += "\n"; + output_fn += assert.stack.split("\n", 1)[0].replace(/@?\w+:\/\/[^ "\/]+(?::\d+)?/g, " "); } - return "" + - "" + - Test.prototype.status_formats[assert.status] + "" + - "
" +
-                    output_fn +
-                    (output_location ? "\n" + escape_html(output_location) : "") +
-                    "
"; + table.appendChild(render( + ["tr", {"class":"overall-" + status_class_name}, + ["td", {"class":status_class_name}, Test.prototype.status_formats[assert.status]], + ["td", {}, ["pre", {}, ["strong", {}, assert.assert_name], output_fn]] ])); } - ).join("\n"); - rv += ""; - return rv; + return asserts_output; } - log.appendChild(document.createElementNS(xhtml_ns, "section")); var assertions = has_assertions(); - var html = "

Details

" + - "" + - (assertions ? "" : "") + - "" + - ""; - for (var i = 0; i < tests.length; i++) { - var test = tests[i]; - html += '' + - '"; - } - html += "
ResultTest NameAssertionMessage
' + - test.format_status() + - "" + - escape_html(test.name) + - "" + - (assertions ? escape_html(get_assertion(test)) + "" : "") + - escape_html(test.message ? tests[i].message : " ") + - (tests[i].stack ? "
" +
-                 escape_html(tests[i].stack) +
-                 "
": ""); + const section = render( + ["section", {}, + ["h2", {}, "Details"], + ["table", {"id":"results", "class":(assertions ? "assertions" : "")}, + ["thead", {}, + ["tr", {}, + ["th", {}, "Result"], + ["th", {}, "Test Name"], + (assertions ? ["th", {}, "Assertion"] : ""), + ["th", {}, "Message" ]]], + ["tbody", {}]]]); + + const tbody = section.querySelector("tbody"); + for (const test of tests) { + const status = test.format_status(); + const status_class_name = status_class(status); + tbody.appendChild(render( + ["tr", {"class":"overall-" + status_class_name}, + ["td", {"class":status_class_name}, status], + ["td", {}, test.name], + (assertions ? ["td", {}, get_assertion(test)] : ""), + ["td", {}, + test.message ?? "", + ["pre", {}, test.stack ?? ""]]])); if (!(test instanceof RemoteTest)) { - html += "
Asserts run" + get_asserts_output(test) + "
" + tbody.lastChild.lastChild.appendChild(get_asserts_output(test)); } - html += "
"; - try { - log.lastChild.innerHTML = html; - } catch (e) { - log.appendChild(document.createElementNS(xhtml_ns, "p")) - .textContent = "Setting innerHTML for the log threw an exception."; - log.appendChild(document.createElementNS(xhtml_ns, "pre")) - .textContent = html; } + log.appendChild(section); }; /* @@ -4777,6 +4770,15 @@ return "Untitled"; } + /** Fetches a JSON resource and parses it */ + async function fetch_json(resource) { + const response = await fetch(resource); + return await response.json(); + } + if (!global_scope.GLOBAL || !global_scope.GLOBAL.isShadowRealm()) { + expose(fetch_json, 'fetch_json'); + } + /** * Setup globals */ diff --git a/test/wpt/tests/resources/testharnessreport.js b/test/wpt/tests/resources/testharnessreport.js index e5cb40fe0ef..405a2d8b06f 100644 --- a/test/wpt/tests/resources/testharnessreport.js +++ b/test/wpt/tests/resources/testharnessreport.js @@ -14,31 +14,6 @@ * parameters they are called with see testharness.js */ -function dump_test_results(tests, status) { - var results_element = document.createElement("script"); - results_element.type = "text/json"; - results_element.id = "__testharness__results__"; - var test_results = tests.map(function(x) { - return {name:x.name, status:x.status, message:x.message, stack:x.stack} - }); - var data = {test:window.location.href, - tests:test_results, - status: status.status, - message: status.message, - stack: status.stack}; - results_element.textContent = JSON.stringify(data); - - // To avoid a HierarchyRequestError with XML documents, ensure that 'results_element' - // is inserted at a location that results in a valid document. - var parent = document.body - ? document.body // is required in XHTML documents - : document.documentElement; // fallback for optional in HTML5, SVG, etc. - - parent.appendChild(results_element); -} - -add_completion_callback(dump_test_results); - /* If the parent window has a testharness_properties object, * we use this to provide the test settings. This is used by the * default in-browser runner to configure the timeout and the diff --git a/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event-worker.js b/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event-worker.js new file mode 100644 index 00000000000..525bc96e763 --- /dev/null +++ b/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event-worker.js @@ -0,0 +1,2 @@ +self.onmessageerror = e => { e.source.postMessage("received error event"); }; +self.onmessage = e => { e.source.postMessage("received message event"); }; diff --git a/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event.https.html b/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event.https.html new file mode 100644 index 00000000000..fc8edb4b896 --- /dev/null +++ b/test/wpt/tests/service-workers/service-worker/ServiceWorkerGlobalScope/error-message-event.https.html @@ -0,0 +1,47 @@ + + + +Service Worker GlobalScope onerror event + + + + + + + + + diff --git a/test/wpt/tests/service-workers/service-worker/WEB_FEATURES.yml b/test/wpt/tests/service-workers/service-worker/WEB_FEATURES.yml new file mode 100644 index 00000000000..9ddc5b400dc --- /dev/null +++ b/test/wpt/tests/service-workers/service-worker/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: js-modules-service-workers + files: + - registration-script-module.https.html + - update-registration-with-type.https.html diff --git a/test/wpt/tests/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html b/test/wpt/tests/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html index 28c30718047..0ee41e96f29 100644 --- a/test/wpt/tests/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html +++ b/test/wpt/tests/service-workers/service-worker/fetch-canvas-tainting-video-with-range-request.https.html @@ -18,7 +18,9 @@ // byte range). Then the